[automerger skipped] Canonicalise path before extracting relative path am: cadac8cdf4 -s ours
am skip reason: skipped by user abkaur
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/providers/MediaProvider/+/20942349
Change-Id: Ic1b39030d4a9959cd03bbf67c14c1e548f3dee1a
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..74b2e0b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+# Intellij
+**/.idea/
+*.iml
+local.properties
+# Gradle
+**/.gradle/
diff --git a/Android.bp b/Android.bp
index 62d0497..a542d34 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,19 +1,6 @@
-
package {
- default_applicable_licenses: ["packages_providers_MediaProvider_license"],
-}
-
-// Added automatically by a large-scale-change
-// See: http://go/android-license-faq
-license {
- name: "packages_providers_MediaProvider_license",
- visibility: [":__subpackages__"],
- license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
- ],
- license_text: [
- "NOTICE",
- ],
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
}
android_app {
@@ -21,10 +8,24 @@
manifest: "AndroidManifest.xml",
static_libs: [
+ "modules-utils-backgroundthread",
"androidx.appcompat_appcompat",
"androidx.core_core",
+ "androidx.legacy_legacy-support-core-ui",
+ "androidx.lifecycle_lifecycle-extensions",
+ "androidx.recyclerview_recyclerview",
+ "com.google.android.material_material",
"guava",
"modules-utils-build",
+ "modules-utils-uieventlogger-interface",
+ "glide-prebuilt",
+ "glide-gifdecoder-prebuilt",
+ "glide-disklrucache-prebuilt",
+ "glide-annotation-and-compiler-prebuilt",
+ "androidx.fragment_fragment",
+ "androidx.vectordrawable_vectordrawable-animated",
+ "androidx.exifinterface_exifinterface",
+ "exoplayer-mediaprovider-ui",
],
libs: [
@@ -38,7 +39,7 @@
jni_libs: [
"libfuse_jni",
- "libfuse"
+ "libfuse",
],
resource_dirs: [
@@ -49,17 +50,20 @@
],
optimize: {
+ shrink: true,
+ optimize: false,
+ obfuscate: false,
proguard_flags_files: ["proguard.flags"],
},
plugins: [
"java_api_finder",
"error_prone_mediaprovider",
+ "glide-annotation-processor",
],
-
+ jarjar_rules: "jarjar-rules.txt",
sdk_version: "module_current",
min_sdk_version: "30",
- target_sdk_version: "31",
certificate: "media",
privileged: true,
@@ -78,7 +82,14 @@
],
},
- required: ["preinstalled-packages-com.android.providers.media.module.xml"],
+ required: [
+ "preinstalled-packages-com.android.providers.media.module.xml",
+ "privapp_allowlist_com.android.providers.media.module.xml",
+ ],
+
+ lint: {
+ strict_updatability_linting: true,
+ },
}
// Used by MediaProvider and MediaProviderTests
@@ -87,7 +98,6 @@
srcs: [
"src/**/*.aidl",
"src/**/*.java",
- ":mediaprovider-database-sources",
":statslog-mediaprovider-java-gen",
],
}
@@ -95,18 +105,30 @@
// This is defined to give LegacyMediaProvider the bare minimum it needs
// to keep the legacy database schema working while also building
// against "system_current"
-filegroup {
- name: "mediaprovider-database-sources",
+java_library {
+ name: "mediaprovider-database",
srcs: [
"src/com/android/providers/media/DatabaseHelper.java",
- "src/com/android/providers/media/util/BackgroundThread.java",
"src/com/android/providers/media/util/DatabaseUtils.java",
"src/com/android/providers/media/util/FileUtils.java",
"src/com/android/providers/media/util/ForegroundThread.java",
- "src/com/android/providers/media/util/HandlerExecutor.java",
"src/com/android/providers/media/util/Logging.java",
"src/com/android/providers/media/util/MimeUtils.java",
+ "src/com/android/providers/media/util/StringUtils.java",
"src/com/android/providers/media/playlist/*.java",
+ "src/com/android/providers/media/dao/*.java",
+ ],
+ sdk_version: "module_current",
+ min_sdk_version: "30",
+ static_libs: [
+ "modules-utils-backgroundthread",
+ "modules-utils-build",
+ "guava",
+ ],
+ libs: [
+ "androidx.annotation_annotation",
+ "framework-annotations-lib",
+ "framework-mediaprovider",
],
}
@@ -119,14 +141,13 @@
name: "statslog-mediaprovider-java-gen",
tools: ["stats-log-api-gen"],
cmd: "$(location stats-log-api-gen) --java $(out) --module mediaprovider" +
- " --javaPackage com.android.providers.media --javaClass MediaProviderStatsLog" +
- " --minApiLevel 30",
+ " --javaPackage com.android.providers.media --javaClass MediaProviderStatsLog" +
+ " --minApiLevel 30",
out: ["com/android/providers/media/MediaProviderStatsLog.java"],
}
prebuilt_etc {
- name: "preinstalled-packages-com.android.providers.media.module.xml",
- src: "preinstalled-packages-com.android.providers.media.module.xml",
- sub_dir: "sysconfig",
+ name: "preinstalled-packages-com.android.providers.media.module.xml",
+ src: "preinstalled-packages-com.android.providers.media.module.xml",
+ sub_dir: "sysconfig",
}
-
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 336e8eb..6cdda12 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1,6 +1,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.providers.media.module">
+ <meta-data
+ android:name="com.android.providers.media.photopicker.data.glide.PickerGlideModule"
+ android:value="GlideModule" />
+
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
@@ -22,6 +26,7 @@
<uses-permission android:name="android.permission.USE_RESERVED_DISK" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS" />
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- Permissions required for reading and logging compat changes -->
<uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/>
@@ -38,13 +43,20 @@
<!-- Permissions required to check if an app is in the foreground or not during IO -->
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+ <!-- Permission required to access CloudMediaProviders. Declared by us -->
+ <uses-permission android:name="com.android.providers.media.permission.MANAGE_CLOUD_MEDIA_PROVIDERS" />
+
+ <permission android:name="com.android.providers.media.permission.MANAGE_CLOUD_MEDIA_PROVIDERS"
+ android:protectionLevel="signature" />
+
<application
android:name="com.android.providers.media.MediaApplication"
android:label="@string/app_label"
android:allowBackup="false"
android:supportsRtl="true"
android:forceQueryable="true"
- android:usesCleartextTraffic="true">
+ android:usesCleartextTraffic="true"
+ android:crossProfile="true">
<provider
android:name="com.android.providers.media.MediaProvider"
android:authorities="media"
@@ -64,6 +76,11 @@
</intent-filter>
</provider>
+ <provider
+ android:name="com.android.providers.media.photopicker.PhotoPickerProvider"
+ android:authorities="com.android.providers.media.photopicker"
+ android:exported="false" />
+
<!-- Handles database upgrades after OTAs, then disables itself -->
<receiver android:name="com.android.providers.media.MediaUpgradeReceiver"
android:exported="true">
@@ -138,5 +155,41 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
+
+ <activity
+ android:name="com.android.providers.media.photopicker.PhotoPickerActivity"
+ android:process=":PhotoPicker"
+ android:label="@string/picker_app_label"
+ android:icon="@mipmap/picker_app_icon"
+ android:theme="@style/PickerDefaultTheme"
+ android:exported="true"
+ android:excludeFromRecents="true" >
+ <intent-filter android:priority="100" >
+ <action android:name="android.provider.action.PICK_IMAGES" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="image/*" />
+ <data android:mimeType="video/*" />
+ </intent-filter>
+ <intent-filter android:priority="100" >
+ <action android:name="android.provider.action.PICK_IMAGES" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <activity-alias
+ android:name="com.android.providers.media.photopicker.PhotoPickerGetContentActivity"
+ android:targetActivity="com.android.providers.media.photopicker.PhotoPickerActivity"
+ android:exported="true"
+ android:excludeFromRecents="true"
+ android:enabled="false">
+ <intent-filter>
+ <action android:name="android.intent.action.GET_CONTENT" />
+ <category android:name="android.intent.category.OPENABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="image/*" />
+ <data android:mimeType="video/*" />
+ </intent-filter>
+ </activity-alias>
+
</application>
</manifest>
diff --git a/NOTICE b/NOTICE
deleted file mode 100644
index c5b1efa..0000000
--- a/NOTICE
+++ /dev/null
@@ -1,190 +0,0 @@
-
- Copyright (c) 2005-2008, The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
-
- 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.
-
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
diff --git a/OWNERS b/OWNERS
index ed4dc2a..79add9e 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1 +1,3 @@
+# Bug component: 95221
+
include platform/frameworks/base:/core/java/android/os/storage/OWNERS
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 5b80e36..0e2dd2f 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -6,3 +6,5 @@
[Hook Scripts]
hidden_api_txt_checksorted_hook = ${REPO_ROOT}/tools/platform-compat/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
+
+checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 87582bb..2fc29df 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1,4 +1,25 @@
{
+ // TODO(b/204107787): Re-enable this once MP from master can be installed on R and S devices
+ // "mainline-presubmit": [
+ // {
+ // "name": "MediaProviderTests[com.google.android.mediaprovider.apex]"
+ // }
+ // {
+ // "name": "MediaProviderTests[com.google.android.mediaprovider.apex]"
+ // },
+ // {
+ // "name": "CtsScopedStorageCoreHostTest"
+ // },
+ // {
+ // "name": "CtsScopedStorageHostTest"
+ // },
+ // {
+ // "name": "CtsScopedStorageDeviceOnlyTest"
+ // },
+ // {
+ // "name": "CtsMediaProviderTranscodeTests[com.google.android.mediaprovider.apex]"
+ // }
+ // ],
"presubmit": [
{
"name": "MediaProviderTests"
@@ -40,7 +61,7 @@
"name": "fuse_node_test"
},
{
- "name": "CtsMediaProviderTranscodeTests"
+ "name": "CtsPhotoPickerTest"
}
],
"postsubmit": [
@@ -48,6 +69,10 @@
"name": "MediaProviderClientTests"
},
{
+ // TODO(b/222253890): Move these tests back to presubmit once the bug is fixed.
+ "name": "CtsMediaProviderTranscodeTests"
+ },
+ {
"name": "CtsAppSecurityHostTestCases",
"options": [
{
diff --git a/apex/Android.bp b/apex/Android.bp
index d53560a..c735880 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -1,10 +1,6 @@
package {
// See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "packages_providers_MediaProvider_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["packages_providers_MediaProvider_license"],
+ default_applicable_licenses: ["Android-Apache-2.0"],
}
apex {
@@ -18,15 +14,17 @@
apex_defaults {
name: "com.android.mediaprovider-defaults",
bootclasspath_fragments: ["com.android.mediaprovider-bootclasspath-fragment"],
- prebuilts: ["current_sdkinfo"],
+ prebuilts: [
+ "current_sdkinfo",
+ "privapp_allowlist_com.android.providers.media.module.xml"
+ ],
key: "com.android.mediaprovider.key",
certificate: ":com.android.mediaprovider.certificate",
file_contexts: ":com.android.mediaprovider-file_contexts",
- min_sdk_version: "30",
+ defaults: ["r-launched-apex-module"],
// Indicates that pre-installed version of this apex can be compressed.
// Whether it actually will be compressed is controlled on per-device basis.
compressible: true,
- updatable: true,
}
apex_key {
@@ -59,9 +57,25 @@
},
],
+ // Additional stubs libraries that this fragment's contents use which are
+ // not provided by another bootclasspath_fragment.
+ additional_stubs: [
+ "android-non-updatable",
+ ],
+
// Additional hidden API flag files to override the defaults. This must only be
// modified by the Soong or platform compat team.
hidden_api: {
max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+
+ // The following packages contain classes from other modules on the
+ // bootclasspath. That means that the hidden API flags for this module
+ // has to explicitly list every single class this module provides in
+ // that package to differentiate them from the classes provided by other
+ // modules. That can include private classes that are not part of the
+ // API.
+ split_packages: [
+ "android.provider",
+ ],
},
}
diff --git a/apex/apex_manifest.json b/apex/apex_manifest.json
index 59c4963..fe2ed11 100644
--- a/apex/apex_manifest.json
+++ b/apex/apex_manifest.json
@@ -1,4 +1,4 @@
{
"name": "com.android.mediaprovider",
- "version": 319999910
+ "version": 339990000
}
diff --git a/apex/framework/Android.bp b/apex/framework/Android.bp
index 716f818..a48bd75 100644
--- a/apex/framework/Android.bp
+++ b/apex/framework/Android.bp
@@ -14,11 +14,7 @@
package {
// See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "packages_providers_MediaProvider_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["packages_providers_MediaProvider_license"],
+ default_applicable_licenses: ["Android-Apache-2.0"],
}
java_sdk_library {
@@ -27,6 +23,7 @@
srcs: [
":framework-mediaprovider-sources",
+ "java/**/*.aidl",
],
permitted_packages: [
@@ -44,6 +41,7 @@
hostdex: true, // for hiddenapi check
impl_library_visibility: [
"//packages/providers/MediaProvider:__subpackages__",
+ "//cts/tests/PhotoPicker",
],
apex_available: [
"com.android.mediaprovider",
diff --git a/apex/framework/api/current.txt b/apex/framework/api/current.txt
index 0a53fb5..8e1691a 100644
--- a/apex/framework/api/current.txt
+++ b/apex/framework/api/current.txt
@@ -1,6 +1,100 @@
// Signature format: 2.0
package android.provider {
+ public abstract class CloudMediaProvider extends android.content.ContentProvider {
+ ctor public CloudMediaProvider();
+ method public final void attachInfo(@NonNull android.content.Context, @NonNull android.content.pm.ProviderInfo);
+ method @NonNull public final android.os.Bundle call(@NonNull String, @Nullable String, @Nullable android.os.Bundle);
+ method @NonNull public final android.net.Uri canonicalize(@NonNull android.net.Uri);
+ method public final int delete(@NonNull android.net.Uri, @Nullable String, @Nullable String[]);
+ method @NonNull public final String getType(@NonNull android.net.Uri);
+ method @NonNull public final android.net.Uri insert(@NonNull android.net.Uri, @NonNull android.content.ContentValues);
+ method @Nullable public android.provider.CloudMediaProvider.CloudMediaSurfaceController onCreateCloudMediaSurfaceController(@NonNull android.os.Bundle, @NonNull android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback);
+ method @NonNull public abstract android.os.Bundle onGetMediaCollectionInfo(@NonNull android.os.Bundle);
+ method @NonNull public abstract android.os.ParcelFileDescriptor onOpenMedia(@NonNull String, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException;
+ method @NonNull public abstract android.content.res.AssetFileDescriptor onOpenPreview(@NonNull String, @NonNull android.graphics.Point, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException;
+ method @NonNull public android.database.Cursor onQueryAlbums(@NonNull android.os.Bundle);
+ method @NonNull public abstract android.database.Cursor onQueryDeletedMedia(@NonNull android.os.Bundle);
+ method @NonNull public abstract android.database.Cursor onQueryMedia(@NonNull android.os.Bundle);
+ method @NonNull public final android.os.ParcelFileDescriptor openFile(@NonNull android.net.Uri, @NonNull String) throws java.io.FileNotFoundException;
+ method @NonNull public final android.os.ParcelFileDescriptor openFile(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException;
+ method @NonNull public final android.content.res.AssetFileDescriptor openTypedAssetFile(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.Bundle) throws java.io.FileNotFoundException;
+ method @NonNull public final android.content.res.AssetFileDescriptor openTypedAssetFile(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException;
+ method @NonNull public final android.database.Cursor query(@NonNull android.net.Uri, @Nullable String[], @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal);
+ method @NonNull public final android.database.Cursor query(@NonNull android.net.Uri, @Nullable String[], @Nullable String, @Nullable String[], @Nullable String);
+ method @NonNull public final android.database.Cursor query(@NonNull android.net.Uri, @Nullable String[], @Nullable String, @Nullable String[], @Nullable String, @Nullable android.os.CancellationSignal);
+ method public final int update(@NonNull android.net.Uri, @NonNull android.content.ContentValues, @Nullable String, @Nullable String[]);
+ }
+
+ public abstract static class CloudMediaProvider.CloudMediaSurfaceController {
+ ctor public CloudMediaProvider.CloudMediaSurfaceController();
+ method public abstract void onConfigChange(@NonNull android.os.Bundle);
+ method public abstract void onDestroy();
+ method public abstract void onMediaPause(int);
+ method public abstract void onMediaPlay(int);
+ method public abstract void onMediaSeekTo(int, long);
+ method public abstract void onPlayerCreate();
+ method public abstract void onPlayerRelease();
+ method public abstract void onSurfaceChanged(int, int, int, int);
+ method public abstract void onSurfaceCreated(int, @NonNull android.view.Surface, @NonNull String);
+ method public abstract void onSurfaceDestroyed(int);
+ }
+
+ public static final class CloudMediaProvider.CloudMediaSurfaceStateChangedCallback {
+ method public void setPlaybackState(int, int, @Nullable android.os.Bundle);
+ field public static final int PLAYBACK_STATE_BUFFERING = 1; // 0x1
+ field public static final int PLAYBACK_STATE_COMPLETED = 5; // 0x5
+ field public static final int PLAYBACK_STATE_ERROR_PERMANENT_FAILURE = 7; // 0x7
+ field public static final int PLAYBACK_STATE_ERROR_RETRIABLE_FAILURE = 6; // 0x6
+ field public static final int PLAYBACK_STATE_MEDIA_SIZE_CHANGED = 8; // 0x8
+ field public static final int PLAYBACK_STATE_PAUSED = 4; // 0x4
+ field public static final int PLAYBACK_STATE_READY = 2; // 0x2
+ field public static final int PLAYBACK_STATE_STARTED = 3; // 0x3
+ }
+
+ public final class CloudMediaProviderContract {
+ field public static final String EXTRA_ALBUM_ID = "android.provider.extra.ALBUM_ID";
+ field public static final String EXTRA_LOOPING_PLAYBACK_ENABLED = "android.provider.extra.LOOPING_PLAYBACK_ENABLED";
+ field public static final String EXTRA_MEDIA_COLLECTION_ID = "android.provider.extra.MEDIA_COLLECTION_ID";
+ field public static final String EXTRA_PAGE_TOKEN = "android.provider.extra.PAGE_TOKEN";
+ field public static final String EXTRA_PREVIEW_THUMBNAIL = "android.provider.extra.PREVIEW_THUMBNAIL";
+ field public static final String EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED = "android.provider.extra.SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED";
+ field public static final String EXTRA_SYNC_GENERATION = "android.provider.extra.SYNC_GENERATION";
+ field public static final String MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION = "com.android.providers.media.permission.MANAGE_CLOUD_MEDIA_PROVIDERS";
+ field public static final String PROVIDER_INTERFACE = "android.content.action.CLOUD_MEDIA_PROVIDER";
+ }
+
+ public static final class CloudMediaProviderContract.AlbumColumns {
+ field public static final String DATE_TAKEN_MILLIS = "date_taken_millis";
+ field public static final String DISPLAY_NAME = "display_name";
+ field public static final String ID = "id";
+ field public static final String MEDIA_COUNT = "album_media_count";
+ field public static final String MEDIA_COVER_ID = "album_media_cover_id";
+ }
+
+ public static final class CloudMediaProviderContract.MediaCollectionInfo {
+ field public static final String ACCOUNT_CONFIGURATION_INTENT = "account_configuration_intent";
+ field public static final String ACCOUNT_NAME = "account_name";
+ field public static final String LAST_MEDIA_SYNC_GENERATION = "last_media_sync_generation";
+ field public static final String MEDIA_COLLECTION_ID = "media_collection_id";
+ }
+
+ public static final class CloudMediaProviderContract.MediaColumns {
+ field public static final String DATE_TAKEN_MILLIS = "date_taken_millis";
+ field public static final String DURATION_MILLIS = "duration_millis";
+ field public static final String ID = "id";
+ field public static final String IS_FAVORITE = "is_favorite";
+ field public static final String MEDIA_STORE_URI = "media_store_uri";
+ field public static final String MIME_TYPE = "mime_type";
+ field public static final String SIZE_BYTES = "size_bytes";
+ field public static final String STANDARD_MIME_TYPE_EXTENSION = "standard_mime_type_extension";
+ field public static final int STANDARD_MIME_TYPE_EXTENSION_ANIMATED_WEBP = 3; // 0x3
+ field public static final int STANDARD_MIME_TYPE_EXTENSION_GIF = 1; // 0x1
+ field public static final int STANDARD_MIME_TYPE_EXTENSION_MOTION_PHOTO = 2; // 0x2
+ field public static final int STANDARD_MIME_TYPE_EXTENSION_NONE = 0; // 0x0
+ field public static final String SYNC_GENERATION = "sync_generation";
+ }
+
public final class MediaStore {
ctor public MediaStore();
method public static boolean canManageMedia(@NonNull android.content.Context);
@@ -14,6 +108,7 @@
method public static android.net.Uri getMediaScannerUri();
method @Nullable public static android.net.Uri getMediaUri(@NonNull android.content.Context, @NonNull android.net.Uri);
method @NonNull public static android.os.ParcelFileDescriptor getOriginalMediaFormatFileDescriptor(@NonNull android.content.Context, @NonNull android.os.ParcelFileDescriptor) throws java.io.IOException;
+ method public static int getPickImagesMaxLimit();
method @NonNull public static java.util.Set<java.lang.String> getRecentExternalVolumeNames(@NonNull android.content.Context);
method @Nullable public static android.net.Uri getRedactedUri(@NonNull android.content.ContentResolver, @NonNull android.net.Uri);
method @NonNull public static java.util.List<android.net.Uri> getRedactedUri(@NonNull android.content.ContentResolver, @NonNull java.util.List<android.net.Uri>);
@@ -21,11 +116,16 @@
method @NonNull public static String getVersion(@NonNull android.content.Context);
method @NonNull public static String getVersion(@NonNull android.content.Context, @NonNull String);
method @NonNull public static String getVolumeName(@NonNull android.net.Uri);
+ method public static boolean isCurrentCloudMediaProviderAuthority(@NonNull android.content.ContentResolver, @NonNull String);
method public static boolean isCurrentSystemGallery(@NonNull android.content.ContentResolver, int, @NonNull String);
+ method public static boolean isSupportedCloudMediaProviderAuthority(@NonNull android.content.ContentResolver, @NonNull String);
+ method public static void notifyCloudMediaChangedEvent(@NonNull android.content.ContentResolver, @NonNull String, @NonNull String) throws java.lang.SecurityException;
method @Deprecated @NonNull public static android.net.Uri setIncludePending(@NonNull android.net.Uri);
method @NonNull public static android.net.Uri setRequireOriginal(@NonNull android.net.Uri);
field public static final String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
field public static final String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE";
+ field public static final String ACTION_PICK_IMAGES = "android.provider.action.PICK_IMAGES";
+ field public static final String ACTION_PICK_IMAGES_SETTINGS = "android.provider.action.PICK_IMAGES_SETTINGS";
field public static final String ACTION_REVIEW = "android.provider.action.REVIEW";
field public static final String ACTION_REVIEW_SECURE = "android.provider.action.REVIEW_SECURE";
field public static final String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE";
@@ -46,6 +146,7 @@
field public static final String EXTRA_MEDIA_RADIO_CHANNEL = "android.intent.extra.radio_channel";
field public static final String EXTRA_MEDIA_TITLE = "android.intent.extra.title";
field public static final String EXTRA_OUTPUT = "output";
+ field public static final String EXTRA_PICK_IMAGES_MAX = "android.provider.extra.PICK_IMAGES_MAX";
field public static final String EXTRA_SCREEN_ORIENTATION = "android.intent.extra.screenOrientation";
field public static final String EXTRA_SHOW_ACTION_ICONS = "android.intent.extra.showActionIcons";
field public static final String EXTRA_SIZE_LIMIT = "android.intent.extra.sizeLimit";
@@ -358,6 +459,15 @@
field public static final String YEAR = "year";
}
+ public static class MediaStore.PickerMediaColumns {
+ field public static final String DATA = "_data";
+ field public static final String DATE_TAKEN = "datetaken";
+ field public static final String DISPLAY_NAME = "_display_name";
+ field public static final String DURATION_MILLIS = "duration";
+ field public static final String MIME_TYPE = "mime_type";
+ field public static final String SIZE = "_size";
+ }
+
public static final class MediaStore.Video {
ctor public MediaStore.Video();
method @Deprecated public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, String[]);
diff --git a/apex/framework/java/android/provider/AsyncContentProvider.java b/apex/framework/java/android/provider/AsyncContentProvider.java
new file mode 100644
index 0000000..25d5609
--- /dev/null
+++ b/apex/framework/java/android/provider/AsyncContentProvider.java
@@ -0,0 +1,74 @@
+/*
+ * 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 android.provider;
+
+import static android.provider.CloudMediaProviderContract.EXTRA_ERROR_MESSAGE;
+import static android.provider.CloudMediaProviderContract.EXTRA_FILE_DESCRIPTOR;
+
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+
+import java.io.FileNotFoundException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * @hide
+ */
+public final class AsyncContentProvider {
+
+ private static final long TIMEOUT_IN_SECONDS = 5L;
+
+ private final IAsyncContentProvider mAsyncContentProvider;
+
+ public AsyncContentProvider(@NonNull IAsyncContentProvider asyncContentProvider) {
+ this.mAsyncContentProvider = asyncContentProvider;
+ }
+
+ @NonNull
+ public ParcelFileDescriptor openMedia(@NonNull Uri uri, @NonNull String mode)
+ throws FileNotFoundException, ExecutionException, InterruptedException,
+ TimeoutException, RemoteException {
+ String mediaId = uri.getLastPathSegment();
+ CompletableFuture<ParcelFileDescriptor> future = new CompletableFuture<>();
+ RemoteCallback callback = new RemoteCallback(result -> setResult(result, future));
+ mAsyncContentProvider.openMedia(mediaId, callback);
+ return future.get(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
+ }
+
+ private void setResult(Bundle result, CompletableFuture<ParcelFileDescriptor> future) {
+ if (result.containsKey(EXTRA_FILE_DESCRIPTOR)) {
+ ParcelFileDescriptor pfd = result.getParcelable(EXTRA_FILE_DESCRIPTOR);
+ future.complete(pfd);
+ } else if (result.containsKey(EXTRA_ERROR_MESSAGE)) {
+ future.completeExceptionally(
+ new RemoteException(
+ result.getString(EXTRA_ERROR_MESSAGE)));
+ } else {
+ future.completeExceptionally(
+ new RemoteException(
+ "File descriptor and error message missing in response from "
+ + "CloudMediaProvider"));
+ }
+ }
+}
\ No newline at end of file
diff --git a/apex/framework/java/android/provider/CloudMediaProvider.java b/apex/framework/java/android/provider/CloudMediaProvider.java
new file mode 100644
index 0000000..6f3b246
--- /dev/null
+++ b/apex/framework/java/android/provider/CloudMediaProvider.java
@@ -0,0 +1,898 @@
+/*
+ * 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 android.provider;
+
+import static android.provider.CloudMediaProviderContract.EXTRA_ASYNC_CONTENT_PROVIDER;
+import static android.provider.CloudMediaProviderContract.EXTRA_AUTHORITY;
+import static android.provider.CloudMediaProviderContract.EXTRA_ERROR_MESSAGE;
+import static android.provider.CloudMediaProviderContract.EXTRA_FILE_DESCRIPTOR;
+import static android.provider.CloudMediaProviderContract.EXTRA_LOOPING_PLAYBACK_ENABLED;
+import static android.provider.CloudMediaProviderContract.EXTRA_MEDIASTORE_THUMB;
+import static android.provider.CloudMediaProviderContract.EXTRA_SURFACE_CONTROLLER;
+import static android.provider.CloudMediaProviderContract.EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED;
+import static android.provider.CloudMediaProviderContract.EXTRA_SURFACE_STATE_CALLBACK;
+import static android.provider.CloudMediaProviderContract.METHOD_CREATE_SURFACE_CONTROLLER;
+import static android.provider.CloudMediaProviderContract.METHOD_GET_ASYNC_CONTENT_PROVIDER;
+import static android.provider.CloudMediaProviderContract.METHOD_GET_MEDIA_COLLECTION_INFO;
+import static android.provider.CloudMediaProviderContract.URI_PATH_ALBUM;
+import static android.provider.CloudMediaProviderContract.URI_PATH_DELETED_MEDIA;
+import static android.provider.CloudMediaProviderContract.URI_PATH_MEDIA;
+import static android.provider.CloudMediaProviderContract.URI_PATH_MEDIA_COLLECTION_INFO;
+import static android.provider.CloudMediaProviderContract.URI_PATH_SURFACE_CONTROLLER;
+
+import android.annotation.DurationMillisLong;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.UriMatcher;
+import android.content.pm.ProviderInfo;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallback;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import java.io.FileNotFoundException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Base class for a cloud media provider. A cloud media provider offers read-only access to durable
+ * media files, specifically photos and videos stored on a local disk, or files in a cloud storage
+ * service. To create a cloud media provider, extend this class, implement the abstract methods,
+ * and add it to your manifest like this:
+ *
+ * <pre class="prettyprint"><manifest>
+ * ...
+ * <application>
+ * ...
+ * <provider
+ * android:name="com.example.MyCloudProvider"
+ * android:authorities="com.example.mycloudprovider"
+ * android:exported="true"
+ * android:permission="com.android.providers.media.permission.MANAGE_CLOUD_MEDIA_PROVIDERS"
+ * <intent-filter>
+ * <action android:name="android.content.action.CLOUD_MEDIA_PROVIDER" />
+ * </intent-filter>
+ * </provider>
+ * ...
+ * </application>
+ *</manifest></pre>
+ * <p>
+ * When defining your provider, you must protect it with the
+ * {@link CloudMediaProviderContract#MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION}, which is a permission
+ * only the system can obtain, trying to define an unprotected {@link CloudMediaProvider} will
+ * result in a {@link SecurityException}.
+ * <p>
+ * Applications cannot use a cloud media provider directly; they must go through
+ * {@link MediaStore#ACTION_PICK_IMAGES} which requires a user to actively navigate and select
+ * media items. When a user selects a media item through that UI, the system issues narrow URI
+ * permission grants to the requesting application.
+ * <h3>Media items</h3>
+ * <p>
+ * A media item must be an openable stream (with a specific MIME type). Media items can belong to
+ * zero or more albums. Albums cannot contain other albums.
+ * <p>
+ * Each item under a provider is uniquely referenced by its media or album id, which must not
+ * change which must be unique across all collection IDs as returned by
+ * {@link #onGetMediaCollectionInfo}.
+ *
+ * @see MediaStore#ACTION_PICK_IMAGES
+ */
+public abstract class CloudMediaProvider extends ContentProvider {
+ private static final String TAG = "CloudMediaProvider";
+
+ private static final int MATCH_MEDIAS = 1;
+ private static final int MATCH_DELETED_MEDIAS = 2;
+ private static final int MATCH_ALBUMS = 3;
+ private static final int MATCH_MEDIA_COLLECTION_INFO = 4;
+ private static final int MATCH_SURFACE_CONTROLLER = 5;
+
+ private static final boolean DEFAULT_LOOPING_PLAYBACK_ENABLED = true;
+ private static final boolean DEFAULT_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED = false;
+
+ private final UriMatcher mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+ private volatile int mMediaStoreAuthorityAppId;
+
+ private final AsyncContentProviderWrapper mAsyncContentProviderWrapper =
+ new AsyncContentProviderWrapper();
+
+ /**
+ * Implementation is provided by the parent class. Cannot be overridden.
+ */
+ @Override
+ public final void attachInfo(@NonNull Context context, @NonNull ProviderInfo info) {
+ registerAuthority(info.authority);
+
+ super.attachInfo(context, info);
+ }
+
+ private void registerAuthority(String authority) {
+ mMatcher.addURI(authority, URI_PATH_MEDIA, MATCH_MEDIAS);
+ mMatcher.addURI(authority, URI_PATH_DELETED_MEDIA, MATCH_DELETED_MEDIAS);
+ mMatcher.addURI(authority, URI_PATH_ALBUM, MATCH_ALBUMS);
+ mMatcher.addURI(authority, URI_PATH_MEDIA_COLLECTION_INFO, MATCH_MEDIA_COLLECTION_INFO);
+ mMatcher.addURI(authority, URI_PATH_SURFACE_CONTROLLER, MATCH_SURFACE_CONTROLLER);
+ }
+
+ /**
+ * Returns {@link Bundle} containing binder to {@link IAsyncContentProvider}.
+ *
+ * @hide
+ */
+ @NonNull
+ public final Bundle onGetAsyncContentProvider() {
+ Bundle bundle = new Bundle();
+ bundle.putBinder(EXTRA_ASYNC_CONTENT_PROVIDER, mAsyncContentProviderWrapper.asBinder());
+ return bundle;
+ }
+
+ /**
+ * Returns metadata about the media collection itself.
+ * <p>
+ * This is useful for the OS to determine if its cache of media items in the collection is
+ * still valid and if a full or incremental sync is required with {@link #onQueryMedia}.
+ * <p>
+ * This method might be called by the OS frequently and is performance critical, hence it should
+ * avoid long running operations.
+ * <p>
+ * If the provider handled any filters in {@code extras}, it must add the key to the
+ * {@link ContentResolver#EXTRA_HONORED_ARGS} as part of the returned {@link Bundle}.
+ *
+ * @param extras containing keys to filter result:
+ * <ul>
+ * <li> {@link CloudMediaProviderContract#EXTRA_ALBUM_ID}
+ * </ul>
+ *
+ * @return {@link Bundle} containing {@link CloudMediaProviderContract.MediaCollectionInfo}
+ * <ul>
+ * <li> {@link CloudMediaProviderContract.MediaCollectionInfo#MEDIA_COLLECTION_ID}
+ * <li> {@link CloudMediaProviderContract.MediaCollectionInfo#LAST_MEDIA_SYNC_GENERATION}
+ * <li> {@link CloudMediaProviderContract.MediaCollectionInfo#ACCOUNT_NAME}
+ * <li> {@link CloudMediaProviderContract.MediaCollectionInfo#ACCOUNT_CONFIGURATION_INTENT}
+ * </ul>
+ */
+ @SuppressWarnings("unused")
+ @NonNull
+ public abstract Bundle onGetMediaCollectionInfo(@NonNull Bundle extras);
+
+ /**
+ * Returns a cursor representing all media items in the media collection optionally filtered by
+ * {@code extras} and sorted in reverse chronological order of
+ * {@link CloudMediaProviderContract.MediaColumns#DATE_TAKEN_MILLIS}, i.e. most recent items
+ * first.
+ * <p>
+ * The cloud media provider must set the
+ * {@link CloudMediaProviderContract#EXTRA_MEDIA_COLLECTION_ID} as part of the returned
+ * {@link Cursor#setExtras} {@link Bundle}. Not setting this is an error and invalidates the
+ * returned {@link Cursor}.
+ * <p>
+ * If the cloud media provider handled any filters in {@code extras}, it must add the key to
+ * the {@link ContentResolver#EXTRA_HONORED_ARGS} as part of the returned
+ * {@link Cursor#setExtras} {@link Bundle}.
+ *
+ * @param extras containing keys to filter media items:
+ * <ul>
+ * <li> {@link CloudMediaProviderContract#EXTRA_SYNC_GENERATION}
+ * <li> {@link CloudMediaProviderContract#EXTRA_PAGE_TOKEN}
+ * <li> {@link CloudMediaProviderContract#EXTRA_ALBUM_ID}
+ * </ul>
+ * @return cursor representing media items containing all
+ * {@link CloudMediaProviderContract.MediaColumns} columns
+ */
+ @SuppressWarnings("unused")
+ @NonNull
+ public abstract Cursor onQueryMedia(@NonNull Bundle extras);
+
+ /**
+ * Returns a {@link Cursor} representing all deleted media items in the entire media collection
+ * within the current provider version as returned by {@link #onGetMediaCollectionInfo}. These
+ * items can be optionally filtered by {@code extras}.
+ * <p>
+ * The cloud media provider must set the
+ * {@link CloudMediaProviderContract#EXTRA_MEDIA_COLLECTION_ID} as part of the returned
+ * {@link Cursor#setExtras} {@link Bundle}. Not setting this is an error and invalidates the
+ * returned {@link Cursor}.
+ * <p>
+ * If the provider handled any filters in {@code extras}, it must add the key to
+ * the {@link ContentResolver#EXTRA_HONORED_ARGS} as part of the returned
+ * {@link Cursor#setExtras} {@link Bundle}.
+ *
+ * @param extras containing keys to filter deleted media items:
+ * <ul>
+ * <li> {@link CloudMediaProviderContract#EXTRA_SYNC_GENERATION}
+ * <li> {@link CloudMediaProviderContract#EXTRA_PAGE_TOKEN}
+ * </ul>
+ * @return cursor representing deleted media items containing just the
+ * {@link CloudMediaProviderContract.MediaColumns#ID} column
+ */
+ @SuppressWarnings("unused")
+ @NonNull
+ public abstract Cursor onQueryDeletedMedia(@NonNull Bundle extras);
+
+ /**
+ * Returns a cursor representing all album items in the media collection optionally filtered
+ * by {@code extras} and sorted in reverse chronological order of
+ * {@link CloudMediaProviderContract.AlbumColumns#DATE_TAKEN_MILLIS}, i.e. most recent items
+ * first.
+ * <p>
+ * The cloud media provider must set the
+ * {@link CloudMediaProviderContract#EXTRA_MEDIA_COLLECTION_ID} as part of the returned
+ * {@link Cursor#setExtras} {@link Bundle}. Not setting this is an error and invalidates the
+ * returned {@link Cursor}.
+ * <p>
+ * If the provider handled any filters in {@code extras}, it must add the key to
+ * the {@link ContentResolver#EXTRA_HONORED_ARGS} as part of the returned
+ * {@link Cursor#setExtras} {@link Bundle}.
+ *
+ * @param extras containing keys to filter album items:
+ * <ul>
+ * <li> {@link CloudMediaProviderContract#EXTRA_SYNC_GENERATION}
+ * <li> {@link CloudMediaProviderContract#EXTRA_PAGE_TOKEN}
+ * </ul>
+ * @return cursor representing album items containing all
+ * {@link CloudMediaProviderContract.AlbumColumns} columns
+ */
+ @SuppressWarnings("unused")
+ @NonNull
+ public Cursor onQueryAlbums(@NonNull Bundle extras) {
+ throw new UnsupportedOperationException("queryAlbums not supported");
+ }
+
+ /**
+ * Returns a thumbnail of {@code size} for a media item identified by {@code mediaId}.
+ * <p>
+ * This is expected to be a much lower resolution version than the item returned by
+ * {@link #onOpenMedia}.
+ * <p>
+ * If you block while downloading content, you should periodically check
+ * {@link CancellationSignal#isCanceled()} to abort abandoned open requests.
+ *
+ * @param mediaId the media item to return
+ * @param size the dimensions of the thumbnail to return. The returned file descriptor doesn't
+ * have to match the {@code size} precisely because the OS will adjust the dimensions before
+ * usage. Implementations can return close approximations especially if the approximation is
+ * already locally on the device and doesn't require downloading from the cloud.
+ * @param extras to modify the way the fd is opened, e.g. for video files we may request a
+ * thumbnail image instead of a video with
+ * {@link CloudMediaProviderContract#EXTRA_PREVIEW_THUMBNAIL}
+ * @param signal used by the OS to signal if the request should be cancelled
+ * @return read-only file descriptor for accessing the thumbnail for the media file
+ *
+ * @see #onOpenMedia
+ * @see CloudMediaProviderContract#EXTRA_PREVIEW_THUMBNAIL
+ */
+ @SuppressWarnings("unused")
+ @NonNull
+ public abstract AssetFileDescriptor onOpenPreview(@NonNull String mediaId,
+ @NonNull Point size, @Nullable Bundle extras, @Nullable CancellationSignal signal)
+ throws FileNotFoundException;
+
+ /**
+ * Returns the full size media item identified by {@code mediaId}.
+ * <p>
+ * If you block while downloading content, you should periodically check
+ * {@link CancellationSignal#isCanceled()} to abort abandoned open requests.
+ *
+ * @param mediaId the media item to return
+ * @param extras to modify the way the fd is opened, there's none at the moment, but some
+ * might be implemented in the future
+ * @param signal used by the OS to signal if the request should be cancelled
+ * @return read-only file descriptor for accessing the media file
+ *
+ * @see #onOpenPreview
+ */
+ @SuppressWarnings("unused")
+ @NonNull
+ public abstract ParcelFileDescriptor onOpenMedia(@NonNull String mediaId,
+ @Nullable Bundle extras, @Nullable CancellationSignal signal)
+ throws FileNotFoundException;
+
+ /**
+ * Returns a {@link CloudMediaSurfaceController} used for rendering the preview of media items,
+ * or null if preview rendering is not supported.
+ *
+ * @param config containing configuration parameters for {@link CloudMediaSurfaceController}
+ * <ul>
+ * <li> {@link CloudMediaProviderContract#EXTRA_LOOPING_PLAYBACK_ENABLED}
+ * <li> {@link CloudMediaProviderContract#EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED}
+ * </ul>
+ * @param callback {@link CloudMediaSurfaceStateChangedCallback} to send state updates for
+ * {@link Surface} to picker launched via {@link MediaStore#ACTION_PICK_IMAGES}
+ */
+ @Nullable
+ public CloudMediaSurfaceController onCreateCloudMediaSurfaceController(@NonNull Bundle config,
+ @NonNull CloudMediaSurfaceStateChangedCallback callback) {
+ return null;
+ }
+
+ /**
+ * Implementation is provided by the parent class. Cannot be overridden.
+ */
+ @Override
+ @NonNull
+ public final Bundle call(@NonNull String method, @Nullable String arg,
+ @Nullable Bundle extras) {
+ if (!method.startsWith("android:")) {
+ // Ignore non-platform methods
+ return super.call(method, arg, extras);
+ }
+
+ try {
+ return callUnchecked(method, arg, extras);
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private Bundle callUnchecked(String method, String arg, Bundle extras)
+ throws FileNotFoundException {
+ if (METHOD_GET_MEDIA_COLLECTION_INFO.equals(method)) {
+ return onGetMediaCollectionInfo(extras);
+ } else if (METHOD_CREATE_SURFACE_CONTROLLER.equals(method)) {
+ return onCreateCloudMediaSurfaceController(extras);
+ } else if (METHOD_GET_ASYNC_CONTENT_PROVIDER.equals(method)) {
+ return onGetAsyncContentProvider();
+ } else {
+ throw new UnsupportedOperationException("Method not supported " + method);
+ }
+ }
+
+ private Bundle onCreateCloudMediaSurfaceController(@NonNull Bundle extras) {
+ Objects.requireNonNull(extras);
+
+ final IBinder binder = extras.getBinder(EXTRA_SURFACE_STATE_CALLBACK);
+ if (binder == null) {
+ throw new IllegalArgumentException("Missing surface state callback");
+ }
+
+ final boolean enableLoop = extras.getBoolean(EXTRA_LOOPING_PLAYBACK_ENABLED,
+ DEFAULT_LOOPING_PLAYBACK_ENABLED);
+ final boolean muteAudio = extras.getBoolean(EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED,
+ DEFAULT_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED);
+ final String authority = extras.getString(EXTRA_AUTHORITY);
+ final CloudMediaSurfaceStateChangedCallback callback =
+ new CloudMediaSurfaceStateChangedCallback(
+ ICloudMediaSurfaceStateChangedCallback.Stub.asInterface(binder));
+ final Bundle config = new Bundle();
+ config.putBoolean(EXTRA_LOOPING_PLAYBACK_ENABLED, enableLoop);
+ config.putBoolean(EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED, muteAudio);
+ config.putString(EXTRA_AUTHORITY, authority);
+ final CloudMediaSurfaceController controller =
+ onCreateCloudMediaSurfaceController(config, callback);
+ if (controller == null) {
+ Log.d(TAG, "onCreateCloudMediaSurfaceController returned null");
+ return Bundle.EMPTY;
+ }
+
+ Bundle result = new Bundle();
+ result.putBinder(EXTRA_SURFACE_CONTROLLER,
+ new CloudMediaSurfaceControllerWrapper(controller).asBinder());
+ return result;
+ }
+
+ /**
+ * Implementation is provided by the parent class. Cannot be overridden.
+ *
+ * @see #onOpenMedia
+ */
+ @NonNull
+ @Override
+ public final ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode)
+ throws FileNotFoundException {
+ return openFile(uri, mode, null);
+ }
+
+ /**
+ * Implementation is provided by the parent class. Cannot be overridden.
+ *
+ * @see #onOpenMedia
+ */
+ @NonNull
+ @Override
+ public final ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode,
+ @Nullable CancellationSignal signal) throws FileNotFoundException {
+ String mediaId = uri.getLastPathSegment();
+
+ return onOpenMedia(mediaId, /* extras */ null, signal);
+ }
+
+ /**
+ * Implementation is provided by the parent class. Cannot be overridden.
+ *
+ * @see #onOpenPreview
+ * @see #onOpenMedia
+ */
+ @NonNull
+ @Override
+ public final AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri,
+ @NonNull String mimeTypeFilter, @Nullable Bundle opts) throws FileNotFoundException {
+ return openTypedAssetFile(uri, mimeTypeFilter, opts, null);
+ }
+
+ /**
+ * Implementation is provided by the parent class. Cannot be overridden.
+ *
+ * @see #onOpenPreview
+ * @see #onOpenMedia
+ */
+ @NonNull
+ @Override
+ public final AssetFileDescriptor openTypedAssetFile(
+ @NonNull Uri uri, @NonNull String mimeTypeFilter, @Nullable Bundle opts,
+ @Nullable CancellationSignal signal) throws FileNotFoundException {
+ final String mediaId = uri.getLastPathSegment();
+ final Bundle bundle = new Bundle();
+ Point previewSize = null;
+
+ final DisplayMetrics screenMetrics = getContext().getResources().getDisplayMetrics();
+ int minPreviewLength = Math.min(screenMetrics.widthPixels, screenMetrics.heightPixels);
+
+ if (opts != null) {
+ bundle.putBoolean(EXTRA_MEDIASTORE_THUMB, opts.getBoolean(EXTRA_MEDIASTORE_THUMB));
+
+ if (opts.containsKey(CloudMediaProviderContract.EXTRA_PREVIEW_THUMBNAIL)) {
+ bundle.putBoolean(CloudMediaProviderContract.EXTRA_PREVIEW_THUMBNAIL, true);
+ minPreviewLength = minPreviewLength / 2;
+ }
+
+ previewSize = opts.getParcelable(ContentResolver.EXTRA_SIZE);
+ }
+
+ if (previewSize == null) {
+ previewSize = new Point(minPreviewLength, minPreviewLength);
+ }
+
+ return onOpenPreview(mediaId, previewSize, bundle, signal);
+ }
+
+ /**
+ * Implementation is provided by the parent class. Cannot be overridden.
+ *
+ * @see #onQueryMedia
+ * @see #onQueryDeletedMedia
+ * @see #onQueryAlbums
+ */
+ @NonNull
+ @Override
+ public final Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+ @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) {
+ switch (mMatcher.match(uri)) {
+ case MATCH_MEDIAS:
+ return onQueryMedia(queryArgs);
+ case MATCH_DELETED_MEDIAS:
+ return onQueryDeletedMedia(queryArgs);
+ case MATCH_ALBUMS:
+ return onQueryAlbums(queryArgs);
+ default:
+ throw new UnsupportedOperationException("Unsupported Uri " + uri);
+ }
+ }
+
+ /**
+ * Implementation is provided by the parent class. Throws by default, and
+ * cannot be overridden.
+ */
+ @NonNull
+ @Override
+ public final String getType(@NonNull Uri uri) {
+ throw new UnsupportedOperationException("getType not supported");
+ }
+
+ /**
+ * Implementation is provided by the parent class. Throws by default, and
+ * cannot be overridden.
+ */
+ @NonNull
+ @Override
+ public final Uri canonicalize(@NonNull Uri uri) {
+ throw new UnsupportedOperationException("Canonicalize not supported");
+ }
+
+ /**
+ * Implementation is provided by the parent class. Throws by default, and
+ * cannot be overridden.
+ */
+ @NonNull
+ @Override
+ public final Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+ @Nullable String selection, @Nullable String[] selectionArgs,
+ @Nullable String sortOrder) {
+ // As of Android-O, ContentProvider#query (w/ bundle arg) is the primary
+ // transport method. We override that, and don't ever delegate to this method.
+ throw new UnsupportedOperationException("Pre-Android-O query format not supported.");
+ }
+
+ /**
+ * Implementation is provided by the parent class. Throws by default, and
+ * cannot be overridden.
+ */
+ @NonNull
+ @Override
+ public final Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+ @Nullable String selection, @Nullable String[] selectionArgs,
+ @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) {
+ // As of Android-O, ContentProvider#query (w/ bundle arg) is the primary
+ // transport method. We override that, and don't ever delegate to this metohd.
+ throw new UnsupportedOperationException("Pre-Android-O query format not supported.");
+ }
+
+ /**
+ * Implementation is provided by the parent class. Throws by default, and
+ * cannot be overridden.
+ */
+ @NonNull
+ @Override
+ public final Uri insert(@NonNull Uri uri, @NonNull ContentValues values) {
+ throw new UnsupportedOperationException("Insert not supported");
+ }
+
+ /**
+ * Implementation is provided by the parent class. Throws by default, and
+ * cannot be overridden.
+ */
+ @Override
+ public final int delete(@NonNull Uri uri, @Nullable String selection,
+ @Nullable String[] selectionArgs) {
+ throw new UnsupportedOperationException("Delete not supported");
+ }
+
+ /**
+ * Implementation is provided by the parent class. Throws by default, and
+ * cannot be overridden.
+ */
+ @Override
+ public final int update(@NonNull Uri uri, @NonNull ContentValues values,
+ @Nullable String selection, @Nullable String[] selectionArgs) {
+ throw new UnsupportedOperationException("Update not supported");
+ }
+
+ /**
+ * Manages rendering the preview of media items on given instances of {@link Surface}.
+ *
+ * <p>The methods of this class are meant to be asynchronous, and should not block by performing
+ * any heavy operation.
+ * <p>Note that a single CloudMediaSurfaceController instance would be responsible for
+ * rendering multiple media items associated with multiple surfaces.
+ */
+ @SuppressLint("PackageLayering") // We need to pass in a Surface which can be prepared for
+ // rendering a media item.
+ public static abstract class CloudMediaSurfaceController {
+
+ /**
+ * Creates any player resource(s) needed for rendering.
+ */
+ public abstract void onPlayerCreate();
+
+ /**
+ * Releases any player resource(s) used for rendering.
+ */
+ public abstract void onPlayerRelease();
+
+ /**
+ * Indicates creation of the given {@link Surface} with given {@code surfaceId} for
+ * rendering the preview of a media item with given {@code mediaId}.
+ *
+ * <p>This is called immediately after the surface is first created. Implementations of this
+ * should start up whatever rendering code they desire.
+ * <p>Note that the given media item remains associated with the given surface id till the
+ * {@link Surface} is destroyed.
+ *
+ * @param surfaceId id which uniquely identifies the {@link Surface} for rendering
+ * @param surface instance of the {@link Surface} on which the media item should be rendered
+ * @param mediaId id which uniquely identifies the media to be rendered
+ *
+ * @see SurfaceHolder.Callback#surfaceCreated(SurfaceHolder)
+ */
+ public abstract void onSurfaceCreated(int surfaceId, @NonNull Surface surface,
+ @NonNull String mediaId);
+
+ /**
+ * Indicates structural changes (format or size) in the {@link Surface} for rendering.
+ *
+ * <p>This method is always called at least once, after {@link #onSurfaceCreated}.
+ *
+ * @param surfaceId id which uniquely identifies the {@link Surface} for rendering
+ * @param format the new {@link PixelFormat} of the surface
+ * @param width the new width of the {@link Surface}
+ * @param height the new height of the {@link Surface}
+ *
+ * @see SurfaceHolder.Callback#surfaceChanged(SurfaceHolder, int, int, int)
+ */
+ public abstract void onSurfaceChanged(int surfaceId, int format, int width, int height);
+
+ /**
+ * Indicates destruction of a {@link Surface} with given {@code surfaceId}.
+ *
+ * <p>This is called immediately before a surface is being destroyed. After returning from
+ * this call, you should no longer try to access this surface.
+ *
+ * @param surfaceId id which uniquely identifies the {@link Surface} for rendering
+ *
+ * @see SurfaceHolder.Callback#surfaceDestroyed(SurfaceHolder)
+ */
+ public abstract void onSurfaceDestroyed(int surfaceId);
+
+ /**
+ * Start playing the preview of the media associated with the given surface id. If
+ * playback had previously been paused, playback will continue from where it was paused.
+ * If playback had been stopped, or never started before, playback will start at the
+ * beginning.
+ *
+ * @param surfaceId id which uniquely identifies the {@link Surface} for rendering
+ */
+ public abstract void onMediaPlay(int surfaceId);
+
+ /**
+ * Pauses the playback of the media associated with the given surface id.
+ *
+ * @param surfaceId id which uniquely identifies the {@link Surface} for rendering
+ */
+ public abstract void onMediaPause(int surfaceId);
+
+ /**
+ * Seeks the media associated with the given surface id to specified timestamp.
+ *
+ * @param surfaceId id which uniquely identifies the {@link Surface} for rendering
+ * @param timestampMillis the timestamp in milliseconds from the start to seek to
+ */
+ public abstract void onMediaSeekTo(int surfaceId, @DurationMillisLong long timestampMillis);
+
+ /**
+ * Changes the configuration parameters for the CloudMediaSurfaceController.
+ *
+ * @param config the updated config to change to. This can include config changes for the
+ * following:
+ * <ul>
+ * <li> {@link CloudMediaProviderContract#EXTRA_LOOPING_PLAYBACK_ENABLED}
+ * <li> {@link CloudMediaProviderContract#EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED}
+ * </ul>
+ */
+ public abstract void onConfigChange(@NonNull Bundle config);
+
+ /**
+ * Indicates destruction of this CloudMediaSurfaceController object.
+ *
+ * <p>This CloudMediaSurfaceController object should no longer be in use after this method
+ * has been called.
+ *
+ * <p>Note that it is possible for this method to be called directly without
+ * {@link #onPlayerRelease} being called, hence you should release any resources associated
+ * with this CloudMediaSurfaceController object, or perform any cleanup required in this
+ * method.
+ */
+ public abstract void onDestroy();
+ }
+
+ /**
+ * This class is used by {@link CloudMediaProvider} to send {@link Surface} state updates to
+ * picker launched via {@link MediaStore#ACTION_PICK_IMAGES}.
+ *
+ * @see MediaStore#ACTION_PICK_IMAGES
+ */
+ public static final class CloudMediaSurfaceStateChangedCallback {
+
+ /** {@hide} */
+ @IntDef(flag = true, prefix = { "PLAYBACK_STATE_" }, value = {
+ PLAYBACK_STATE_BUFFERING,
+ PLAYBACK_STATE_READY,
+ PLAYBACK_STATE_STARTED,
+ PLAYBACK_STATE_PAUSED,
+ PLAYBACK_STATE_COMPLETED,
+ PLAYBACK_STATE_ERROR_RETRIABLE_FAILURE,
+ PLAYBACK_STATE_ERROR_PERMANENT_FAILURE,
+ PLAYBACK_STATE_MEDIA_SIZE_CHANGED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PlaybackState {}
+
+ /**
+ * Constant to notify that the playback is buffering
+ */
+ public static final int PLAYBACK_STATE_BUFFERING = 1;
+
+ /**
+ * Constant to notify that the playback is ready to be played
+ */
+ public static final int PLAYBACK_STATE_READY = 2;
+
+ /**
+ * Constant to notify that the playback has started
+ */
+ public static final int PLAYBACK_STATE_STARTED = 3;
+
+ /**
+ * Constant to notify that the playback is paused.
+ */
+ public static final int PLAYBACK_STATE_PAUSED = 4;
+
+ /**
+ * Constant to notify that the playback has completed
+ */
+ public static final int PLAYBACK_STATE_COMPLETED = 5;
+
+ /**
+ * Constant to notify that the playback has failed with a retriable error.
+ */
+ public static final int PLAYBACK_STATE_ERROR_RETRIABLE_FAILURE = 6;
+
+ /**
+ * Constant to notify that the playback has failed with a permanent error.
+ */
+ public static final int PLAYBACK_STATE_ERROR_PERMANENT_FAILURE = 7;
+
+ /**
+ * Constant to notify that the media size is first known or has changed.
+ *
+ * Pass the width and height of the media as a {@link Point} inside the {@link Bundle} with
+ * {@link ContentResolver#EXTRA_SIZE} as the key.
+ *
+ * @see CloudMediaSurfaceStateChangedCallback#setPlaybackState(int, int, Bundle)
+ * @see MediaPlayer.OnVideoSizeChangedListener#onVideoSizeChanged(MediaPlayer, int, int)
+ */
+ public static final int PLAYBACK_STATE_MEDIA_SIZE_CHANGED = 8;
+
+ private final ICloudMediaSurfaceStateChangedCallback mCallback;
+
+ CloudMediaSurfaceStateChangedCallback(ICloudMediaSurfaceStateChangedCallback callback) {
+ mCallback = callback;
+ }
+
+ /**
+ * This is called to notify playback state update for a {@link Surface}
+ * on the picker launched via {@link MediaStore#ACTION_PICK_IMAGES}.
+ *
+ * @param surfaceId id which uniquely identifies a {@link Surface}
+ * @param playbackState playback state to notify picker about
+ * @param playbackStateInfo {@link Bundle} which may contain extra information about the
+ * playback state, such as media size, progress/seek info or
+ * details about errors.
+ */
+ public void setPlaybackState(int surfaceId, @PlaybackState int playbackState,
+ @Nullable Bundle playbackStateInfo) {
+ try {
+ mCallback.setPlaybackState(surfaceId, playbackState, playbackStateInfo);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to notify playback state (" + playbackState + ") for "
+ + "surfaceId: " + surfaceId + " ; playbackStateInfo: " + playbackStateInfo,
+ e);
+ }
+ }
+ }
+
+ /** {@hide} */
+ private static class CloudMediaSurfaceControllerWrapper
+ extends ICloudMediaSurfaceController.Stub {
+
+ final private CloudMediaSurfaceController mSurfaceController;
+
+ CloudMediaSurfaceControllerWrapper(CloudMediaSurfaceController surfaceController) {
+ mSurfaceController = surfaceController;
+ }
+
+ @Override
+ public void onPlayerCreate() {
+ Log.i(TAG, "Creating player.");
+ mSurfaceController.onPlayerCreate();
+ }
+
+ @Override
+ public void onPlayerRelease() {
+ Log.i(TAG, "Releasing player.");
+ mSurfaceController.onPlayerRelease();
+ }
+
+ @Override
+ public void onSurfaceCreated(int surfaceId, @NonNull Surface surface,
+ @NonNull String mediaId) {
+ Log.i(TAG, "Surface prepared. SurfaceId: " + surfaceId + ". MediaId: " + mediaId);
+ mSurfaceController.onSurfaceCreated(surfaceId, surface, mediaId);
+ }
+
+ @Override
+ public void onSurfaceChanged(int surfaceId, int format, int width, int height) {
+ Log.i(TAG, "Surface changed. SurfaceId: " + surfaceId + ". Format: " + format
+ + ". Width: " + width + ". Height: " + height);
+ mSurfaceController.onSurfaceChanged(surfaceId, format, width, height);
+ }
+
+ @Override
+ public void onSurfaceDestroyed(int surfaceId) {
+ Log.i(TAG, "Surface released. SurfaceId: " + surfaceId);
+ mSurfaceController.onSurfaceDestroyed(surfaceId);
+ }
+
+ @Override
+ public void onMediaPlay(int surfaceId) {
+ Log.i(TAG, "Media played. SurfaceId: " + surfaceId);
+ mSurfaceController.onMediaPlay(surfaceId);
+ }
+
+ @Override
+ public void onMediaPause(int surfaceId) {
+ Log.i(TAG, "Media paused. SurfaceId: " + surfaceId);
+ mSurfaceController.onMediaPause(surfaceId);
+ }
+
+ @Override
+ public void onMediaSeekTo(int surfaceId, @DurationMillisLong long timestampMillis) {
+ Log.i(TAG, "Media seeked. SurfaceId: " + surfaceId + ". Seek timestamp(ms): "
+ + timestampMillis);
+ mSurfaceController.onMediaSeekTo(surfaceId, timestampMillis);
+ }
+
+ @Override
+ public void onConfigChange(@NonNull Bundle config) {
+ Log.i(TAG, "Config changed. Updated config params: " + config);
+ mSurfaceController.onConfigChange(config);
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.i(TAG, "Controller destroyed");
+ mSurfaceController.onDestroy();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ private class AsyncContentProviderWrapper extends IAsyncContentProvider.Stub {
+
+ @Override
+ public void openMedia(String mediaId, RemoteCallback remoteCallback) {
+ try {
+ ParcelFileDescriptor pfd = onOpenMedia(mediaId,/* extras */
+ null,/* cancellationSignal */ null);
+ sendResult(pfd, null, remoteCallback);
+ } catch (Exception e) {
+ sendResult(null, e, remoteCallback);
+ }
+ }
+
+ private void sendResult(ParcelFileDescriptor pfd, Throwable throwable,
+ RemoteCallback remoteCallback) {
+ Bundle bundle = new Bundle();
+ if (pfd == null && throwable == null) {
+ throw new IllegalStateException("Expected ParcelFileDescriptor or an exception.");
+ }
+ if (pfd != null) {
+ bundle.putParcelable(EXTRA_FILE_DESCRIPTOR, pfd);
+ }
+ if (throwable != null) {
+ bundle.putString(EXTRA_ERROR_MESSAGE, throwable.getMessage());
+ }
+ remoteCallback.sendResult(bundle);
+ }
+ }
+}
diff --git a/apex/framework/java/android/provider/CloudMediaProviderContract.java b/apex/framework/java/android/provider/CloudMediaProviderContract.java
new file mode 100644
index 0000000..9e35058
--- /dev/null
+++ b/apex/framework/java/android/provider/CloudMediaProviderContract.java
@@ -0,0 +1,699 @@
+/*
+ * 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 android.provider;
+
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.Bundle;
+
+import java.util.UUID;
+
+/**
+ * Defines the contract between a cloud media provider and the OS.
+ * <p>
+ * To create a cloud media provider, extend {@link CloudMediaProvider}, which
+ * provides a foundational implementation of this contract.
+ *
+ * @see CloudMediaProvider
+ */
+public final class CloudMediaProviderContract {
+ private static final String TAG = "CloudMediaProviderContract";
+
+ private CloudMediaProviderContract() {}
+
+ /**
+ * {@link Intent} action used to identify {@link CloudMediaProvider} instances. This
+ * is used in the {@code <intent-filter>} of the {@code <provider>}.
+ */
+ public static final String PROVIDER_INTERFACE = "android.content.action.CLOUD_MEDIA_PROVIDER";
+
+ /**
+ * Permission required to protect {@link CloudMediaProvider} instances. Providers should
+ * require this in the {@code permission} attribute in their {@code <provider>} tag.
+ * The OS will not connect to a provider without this protection.
+ */
+ public static final String MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION =
+ "com.android.providers.media.permission.MANAGE_CLOUD_MEDIA_PROVIDERS";
+
+ /** Constants related to a media item, including {@link Cursor} column names */
+ public static final class MediaColumns {
+ private MediaColumns() {}
+
+ /**
+ * Unique ID of a media item. This ID is both provided by and interpreted
+ * by a {@link CloudMediaProvider}, and should be treated as an opaque
+ * value by client applications.
+ *
+ * <p>
+ * Each media item must have a unique ID within a provider.
+ *
+ * <p>
+ * A provider must always return stable IDs, since they will be used to
+ * issue long-term URI permission grants when an application interacts
+ * with {@link MediaStore#ACTION_PICK_IMAGES}.
+ * <p>
+ * Type: STRING
+ */
+ public static final String ID = "id";
+
+ /**
+ * Timestamp when a media item was capture, in milliseconds since
+ * January 1, 1970 00:00:00.0 UTC.
+ * <p>
+ * Implementations should extract this data from the metadata embedded in the media
+ * file. If this information is not available, a reasonable heuristic can be used, e.g.
+ * the time the media file was added to the media collection.
+ * <p>
+ * Type: LONG
+ *
+ * @see CloudMediaProviderContract.AlbumColumns#DATE_TAKEN_MILLIS
+ * @see System#currentTimeMillis()
+ */
+ public static final String DATE_TAKEN_MILLIS = "date_taken_millis";
+
+ /**
+ * Number associated with a media item indicating what generation or batch the media item
+ * was synced into the media collection.
+ * <p>
+ * Providers should associate a monotonically increasing sync generation number to each
+ * media item which is expected to increase for each atomic modification on the media item.
+ * This is useful for the OS to quickly identify that a media item has changed since a
+ * previous point in time. Note that this does not need to be unique across all media items,
+ * i.e. multiple media items can have the same SYNC_GENERATION value. However, the
+ * modification of a media item should increase the
+ * {@link MediaCollectionInfo#LAST_MEDIA_SYNC_GENERATION}.
+ * <p>
+ * Type: LONG
+ *
+ * @see MediaCollectionInfo#LAST_MEDIA_SYNC_GENERATION
+ */
+ public static final String SYNC_GENERATION = "sync_generation";
+
+ /**
+ * Concrete MIME type of a media file. For example, "image/png" or
+ * "video/mp4".
+ * <p>
+ * Type: STRING
+ */
+ public static final String MIME_TYPE = "mime_type";
+
+ /**
+ * Mime-type extension representing special format for a media item.
+ *
+ * Photo Picker requires special format tagging for media items.
+ * This is essential as media items can have various formats like
+ * Motion Photos, GIFs etc, which are not identifiable by
+ * {@link #MIME_TYPE}.
+ * <p>
+ * Type: INTEGER
+ */
+ public static final String STANDARD_MIME_TYPE_EXTENSION = "standard_mime_type_extension";
+
+ /**
+ * Constant for the {@link #STANDARD_MIME_TYPE_EXTENSION} column indicating
+ * that the media item doesn't have any special format associated with it.
+ */
+ public static final int STANDARD_MIME_TYPE_EXTENSION_NONE = 0;
+
+ /**
+ * Constant for the {@link #STANDARD_MIME_TYPE_EXTENSION} column indicating
+ * that the media item is a GIF.
+ */
+ public static final int STANDARD_MIME_TYPE_EXTENSION_GIF = 1;
+
+ /**
+ * Constant for the {@link #STANDARD_MIME_TYPE_EXTENSION} column indicating
+ * that the media item is a Motion Photo.
+ */
+ public static final int STANDARD_MIME_TYPE_EXTENSION_MOTION_PHOTO = 2;
+
+ /**
+ * Constant for the {@link #STANDARD_MIME_TYPE_EXTENSION} column indicating
+ * that the media item is an Animated Webp.
+ */
+ public static final int STANDARD_MIME_TYPE_EXTENSION_ANIMATED_WEBP = 3;
+
+ /**
+ * Size of a media file, in bytes.
+ * <p>
+ * Type: LONG
+ */
+ public static final String SIZE_BYTES = "size_bytes";
+
+ /**
+ * {@link MediaStore} URI of a media file if the file is available locally on the device.
+ * <p>
+ * If it's a cloud-only media file, this field should not be set.
+ * Any of the following URIs can be used: {@link MediaStore.Files},
+ * {@link MediaStore.Images} or {@link MediaStore.Video} e.g.
+ * {@code content://media/file/45}.
+ * <p>
+ * Implementations don't need to handle the {@link MediaStore} URI becoming invalid after
+ * the local item has been deleted or modified. If the URI becomes invalid or the
+ * local and cloud file content diverges, the OS will treat the cloud media item as a
+ * cloud-only item.
+ * <p>
+ * Type: STRING
+ */
+ public static final String MEDIA_STORE_URI = "media_store_uri";
+
+ /**
+ * Duration of a video file in ms. If the file is an image for which duration is not
+ * applicable, this field can be left empty or set to {@code zero}.
+ * <p>
+ * Type: LONG
+ */
+ public static final String DURATION_MILLIS = "duration_millis";
+
+ /**
+ * Whether the item has been favourited in the media collection. If {@code non-zero}, this
+ * media item will appear in the favourites category in the Photo Picker.
+ * <p>
+ * Type: INTEGER
+ */
+ public static final String IS_FAVORITE = "is_favorite";
+
+ /**
+ * Authority of the media item
+ * <p>
+ * Type: STRING
+ *
+ * @hide
+ */
+ public static final String AUTHORITY = "authority";
+
+ /**
+ * File path of the media item
+ * <p>
+ * Type: STRING
+ *
+ * @hide
+ */
+ public static final String DATA = "data";
+
+ /**
+ * Array of all {@link MediaColumn} fields.
+ *
+ * @hide
+ */
+ public static final String[] ALL_PROJECTION = new String[] {
+ ID,
+ DATE_TAKEN_MILLIS,
+ SYNC_GENERATION,
+ MIME_TYPE,
+ STANDARD_MIME_TYPE_EXTENSION,
+ SIZE_BYTES,
+ MEDIA_STORE_URI,
+ DURATION_MILLIS,
+ IS_FAVORITE,
+ DATA,
+ AUTHORITY,
+ };
+ }
+
+ /** Constants related to an album item, including {@link Cursor} column names */
+ public static final class AlbumColumns {
+ private AlbumColumns() {}
+
+ /**
+ * Unique ID of an album. This ID is both provided by and interpreted
+ * by a {@link CloudMediaProvider}.
+ * <p>
+ * Each album item must have a unique ID within a media collection.
+ * <p>
+ * A provider should return durable IDs, since they will be used to cache
+ * album information in the OS.
+ * <p>
+ * Type: STRING
+ */
+ public static final String ID = "id";
+
+
+ /**
+ * Display name of a an album, used as the primary title displayed to a
+ * user.
+ * <p>
+ * Type: STRING
+ */
+ public static final String DISPLAY_NAME = "display_name";
+
+ /**
+ * Timestamp of the most recently taken photo in an album, in milliseconds since
+ * January 1, 1970 00:00:00.0 UTC.
+ * <p>
+ * Type: LONG
+ *
+ * @see CloudMediaProviderContract.MediaColumns#DATE_TAKEN_MILLIS
+ * @see System#currentTimeMillis()
+ */
+ public static final String DATE_TAKEN_MILLIS = "date_taken_millis";
+
+ /**
+ * Media id to use as the album cover photo.
+ * <p>
+ * If this field is not provided, albums will be shown in the Photo Picker without a cover
+ * photo.
+ * <p>
+ * Type: LONG
+ *
+ * @see CloudMediaProviderContract.MediaColumns#ID
+ */
+ public static final String MEDIA_COVER_ID = "album_media_cover_id";
+
+ /**
+ * Total count of all media within the album, including photos and videos.
+ * <p>
+ * If this field is not provided, albums will be shown without a count in the Photo Picker.
+ * <p>
+ * Empty albums should be omitted from the {@link CloudMediaProvider#onQueryAlbums} result,
+ * i.e. zero is not a valid media count.
+ * <p>
+ * Type: LONG
+ */
+ public static final String MEDIA_COUNT = "album_media_count";
+
+ /**
+ * Authority of the album item
+ * <p>
+ * Type: STRING
+ *
+ * @hide
+ */
+ public static final String AUTHORITY = "authority";
+
+ /**
+ * Whether the album item was generated locally
+ * <p>
+ * Type: STRING
+ *
+ * @hide
+ */
+ public static final String IS_LOCAL = "is_local";
+
+ /**
+ * Array of all {@link AlbumColumn} fields.
+ *
+ * @hide
+ */
+ public static final String[] ALL_PROJECTION = new String[] {
+ ID,
+ DATE_TAKEN_MILLIS,
+ DISPLAY_NAME,
+ MEDIA_COVER_ID,
+ MEDIA_COUNT,
+ AUTHORITY,
+ };
+
+ /**
+ * Includes local media present in any directory containing
+ * {@link Environment#DIRECTORY_SCREENSHOTS} in relative path
+ *
+ * @hide
+ */
+ public static final String ALBUM_ID_SCREENSHOTS = "Screenshots";
+
+ /**
+ * Includes local images/videos that are present in the
+ * {@link Environment#DIRECTORY_DCIM}/Camera directory.
+ *
+ * @hide
+ */
+ public static final String ALBUM_ID_CAMERA = "Camera";
+
+ /**
+ * Includes local and cloud videos only.
+ *
+ * @hide
+ */
+ public static final String ALBUM_ID_VIDEOS = "Videos";
+
+ /**
+ * Includes local images/videos that have {@link MediaStore.MediaColumns#IS_DOWNLOAD} set.
+ *
+ * @hide
+ */
+ public static final String ALBUM_ID_DOWNLOADS = "Downloads";
+
+ /**
+ * Includes local and cloud images/videos that have been favorited by the user.
+ *
+ * @hide
+ */
+ public static final String ALBUM_ID_FAVORITES = "Favorites";
+ }
+
+ /** Constants related to a media collection */
+ public static final class MediaCollectionInfo {
+ private MediaCollectionInfo() {}
+
+ /**
+ * Media collection identifier
+ * <p>
+ * The only requirement on the collection ID is uniqueness on a device.
+ * <p>
+ * This value will not be interpreted by the OS, however it will be used to check the
+ * validity of cached data and URI grants to client apps. Anytime the media or album ids
+ * get re-indexed, a new collection with a new and unique id should be created so that the
+ * OS can clear its cache and more importantly, revoke any URI grants to apps.
+ * <p>
+ * Apps are recommended to generate unique collection ids with, {@link UUID#randomUUID}.
+ * This is preferred to using a simple monotonic sequence because the provider data could
+ * get cleared and it might have to re-index media items on the device without any history
+ * of its last ID. With random UUIDs, if data gets cleared, a new one can easily be
+ * generated safely.
+ * <p>
+ * Type: STRING
+ *
+ * @see CloudMediaProvider#onGetMediaCollectionInfo
+ */
+ public static final String MEDIA_COLLECTION_ID = "media_collection_id";
+
+ /**
+ * Last {@link CloudMediaProviderContract.MediaColumns#SYNC_GENERATION} in the media
+ * collection including deleted media items.
+ * <p>
+ * Providers should associate a monotonically increasing sync generation to each
+ * media item change (insertion/deletion/update). This is useful for the OS to quickly
+ * identify exactly which media items have changed since a previous point in time.
+ * <p>
+ * Type: LONG
+ *
+ * @see CloudMediaProviderContract#EXTRA_SYNC_GENERATION
+ * @see CloudMediaProvider#onGetMediaCollectionInfo
+ * @see CloudMediaProviderContract.MediaColumns#SYNC_GENERATION
+ */
+ public static final String LAST_MEDIA_SYNC_GENERATION = "last_media_sync_generation";
+
+ /**
+ * Name of the account that owns the media collection.
+ * <p>
+ * Type: STRING
+ *
+ * @see CloudMediaProvider#onGetMediaCollectionInfo
+ */
+ public static final String ACCOUNT_NAME = "account_name";
+
+ /**
+ * {@link Intent} Intent to launch an {@link Activity} to allow users configure their media
+ * collection account information like the account name.
+ * <p>
+ * Type: PARCELABLE
+ *
+ * @see CloudMediaProvider#onGetMediaCollectionInfo
+ */
+ public static final String ACCOUNT_CONFIGURATION_INTENT = "account_configuration_intent";
+ }
+
+ /**
+ * Opaque pagination token to retrieve the next page (cursor) from a media or album query.
+ * <p>
+ * Providers can optionally set this token as part of the {@link Cursor#setExtras}
+ * {@link Bundle}. If a token is set, the OS can pass it as a {@link Bundle} parameter when
+ * querying for media or albums to fetch subsequent pages. The provider can keep returning
+ * pagination tokens until the last page at which point it should not set a token on the
+ * {@link Cursor}.
+ * <p>
+ * If the provider handled the page token as part of the query, they must add
+ * the {@link #EXTRA_PAGE_TOKEN} key to the array of {@link ContentResolver#EXTRA_HONORED_ARGS}
+ * as part of the returned {@link Cursor#setExtras} {@link Bundle}.
+ *
+ * @see CloudMediaProvider#onQueryMedia
+ * @see CloudMediaProvider#onQueryAlbums
+ * <p>
+ * Type: STRING
+ */
+ public static final String EXTRA_PAGE_TOKEN = "android.provider.extra.PAGE_TOKEN";
+
+ /**
+ * {@link MediaCollectionInfo#MEDIA_COLLECTION_ID} on which the media or album query occurred.
+ *
+ * <p>
+ * Providers must set this token as part of the {@link Cursor#setExtras}
+ * {@link Bundle} returned from the cursors on query.
+ * This allows the OS to verify that the returned results match the
+ * {@link MediaCollectionInfo#MEDIA_COLLECTION_ID} queried via
+ * {@link CloudMediaProvider#onGetMediaCollectionInfo}. If the collection differs, the OS will
+ * ignore the result and may try again.
+ *
+ * @see CloudMediaProvider#onQueryMedia
+ * @see CloudMediaProvider#onQueryDeletedMedia
+ * @see CloudMediaProvider#onQueryAlbums
+ * <p>
+ * Type: STRING
+ */
+ public static final String EXTRA_MEDIA_COLLECTION_ID =
+ "android.provider.extra.MEDIA_COLLECTION_ID";
+
+ /**
+ * Generation number to fetch the latest media or album metadata changes from the media
+ * collection.
+ * <p>
+ * The provider should associate a monotonically increasing sync generation to each media
+ * item change (insertion/deletion/update). This is useful to quickly identify exactly which
+ * media items have changed since a previous point in time.
+ * <p>
+ * Providers should also associate a separate monotonically increasing sync generation
+ * for album changes (insertion/deletion/update). This album sync generation, should record
+ * both changes to the album metadata itself and changes to the media items contained in the
+ * album. E.g. a direct change to an album's
+ * {@link CloudMediaProviderContract.AlbumColumns#DISPLAY_NAME} will increase the
+ * album sync generation, likewise adding a photo to that album should also increase the
+ * sync generation.
+ * <p>
+ * Note that multiple media (or album) items can share a sync generation as long as the entire
+ * change appears atomic from the perspective of the query APIs. E.g. each item in a batch photo
+ * sync from the cloud can have the same sync generation if they were all synced atomically into
+ * the collection from the perspective of an external observer.
+ * <p>
+ * This extra can be passed as a {@link Bundle} parameter to the media or album query methods
+ * and the provider should only return items with a sync generation that is strictly greater
+ * than the one provided in the filter.
+ * <p>
+ * If the provider supports this filter, it must support the respective
+ * {@link CloudMediaProvider#onGetMediaCollectionInfo} methods to return the {@code count} and
+ * {@code max generation} for media or albums.
+ * <p>
+ * If the provider handled the generation, they must add the
+ * {@link #EXTRA_SYNC_GENERATION} key to the array of {@link ContentResolver#EXTRA_HONORED_ARGS}
+ * as part of the returned {@link Cursor#setExtras} {@link Bundle}.
+ *
+ * @see MediaCollectionInfo#LAST_MEDIA_SYNC_GENERATION
+ * @see CloudMediaProvider#onQueryMedia
+ * @see CloudMediaProvider#onQueryAlbums
+ * @see MediaStore.MediaColumns#GENERATION_MODIFIED
+ * <p>
+ * Type: LONG
+ */
+ public static final String EXTRA_SYNC_GENERATION = "android.provider.extra.SYNC_GENERATION";
+
+ /**
+ * Limits the query results to only media items matching the given album id.
+ * <p>
+ * If the provider handled the album filter, they must also add the {@link #EXTRA_ALBUM_ID}
+ * key to the array of {@link ContentResolver#EXTRA_HONORED_ARGS} as part of the returned
+ * {@link Cursor#setExtras} {@link Bundle}.
+ *
+ * @see CloudMediaProvider#onQueryMedia
+ * <p>
+ * Type: STRING
+ */
+ public static final String EXTRA_ALBUM_ID = "android.provider.extra.ALBUM_ID";
+
+ /**
+ * Limits the query results to only media items matching the give mimetype.
+ * <p>
+ * This may be a pattern, such as */*, to query for all available MIME types that match the
+ * pattern, e.g. {@code image/*} should match {@code image/jpeg} and {@code image/png}.
+ *
+ * @see CloudMediaProvider#onQueryMedia
+ * <p>
+ * Type: STRING
+ * @hide
+ */
+ public static final String EXTRA_MIME_TYPE = "android.provider.extra.MIME_TYPE";
+
+ /**
+ * Limits the query results to only media items less than the given file size in bytes.
+ * <p>
+ * This is only intended for the MediaProvider to implement for cross-user communication. Not
+ * for third party apps.
+ *
+ * @see CloudMediaProvider#onQueryMedia
+ * <p>
+ * Type: LONG
+ * @hide
+ */
+ public static final String EXTRA_SIZE_LIMIT_BYTES =
+ "android.provider.extra.EXTRA_SIZE_LIMIT_BYTES";
+
+ /**
+ * Forces the {@link CloudMediaProvider#onOpenPreview} file descriptor to return a thumbnail
+ * image. This is only useful for videos where the OS can either request a video or image
+ * for preview.
+ *
+ * @see CloudMediaProvider#onOpenPreview
+ * <p>
+ * Type: BOOLEAN
+ */
+ public static final String EXTRA_PREVIEW_THUMBNAIL =
+ "android.provider.extra.PREVIEW_THUMBNAIL";
+
+ /**
+ * A boolean to indicate {@link com.android.providers.media.photopicker.PhotoPickerProvider}
+ * this request is requesting a cached thumbnail file from MediaStore.
+ *
+ * Type: BOOLEAN
+ *
+ * {@hide}
+ */
+ public static final String EXTRA_MEDIASTORE_THUMB = "android.provider.extra.MEDIASTORE_THUMB";
+
+ /**
+ * Constant used to execute {@link CloudMediaProvider#onGetMediaCollectionInfo} via
+ * {@link ContentProvider#call}.
+ *
+ * {@hide}
+ */
+ public static final String METHOD_GET_MEDIA_COLLECTION_INFO = "android:getMediaCollectionInfo";
+
+ /**
+ * Constant used to execute {@link CloudMediaProvider#onCreateCloudMediaSurfaceController} via
+ * {@link ContentProvider#call}.
+ *
+ * {@hide}
+ */
+ public static final String METHOD_CREATE_SURFACE_CONTROLLER = "android:createSurfaceController";
+
+ /**
+ * Gets surface controller from {@link CloudMediaProvider#onCreateCloudMediaSurfaceController}.
+ * {@hide}
+ */
+ public static final String EXTRA_SURFACE_CONTROLLER =
+ "android.provider.extra.SURFACE_CONTROLLER";
+
+ /**
+ * Indicates whether to enable looping playback of media items.
+ * <p>
+ * In case this is not present, the default value should be false.
+ *
+ * @see CloudMediaProvider#onCreateCloudMediaSurfaceController
+ * @see CloudMediaProvider.CloudMediaSurfaceController#onConfigChange
+ * <p>
+ * Type: BOOLEAN
+ * By default, the value is true
+ */
+ public static final String EXTRA_LOOPING_PLAYBACK_ENABLED =
+ "android.provider.extra.LOOPING_PLAYBACK_ENABLED";
+
+ /**
+ * Indicates whether to mute audio during preview of media items.
+ *
+ * @see CloudMediaProvider#onCreateCloudMediaSurfaceController
+ * @see CloudMediaProvider.CloudMediaSurfaceController#onConfigChange
+ * <p>
+ * Type: BOOLEAN
+ * By default, the value is false
+ */
+ public static final String EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED =
+ "android.provider.extra.SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED";
+
+ /**
+ * Gets surface state callback from picker launched via
+ * {@link MediaStore#ACTION_PICK_IMAGES}).
+ *
+ * @see MediaStore#ACTION_PICK_IMAGES
+ *
+ * {@hide}
+ */
+ public static final String EXTRA_SURFACE_STATE_CALLBACK =
+ "android.provider.extra.SURFACE_STATE_CALLBACK";
+
+ /**
+ * Constant used to execute {@link CloudMediaProvider#onGetAsyncContentProvider()} via
+ * {@link android.content.ContentProvider#call}.
+ *
+ * {@hide}
+ */
+ public static final String METHOD_GET_ASYNC_CONTENT_PROVIDER =
+ "android:getAsyncContentProvider";
+
+ /**
+ * Constant used to get/set {@link IAsyncContentProvider} in {@link Bundle}.
+ *
+ * {@hide}
+ */
+ public static final String EXTRA_ASYNC_CONTENT_PROVIDER =
+ "android.provider.extra.ASYNC_CONTENT_PROVIDER";
+
+ /**
+ * Constant used to get/set {@link android.os.ParcelFileDescriptor} in {@link Bundle}.
+ *
+ * {@hide}
+ */
+ public static final String EXTRA_FILE_DESCRIPTOR = "android.provider.extra.file_descriptor";
+
+ /**
+ * Constant used to get/set CMP exception message in {@link Bundle}.
+ *
+ * {@hide}
+ */
+ public static final String EXTRA_ERROR_MESSAGE = "android.provider.extra.error_message";
+
+ /**
+ * Constant used to get/set the {@link CloudMediaProvider} authority.
+ *
+ * {@hide}
+ */
+ public static final String EXTRA_AUTHORITY = "android.provider.extra.authority";
+
+ /**
+ * URI path for {@link CloudMediaProvider#onQueryMedia}
+ *
+ * {@hide}
+ */
+ public static final String URI_PATH_MEDIA = "media";
+
+ /**
+ * URI path for {@link CloudMediaProvider#onQueryDeletedMedia}
+ *
+ * {@hide}
+ */
+ public static final String URI_PATH_DELETED_MEDIA = "deleted_media";
+
+ /**
+ * URI path for {@link CloudMediaProvider#onQueryAlbums}
+ *
+ * {@hide}
+ */
+ public static final String URI_PATH_ALBUM = "album";
+
+ /**
+ * URI path for {@link CloudMediaProvider#onGetMediaCollectionInfo}
+ *
+ * {@hide}
+ */
+ public static final String URI_PATH_MEDIA_COLLECTION_INFO = "media_collection_info";
+
+ /**
+ * URI path for {@link CloudMediaProvider#onCreateCloudMediaSurfaceController}
+ *
+ * {@hide}
+ */
+ public static final String URI_PATH_SURFACE_CONTROLLER = "surface_controller";
+}
diff --git a/apex/framework/java/android/provider/ExportedSince.java b/apex/framework/java/android/provider/ExportedSince.java
new file mode 100644
index 0000000..20a1a11
--- /dev/null
+++ b/apex/framework/java/android/provider/ExportedSince.java
@@ -0,0 +1,41 @@
+/*
+ * 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 android.provider;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import androidx.annotation.IntRange;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that a field is exported starting with the OS version
+ * @hide
+ */
+@Documented
+@Retention(RUNTIME)
+@Target({FIELD})
+public @interface ExportedSince {
+ /**
+ * The API level of the OS version to require.
+ */
+ @IntRange(from = 1)
+ int osVersion() default 1;
+}
diff --git a/apex/framework/java/android/provider/IAsyncContentProvider.aidl b/apex/framework/java/android/provider/IAsyncContentProvider.aidl
new file mode 100644
index 0000000..3e8340f
--- /dev/null
+++ b/apex/framework/java/android/provider/IAsyncContentProvider.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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 android.provider;
+
+import android.os.RemoteCallback;
+
+/**
+ * @hide
+ */
+oneway interface IAsyncContentProvider {
+
+ void openMedia(String mediaId, in RemoteCallback callback);
+}
\ No newline at end of file
diff --git a/apex/framework/java/android/provider/ICloudMediaSurfaceController.aidl b/apex/framework/java/android/provider/ICloudMediaSurfaceController.aidl
new file mode 100644
index 0000000..a9f48de
--- /dev/null
+++ b/apex/framework/java/android/provider/ICloudMediaSurfaceController.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package android.provider;
+
+import android.view.Surface;
+
+/**
+ * @hide
+ */
+interface ICloudMediaSurfaceController {
+
+ void onPlayerCreate();
+ void onPlayerRelease();
+
+ void onSurfaceCreated(int surfaceId, in Surface surface, @utf8InCpp String mediaId);
+ void onSurfaceChanged(int surfaceId, int format, int width, int height);
+ void onSurfaceDestroyed(int surfaceId);
+
+ void onMediaPlay(int surfaceId);
+ void onMediaPause(int surfaceId);
+ void onMediaSeekTo(int surfaceId, long timestampMillis);
+
+ void onConfigChange(in Bundle config);
+
+ void onDestroy();
+}
diff --git a/apex/framework/java/android/provider/ICloudMediaSurfaceStateChangedCallback.aidl b/apex/framework/java/android/provider/ICloudMediaSurfaceStateChangedCallback.aidl
new file mode 100644
index 0000000..d557dd0
--- /dev/null
+++ b/apex/framework/java/android/provider/ICloudMediaSurfaceStateChangedCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+package android.provider;
+
+import android.os.Bundle;
+
+/**
+ * Interface through which Photo Picker receives playback state updates from the CloudMediaProviders
+ * @hide
+ */
+interface ICloudMediaSurfaceStateChangedCallback {
+ void setPlaybackState(int surfaceId, int playbackState, in Bundle playbackStateInfo);
+}
diff --git a/apex/framework/java/android/provider/MediaStore.java b/apex/framework/java/android/provider/MediaStore.java
index 60ec082..ca67b8a 100644
--- a/apex/framework/java/android/provider/MediaStore.java
+++ b/apex/framework/java/android/provider/MediaStore.java
@@ -33,6 +33,7 @@
import android.app.PendingIntent;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ClipData;
+import android.content.ContentProvider;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentUris;
@@ -60,6 +61,7 @@
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.text.TextUtils;
@@ -216,6 +218,8 @@
public static final String GET_REDACTED_MEDIA_URI_LIST_CALL = "get_redacted_media_uri_list";
/** {@hide} */
public static final String EXTRA_URI_LIST = "uri_list";
+ /** {@hide} */
+ public static final String QUERY_ARG_REDACTED_URI = "android:query-arg-redacted-uri";
/** {@hide} */
public static final String EXTRA_URI = "uri";
@@ -228,7 +232,6 @@
public static final String EXTRA_CONTENT_VALUES = "content_values";
/** {@hide} */
public static final String EXTRA_RESULT = "result";
-
/** {@hide} */
public static final String EXTRA_FILE_DESCRIPTOR = "file_descriptor";
@@ -239,6 +242,40 @@
/** {@hide} */
public static final String EXTRA_IS_SYSTEM_GALLERY_RESPONSE = "is_system_gallery_response";
+ /** {@hide} */
+ public static final String IS_CURRENT_CLOUD_PROVIDER_CALL = "is_current_cloud_provider";
+ /** {@hide} */
+ public static final String IS_SUPPORTED_CLOUD_PROVIDER_CALL = "is_supported_cloud_provider";
+ /** {@hide} */
+ public static final String NOTIFY_CLOUD_MEDIA_CHANGED_EVENT_CALL =
+ "notify_cloud_media_changed_event";
+ /** {@hide} */
+ public static final String SYNC_PROVIDERS_CALL = "sync_providers";
+ /** {@hide} */
+ public static final String SET_CLOUD_PROVIDER_CALL = "set_cloud_provider";
+ /** {@hide} */
+ public static final String EXTRA_CLOUD_PROVIDER = "cloud_provider";
+ /** {@hide} */
+ public static final String EXTRA_CLOUD_PROVIDER_RESULT = "cloud_provider_result";
+ /** {@hide} */
+ public static final String CREATE_SURFACE_CONTROLLER = "create_surface_controller";
+
+ /** {@hide} */
+ public static final String USES_FUSE_PASSTHROUGH = "uses_fuse_passthrough";
+ /** {@hide} */
+ public static final String USES_FUSE_PASSTHROUGH_RESULT = "uses_fuse_passthrough_result";
+
+ /** {@hide} */
+ public static final String QUERY_ARG_LIMIT = ContentResolver.QUERY_ARG_LIMIT;
+ /** {@hide} */
+ public static final String QUERY_ARG_MIME_TYPE = "android:query-arg-mime_type";
+ /** {@hide} */
+ public static final String QUERY_ARG_SIZE_BYTES = "android:query-arg-size_bytes";
+ /** {@hide} */
+ public static final String QUERY_ARG_ALBUM_ID = "android:query-arg-album_id";
+ /** {@hide} */
+ public static final String QUERY_ARG_ALBUM_AUTHORITY = "android:query-arg-album_authority";
+
/**
* This is for internal use by the media scanner only.
* Name of the (optional) Uri parameter that determines whether to skip deleting
@@ -260,6 +297,16 @@
/** {@hide} */
public static final String PARAM_LIMIT = "limit";
+ /** {@hide} */
+ public static final int MY_USER_ID = UserHandle.myUserId();
+ /** {@hide} */
+ public static final int MY_UID = android.os.Process.myUid();
+ // Stolen from: UserHandle#getUserId
+ /** {@hide} */
+ public static final int PER_USER_RANGE = 100000;
+
+ private static final int PICK_IMAGES_MAX_LIMIT = 100;
+
/**
* Activity Action: Launch a music player.
* The activity should be able to play, browse, or manipulate music files stored on the device.
@@ -634,6 +681,80 @@
public final static String EXTRA_OUTPUT = "output";
/**
+ * Activity Action: Allow the user to select images or videos provided by
+ * system and return it. This is different than {@link Intent#ACTION_PICK}
+ * and {@link Intent#ACTION_GET_CONTENT} in that
+ * <ul>
+ * <li> the data for this action is provided by the system
+ * <li> this action is only used for picking images and videos
+ * <li> caller gets read access to user picked items even without storage
+ * permissions
+ * </ul>
+ * <p>
+ * Callers can optionally specify MIME type (such as {@code image/*} or
+ * {@code video/*}), resulting in a range of content selection that the
+ * caller is interested in. The optional MIME type can be requested with
+ * {@link Intent#setType(String)}.
+ * <p>
+ * If the caller needs multiple returned items (or caller wants to allow
+ * multiple selection), then it can specify
+ * {@link MediaStore#EXTRA_PICK_IMAGES_MAX} to indicate this.
+ * <p>
+ * When the caller requests multiple selection, the value of
+ * {@link MediaStore#EXTRA_PICK_IMAGES_MAX} must be a positive integer
+ * greater than 1 and less than or equal to
+ * {@link MediaStore#getPickImagesMaxLimit}, otherwise
+ * {@link Activity#RESULT_CANCELED} is returned.
+ * <p>
+ * Output: MediaStore content URI(s) of the item(s) that was picked.
+ * Unlike other MediaStore URIs, these are referred to as 'picker' URIs and
+ * expose a limited set of read-only operations. Specifically, picker URIs
+ * can only be opened for read and queried for columns in {@link PickerMediaColumns}.
+ * <p>
+ * Before this API, apps could use {@link Intent#ACTION_GET_CONTENT}. However,
+ * {@link #ACTION_PICK_IMAGES} is now the recommended option for images and videos,
+ * since it ofers a better user experience.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_PICK_IMAGES = "android.provider.action.PICK_IMAGES";
+
+ /**
+ * Activity Action: Launch settings controlling images or videos selection with
+ * {@link #ACTION_PICK_IMAGES}.
+ *
+ * The settings page allows a user to change the enabled {@link CloudMediaProvider} on the
+ * device and other media selection configurations.
+ *
+ * @see #ACTION_PICK_IMAGES
+ * @see #isCurrentCloudMediaProviderAuthority(ContentResolver, String)
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_PICK_IMAGES_SETTINGS =
+ "android.provider.action.PICK_IMAGES_SETTINGS";
+
+ /**
+ * The name of an optional intent-extra used to allow multiple selection of
+ * items and constrain maximum number of items that can be returned by
+ * {@link MediaStore#ACTION_PICK_IMAGES}, action may still return nothing
+ * (0 items) if the user chooses to cancel.
+ * <p>
+ * The value of this intent-extra should be a positive integer greater
+ * than 1 and less than or equal to
+ * {@link MediaStore#getPickImagesMaxLimit}, otherwise
+ * {@link Activity#RESULT_CANCELED} is returned.
+ */
+ public final static String EXTRA_PICK_IMAGES_MAX = "android.provider.extra.PICK_IMAGES_MAX";
+
+ /**
+ * The maximum limit for the number of items that can be selected using
+ * {@link MediaStore#ACTION_PICK_IMAGES} when launched in multiple selection mode.
+ * This can be used as a constant value for {@link MediaStore#EXTRA_PICK_IMAGES_MAX}.
+ */
+ public static int getPickImagesMaxLimit() {
+ return PICK_IMAGES_MAX_LIMIT;
+ }
+
+ /**
* Specify that the caller wants to receive the original media format without transcoding.
*
* <b>Caution: using this flag can cause app
@@ -692,6 +813,13 @@
"android.provider.extra.MEDIA_CAPABILITIES_UID";
/**
+ * Flag used to set file mode in bundle for opening a document.
+ *
+ * @hide
+ */
+ public static final String EXTRA_MODE = "android.provider.extra.MODE";
+
+ /**
* The string that is used when a media attribute is not known. For example,
* if an audio file does not have any meta data, the artist and album columns
* will be set to this value.
@@ -1684,6 +1812,69 @@
}
/**
+ * Photo picker metadata columns.
+ *
+ * @see #ACTION_PICK_IMAGES
+ */
+ public static class PickerMediaColumns {
+ private PickerMediaColumns() {}
+
+ /**
+ * This is identical to {@link MediaColumns#DATA}, however, apps should not assume that the
+ * file is always available because the file may be backed by a {@link CloudMediaProvider}
+ * fetching content over a network. Therefore, apps must be prepared to handle any
+ * additional file-based I/O errors that could occur as a result of network errors.
+ *
+ * @see MediaColumns#DATA
+ */
+ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ public static final String DATA = MediaColumns.DATA;
+
+ /**
+ * This is identical to {@link MediaColumns#SIZE}.
+ *
+ * @see MediaColumns#SIZE
+ */
+ @BytesLong
+ @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
+ public static final String SIZE = MediaColumns.SIZE;
+
+ /**
+ * This is identical to {@link MediaColumns#DISPLAY_NAME}.
+ *
+ * @see MediaColumns#DISPLAY_NAME
+ */
+ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ public static final String DISPLAY_NAME = MediaColumns.DISPLAY_NAME;
+
+ /**
+ * This is identical to {@link MediaColumns#DATE_TAKEN}.
+ *
+ * @see MediaColumns#DATE_TAKEN
+ */
+ @CurrentTimeMillisLong
+ @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
+ public static final String DATE_TAKEN = MediaColumns.DATE_TAKEN;
+
+ /**
+ * This is identical to {@link MediaColumns#MIME_TYPE}.
+ *
+ * @see MediaColumns#MIME_TYPE
+ */
+ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ public static final String MIME_TYPE = MediaColumns.MIME_TYPE;
+
+ /**
+ * This is identical to {@link MediaColumns#DURATION}.
+ *
+ * @see MediaColumns#DURATION
+ */
+ @DurationMillisLong
+ @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
+ public static final String DURATION_MILLIS = MediaColumns.DURATION;
+ }
+
+ /**
* Media provider table containing an index of all files in the media storage,
* including non-media files. This should be used by applications that work with
* non-media file types (text, HTML, PDF, etc) as well as applications that need to
@@ -1959,6 +2150,57 @@
*/
// @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
public static final String _USER_ID = "_user_id";
+
+ /**
+ * Special format for a file.
+ *
+ * Photo Picker requires special format tagging for media files.
+ * This is essential as {@link Images} collection can include
+ * images of various formats like Motion Photos, GIFs etc, which
+ * is not identifiable by {@link #MIME_TYPE}.
+ *
+ * @hide
+ */
+ // @Column(value = Cursor.FIELD_TYPE_INTEGER)
+ public static final String _SPECIAL_FORMAT = "_special_format";
+
+ /**
+ * Constant for the {@link #_SPECIAL_FORMAT} column indicating
+ * that the file doesn't have any special format associated with it.
+ *
+ * @hide
+ */
+ public static final int _SPECIAL_FORMAT_NONE =
+ CloudMediaProviderContract.MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE;
+
+ /**
+ * Constant for the {@link #_SPECIAL_FORMAT} column indicating
+ * that the file is a GIF file.
+ *
+ * @hide
+ */
+ public static final int _SPECIAL_FORMAT_GIF =
+ CloudMediaProviderContract.MediaColumns.STANDARD_MIME_TYPE_EXTENSION_GIF;
+
+ /**
+ * Constant for the {@link #_SPECIAL_FORMAT} column indicating
+ * that the file is a Motion Photo.
+ *
+ * @hide
+ */
+ public static final int _SPECIAL_FORMAT_MOTION_PHOTO =
+ CloudMediaProviderContract.MediaColumns.
+ STANDARD_MIME_TYPE_EXTENSION_MOTION_PHOTO;
+
+ /**
+ * Constant for the {@link #_SPECIAL_FORMAT} column indicating
+ * that the file is an Animated Webp.
+ *
+ * @hide
+ */
+ public static final int _SPECIAL_FORMAT_ANIMATED_WEBP =
+ CloudMediaProviderContract.MediaColumns.
+ STANDARD_MIME_TYPE_EXTENSION_ANIMATED_WEBP;
}
}
@@ -4205,6 +4447,50 @@
return out.getBoolean(EXTRA_IS_SYSTEM_GALLERY_RESPONSE);
}
+ private static Uri maybeRemoveUserId(@NonNull Uri uri) {
+ if (uri.getUserInfo() == null) return uri;
+
+ Uri.Builder builder = uri.buildUpon();
+ builder.authority(uri.getHost());
+ return builder.build();
+ }
+
+ private static List<Uri> maybeRemoveUserId(@NonNull List<Uri> uris) {
+ List<Uri> newUriList = new ArrayList<>();
+ for (Uri uri : uris) {
+ newUriList.add(maybeRemoveUserId(uri));
+ }
+ return newUriList;
+ }
+
+ private static int getUserIdFromUri(Uri uri) {
+ final String userId = uri.getUserInfo();
+ return userId == null ? MY_USER_ID : Integer.parseInt(userId);
+ }
+
+ @RequiresApi(Build.VERSION_CODES.S)
+ private static Uri maybeAddUserId(@NonNull Uri uri, String userId) {
+ if (userId == null) {
+ return uri;
+ }
+
+ return ContentProvider.createContentUriForUser(uri,
+ UserHandle.of(Integer.parseInt(userId)));
+ }
+
+ @RequiresApi(Build.VERSION_CODES.S)
+ private static List<Uri> maybeAddUserId(@NonNull List<Uri> uris, String userId) {
+ if (userId == null) {
+ return uris;
+ }
+
+ List<Uri> newUris = new ArrayList<>();
+ for (Uri uri : uris) {
+ newUris.add(maybeAddUserId(uri, userId));
+ }
+ return newUris;
+ }
+
/**
* Returns an EXIF redacted version of {@code uri} i.e. a {@link Uri} with metadata such as
* location, GPS datestamp etc. redacted from the EXIF headers.
@@ -4225,18 +4511,36 @@
* @throws SecurityException if the caller doesn't have the read access to {@code uri}
* @see #getRedactedUri(ContentResolver, List)
*/
+ @RequiresApi(Build.VERSION_CODES.S)
@Nullable
public static Uri getRedactedUri(@NonNull ContentResolver resolver, @NonNull Uri uri) {
- try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY)) {
+ final String authority = uri.getAuthority();
+ try (ContentProviderClient client = resolver.acquireContentProviderClient(authority)) {
final Bundle in = new Bundle();
- in.putParcelable(EXTRA_URI, uri);
+ final String userId = uri.getUserInfo();
+ // NOTE: The user-id in URI authority is ONLY required to find the correct MediaProvider
+ // process. Once in the correct process, the field is no longer required and may cause
+ // breakage in MediaProvider code. This is because per process logic is agnostic of
+ // user-id. Hence strip away the user ids from URI, if present.
+ in.putParcelable(EXTRA_URI, maybeRemoveUserId(uri));
final Bundle out = client.call(GET_REDACTED_MEDIA_URI_CALL, null, in);
- return out.getParcelable(EXTRA_URI);
+ // Add the user-id back to the URI if we had striped it earlier.
+ return maybeAddUserId((Uri) out.getParcelable(EXTRA_URI), userId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
+ private static void verifyUrisBelongToSingleUserId(@NonNull List<Uri> uris) {
+ final int userId = getUserIdFromUri(uris.get(0));
+ for (Uri uri : uris) {
+ if (userId != getUserIdFromUri(uri)) {
+ throw new IllegalArgumentException(
+ "All the uris should belong to a single user-id");
+ }
+ }
+ }
+
/**
* Returns a list of EXIF redacted version of {@code uris} i.e. a {@link Uri} with metadata
* such as location, GPS datestamp etc. redacted from the EXIF headers.
@@ -4257,16 +4561,27 @@
* when the corresponding {@link Uri} could not be found or is unsupported
* @throws SecurityException if the caller doesn't have the read access to all the elements
* in {@code uris}
+ * @throws IllegalArgumentException if all the uris in {@code uris} don't belong to same user id
* @see #getRedactedUri(ContentResolver, Uri)
*/
+ @RequiresApi(Build.VERSION_CODES.S)
@NonNull
public static List<Uri> getRedactedUri(@NonNull ContentResolver resolver,
@NonNull List<Uri> uris) {
- try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY)) {
+ verifyUrisBelongToSingleUserId(uris);
+ final String authority = uris.get(0).getAuthority();
+ try (ContentProviderClient client = resolver.acquireContentProviderClient(authority)) {
+ final String userId = uris.get(0).getUserInfo();
final Bundle in = new Bundle();
- in.putParcelableArrayList(EXTRA_URI_LIST, (ArrayList<? extends Parcelable>) uris);
+ // NOTE: The user-id in URI authority is ONLY required to find the correct MediaProvider
+ // process. Once in the correct process, the field is no longer required and may cause
+ // breakage in MediaProvider code. This is because per process logic is agnostic of
+ // user-id. Hence strip away the user ids from URIs, if present.
+ in.putParcelableArrayList(EXTRA_URI_LIST,
+ (ArrayList<? extends Parcelable>) maybeRemoveUserId(uris));
final Bundle out = client.call(GET_REDACTED_MEDIA_URI_LIST_CALL, null, in);
- return out.getParcelableArrayList(EXTRA_URI_LIST);
+ // Add the user-id back to the URI if we had striped it earlier.
+ return maybeAddUserId(out.getParcelableArrayList(EXTRA_URI_LIST), userId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -4363,4 +4678,61 @@
return false;
}
}
+
+ /**
+ * Returns {@code true} if and only if the caller with {@code authority} is the currently
+ * enabled {@link CloudMediaProvider}. More specifically, {@code false} is also returned
+ * if the calling uid doesn't match the uid of the {@code authority}.
+ *
+ * @see android.provider.CloudMediaProvider
+ * @see #isSupportedCloudMediaProviderAuthority(ContentResolver, String)
+ */
+ public static boolean isCurrentCloudMediaProviderAuthority(@NonNull ContentResolver resolver,
+ @NonNull String authority) {
+ return callForCloudProvider(resolver, IS_CURRENT_CLOUD_PROVIDER_CALL, authority);
+ }
+
+ /**
+ * Returns {@code true} if and only if the caller with {@code authority} is a supported
+ * {@link CloudMediaProvider}. More specifically, {@code false} is also returned
+ * if the calling uid doesn't match the uid of the {@code authority}.
+ *
+ * @see android.provider.CloudMediaProvider
+ * @see #isCurrentCloudMediaProviderAuthority(ContentResolver, String)
+ */
+ public static boolean isSupportedCloudMediaProviderAuthority(@NonNull ContentResolver resolver,
+ @NonNull String authority) {
+ return callForCloudProvider(resolver, IS_SUPPORTED_CLOUD_PROVIDER_CALL, authority);
+ }
+
+ /**
+ * Notifies the OS about a cloud media event requiring a full or incremental media collection
+ * sync for the currently enabled cloud provider, {@code authority}.
+ *
+ * The OS will schedule the sync in the background and will attempt to batch frequent
+ * notifications into a single sync event.
+ *
+ * If the caller is not the currently enabled cloud provider as returned by
+ * {@link #isCurrentCloudMediaProviderAuthority(ContentResolver, String)}, the request will be
+ * unsuccessful.
+ *
+ * @return {@code true} if the notification was successful, {@code false} otherwise
+ */
+ public static void notifyCloudMediaChangedEvent(@NonNull ContentResolver resolver,
+ @NonNull String authority, @NonNull String currentMediaCollectionId)
+ throws SecurityException {
+ if (!callForCloudProvider(resolver, NOTIFY_CLOUD_MEDIA_CHANGED_EVENT_CALL, authority)) {
+ throw new SecurityException("Failed to notify cloud media changed event");
+ }
+ }
+
+ private static boolean callForCloudProvider(ContentResolver resolver, String method,
+ String callingAuthority) {
+ Objects.requireNonNull(resolver);
+ Objects.requireNonNull(method);
+ Objects.requireNonNull(callingAuthority);
+
+ final Bundle out = resolver.call(AUTHORITY, method, callingAuthority, /* extras */ null);
+ return out.getBoolean(EXTRA_CLOUD_PROVIDER_RESULT);
+ }
}
diff --git a/apex/permissions/Android.bp b/apex/permissions/Android.bp
new file mode 100644
index 0000000..e7330a8
--- /dev/null
+++ b/apex/permissions/Android.bp
@@ -0,0 +1,26 @@
+
+//
+// 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.
+//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+ default_visibility: ["//packages/providers/MediaProvider:__subpackages__"],
+}
+
+prebuilt_etc {
+ name: "privapp_allowlist_com.android.providers.media.module.xml",
+ sub_dir: "permissions",
+ src: "com.android.providers.media.module.xml",
+}
\ No newline at end of file
diff --git a/apex/permissions/OWNERS b/apex/permissions/OWNERS
new file mode 100644
index 0000000..8b7e2e5
--- /dev/null
+++ b/apex/permissions/OWNERS
@@ -0,0 +1,2 @@
+per-file *.xml,OWNERS = set noparent
+per-file *.xml,OWNERS = file:platform/frameworks/base:/data/etc/OWNERS
diff --git a/apex/permissions/com.android.providers.media.module.xml b/apex/permissions/com.android.providers.media.module.xml
new file mode 100644
index 0000000..86da4d5
--- /dev/null
+++ b/apex/permissions/com.android.providers.media.module.xml
@@ -0,0 +1,38 @@
+<?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
+ -->
+<permissions>
+ <privapp-permissions package="com.android.providers.media.module">
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.USE_RESERVED_DISK"/>
+ <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
+ <permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
+ <permission name="android.permission.WATCH_APPOPS"/>
+ <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
+ <permission name="android.permission.UPDATE_DEVICE_STATS"/>
+ <!-- Permissions required for reading and logging compat changes -->
+ <permission name="android.permission.LOG_COMPAT_CHANGE" />
+ <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
+ <permission name="android.permission.REGISTER_STATS_PULL_ATOM" />
+ <!-- Permissions required for reading DeviceConfig -->
+ <permission name="android.permission.READ_DEVICE_CONFIG" />
+ <permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"/>
+ <permission name="android.permission.MODIFY_QUIET_MODE"/>
+ <!-- Permissions required to check if an app is in the foreground or not during IO -->
+ <permission name="android.permission.PACKAGE_USAGE_STATS"/>
+ </privapp-permissions>
+</permissions>
\ No newline at end of file
diff --git a/apex/testing/Android.bp b/apex/testing/Android.bp
index 41612ba..d42a561 100644
--- a/apex/testing/Android.bp
+++ b/apex/testing/Android.bp
@@ -1,10 +1,6 @@
package {
// See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "packages_providers_MediaProvider_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["packages_providers_MediaProvider_license"],
+ default_applicable_licenses: ["Android-Apache-2.0"],
}
apex_test {
diff --git a/deploy.sh b/deploy.sh
index cc1be8e..5666be8 100755
--- a/deploy.sh
+++ b/deploy.sh
@@ -1,7 +1,7 @@
set -e
# Build both our APK and APEX combined together
-./build/soong/soong_ui.bash --make-mode -j64 MediaProviderLegacy com.google.android.mediaprovider
+MODULE_BUILD_FROM_SOURCE=true ./build/soong/soong_ui.bash --make-mode -j64 MediaProviderLegacy com.google.android.mediaprovider
# Push our updated APEX to device, then force apexd to remount it
adb shell stop
diff --git a/errorprone/Android.bp b/errorprone/Android.bp
index 3f11f91..fc3d67f 100644
--- a/errorprone/Android.bp
+++ b/errorprone/Android.bp
@@ -1,11 +1,6 @@
-
package {
// See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "packages_providers_MediaProvider_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["packages_providers_MediaProvider_license"],
+ default_applicable_licenses: ["Android-Apache-2.0"],
}
java_plugin {
diff --git a/errorprone/src/com/android/providers/media/MimeTypeChecker.java b/errorprone/src/com/android/providers/media/MimeTypeChecker.java
index 2b7cb3d..997c1ba 100644
--- a/errorprone/src/com/android/providers/media/MimeTypeChecker.java
+++ b/errorprone/src/com/android/providers/media/MimeTypeChecker.java
@@ -27,9 +27,11 @@
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.matchers.Matchers;
+import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.SwitchTree;
+import com.sun.tools.javac.code.Type;
import java.util.function.Predicate;
@@ -70,6 +72,12 @@
@Override
public Description matchSwitch(SwitchTree tree, VisitorState state) {
+ // Ignore non-string switch-case
+ final Type switchType = ASTHelpers.getType(tree.getExpression());
+ if (switchType != null && !String.class.getName().equals(switchType.toString())) {
+ return Description.NO_MATCH;
+ }
+
if (MIME_WITHOUT_CASE_FOLDING.matches(tree.getExpression(), state)) {
return buildDescription(tree).setMessage(MESSAGE).build();
}
@@ -85,8 +93,8 @@
public boolean matches(ExpressionTree tree, VisitorState state) {
// This is a pretty rough way to match raw names, but it works
final String string = tree.toString();
- return string.toLowerCase().contains("mime") && !string.contains("toUpperCase")
- && !string.contains("toLowerCase");
+ return string.toLowerCase().contains("mime") &&
+ !string.contains("toUpperCase") && !string.contains("toLowerCase");
}
@Override
diff --git a/gen_strings.py b/gen_strings.py
index a3d98be..23b3315 100755
--- a/gen_strings.py
+++ b/gen_strings.py
@@ -38,42 +38,42 @@
if verb == "trash":
print Template('''
<!-- Dialog title asking if user will allow $verb permission to the $data item displayed below this string. [CHAR LIMIT=128] -->
-<plurals name="permission_${verb}_${data}">
- <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this $datalabel to trash?</item>
- <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> ${datalabel}s to trash?</item>
-</plurals>
+<string name="permission_${verb}_${data}"> {count, plural,
+ =1 {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this $datalabel to trash?}
+ other {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> ${datalabel}s to trash?}
+}</string>
''').substitute(vars()).strip("\n")
print Template('''
<!-- Progress dialog message after user allows $verb permission to the $data item [CHAR LIMIT=128] -->
-<plurals name="permission_progress_${verb}_${data}">
- <item quantity="one">Moving $datalabel to trash…</item>
- <item quantity="other">Moving <xliff:g id="count" example="42">^1</xliff:g> ${datalabel}s to trash…</item>
-</plurals>
+<string name="permission_progress_${verb}_${data}"> {count, plural,
+ =1 {Moving $datalabel to trash…}
+ other {Moving <xliff:g id="count" example="42">^1</xliff:g> ${datalabel}s to trash…}
+}</string>
''').substitute(vars()).strip("\n")
elif verb == "untrash":
print Template('''
<!-- Dialog title asking if user will allow $verb permission to the $data item displayed below this string. [CHAR LIMIT=128] -->
-<plurals name="permission_${verb}_${data}">
- <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this $datalabel out of trash?</item>
- <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> ${datalabel}s out of trash?</item>
-</plurals>
+<string name="permission_${verb}_${data}"> {count, plural,
+ =1 {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this $datalabel out of trash?}
+ other {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> ${datalabel}s out of trash?}
+}</string>
''').substitute(vars()).strip("\n")
print Template('''
<!-- Progress dialog message after user allows $verb permission to the $data item [CHAR LIMIT=128] -->
-<plurals name="permission_progress_${verb}_${data}">
- <item quantity="one">Moving $datalabel out of trash…</item>
- <item quantity="other">Moving <xliff:g id="count" example="42">^1</xliff:g> ${datalabel}s out of trash…</item>
-</plurals>
+<string name="permission_progress_${verb}_${data}"> {count, plural,
+ =1 {Moving $datalabel out of trash…}
+ other {Moving <xliff:g id="count" example="42">^1</xliff:g> ${datalabel}s out of trash…}
+}</string>
''').substitute(vars()).strip("\n")
else:
print Template('''
<!-- Dialog title asking if user will allow $verb permission to the $data item displayed below this string. [CHAR LIMIT=128] -->
-<plurals name="permission_${verb}_${data}">
- <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to $verblabel this $datalabel?</item>
- <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to $verblabel <xliff:g id="count" example="42">^2</xliff:g> ${datalabel}s?</item>
-</plurals>
+<string name="permission_${verb}_${data}"> {count, plural,
+ =1 {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to $verblabel this $datalabel?}
+ other {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to $verblabel <xliff:g id="count" example="42">^2</xliff:g> ${datalabel}s?}
+}</string>
''').substitute(vars()).strip("\n")
if verb == "write":
actionLabel = "Modifying"
@@ -81,10 +81,10 @@
actionLabel = "Deleting"
print Template('''
<!-- Progress dialog message after user allows $verb permission to the $data item [CHAR LIMIT=128] -->
-<plurals name="permission_progress_${verb}_${data}">
- <item quantity="one">$actionLabel $datalabel…</item>
- <item quantity="other">$actionLabel <xliff:g id="count" example="42">^1</xliff:g> ${datalabel}s…</item>
-</plurals>
+<string name="permission_progress_${verb}_${data}"> {count, plural,
+ =1 {$actionLabel $datalabel…}
+ other {$actionLabel <xliff:g id="count" example="42">^1</xliff:g> ${datalabel}s…}
+}</string>
''').substitute(vars()).strip("\n")
print '''
diff --git a/jarjar-rules.txt b/jarjar-rules.txt
new file mode 100644
index 0000000..f719207
--- /dev/null
+++ b/jarjar-rules.txt
@@ -0,0 +1,2 @@
+rule com.android.modules.utils.** com.android.providers.media.internal.modules.utils.@1
+rule com.android.internal.logging.** com.android.providers.media.internal.logging.@1
diff --git a/jni/Android.bp b/jni/Android.bp
index 758bee1..b94f1b9 100644
--- a/jni/Android.bp
+++ b/jni/Android.bp
@@ -16,11 +16,7 @@
package {
// See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "packages_providers_MediaProvider_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["packages_providers_MediaProvider_license"],
+ default_applicable_licenses: ["Android-Apache-2.0"],
}
cc_library_shared {
@@ -38,6 +34,7 @@
],
header_libs: [
+ "bpf_syscall_wrappers",
"libnativehelper_header_only",
],
@@ -67,6 +64,7 @@
tidy: true,
tidy_checks: [
"-google-runtime-int",
+ "-performance-no-int-to-ptr",
],
sdk_version: "current",
@@ -103,6 +101,9 @@
],
tidy: true,
+ tidy_checks: [
+ "-performance-no-int-to-ptr",
+ ],
sdk_version: "current",
stl: "c++_static",
diff --git a/jni/FuseDaemon.cpp b/jni/FuseDaemon.cpp
old mode 100755
new mode 100644
index f711405..b2b8ab3
--- a/jni/FuseDaemon.cpp
+++ b/jni/FuseDaemon.cpp
@@ -17,10 +17,10 @@
#define LIBFUSE_LOG_TAG "libfuse"
#include "FuseDaemon.h"
-#include "android-base/strings.h"
#include <android-base/logging.h>
#include <android-base/properties.h>
+#include <android-base/strings.h>
#include <android/log.h>
#include <android/trace.h>
#include <ctype.h>
@@ -28,11 +28,11 @@
#include <errno.h>
#include <fcntl.h>
#include <fuse_i.h>
+#include <fuse_kernel.h>
#include <fuse_log.h>
#include <fuse_lowlevel.h>
#include <inttypes.h>
#include <limits.h>
-#include <linux/fuse.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@@ -51,7 +51,6 @@
#include <unistd.h>
#include <iostream>
-#include <list>
#include <map>
#include <mutex>
#include <queue>
@@ -61,18 +60,18 @@
#include <unordered_set>
#include <vector>
+#define BPF_FD_JUST_USE_INT
+#include "BpfSyscallWrappers.h"
#include "MediaProviderWrapper.h"
#include "libfuse_jni/FuseUtils.h"
#include "libfuse_jni/ReaddirHelper.h"
#include "libfuse_jni/RedactionInfo.h"
-#include "node-inl.h"
using mediaprovider::fuse::DirectoryEntry;
using mediaprovider::fuse::dirhandle;
using mediaprovider::fuse::handle;
using mediaprovider::fuse::node;
using mediaprovider::fuse::RedactionInfo;
-using std::list;
using std::string;
using std::vector;
@@ -117,9 +116,16 @@
const std::regex PATTERN_OWNED_PATH(
"^/storage/[^/]+/(?:[0-9]+/)?Android/(?:data|obb)/([^/]+)(/?.*)?",
std::regex_constants::icase);
+const std::regex PATTERN_BPF_BACKING_PATH("^/storage/[^/]+/[0-9]+/Android/(data|obb)$",
+ std::regex_constants::icase);
static constexpr char TRANSFORM_SYNTHETIC_DIR[] = "synthetic";
static constexpr char TRANSFORM_TRANSCODE_DIR[] = "transcode";
+static constexpr char PRIMARY_VOLUME_PREFIX[] = "/storage/emulated";
+
+static constexpr char FUSE_BPF_PROG_PATH[] = "/sys/fs/bpf/prog_fuse_media_fuse_media";
+
+enum class BpfFd { REMOVE = -1 };
/*
* In order to avoid double caching with fuse, call fadvise on the file handles
@@ -247,19 +253,27 @@
/* Single FUSE mount */
struct fuse {
- explicit fuse(const std::string& _path, ino_t _ino)
+ explicit fuse(const std::string& _path, const ino_t _ino, const bool _uncached_mode,
+ const bool _bpf, const int _bpf_fd,
+ const std::vector<string>& _supported_transcoding_relative_paths,
+ const std::vector<string>& _supported_uncached_relative_paths)
: path(_path),
tracker(mediaprovider::fuse::NodeTracker(&lock)),
root(node::CreateRoot(_path, &lock, _ino, &tracker)),
+ uncached_mode(_uncached_mode),
mp(0),
zero_addr(0),
disable_dentry_cache(false),
- passthrough(false) {}
+ passthrough(false),
+ bpf(_bpf),
+ bpf_fd(_bpf_fd),
+ supported_transcoding_relative_paths(_supported_transcoding_relative_paths),
+ supported_uncached_relative_paths(_supported_uncached_relative_paths) {}
inline bool IsRoot(const node* node) const { return node == root; }
inline string GetEffectiveRootPath() {
- if (android::base::StartsWith(path, "/storage/emulated")) {
+ if (android::base::StartsWith(path, PRIMARY_VOLUME_PREFIX)) {
return path + "/" + MY_USER_ID_STRING;
}
return path;
@@ -279,6 +293,14 @@
return node::FromInode(inode, &tracker);
}
+ inline node* FromInodeNoThrow(__u64 inode) {
+ if (inode == FUSE_ROOT_ID) {
+ return root;
+ }
+
+ return node::FromInodeNoThrow(inode, &tracker);
+ }
+
inline __u64 ToInode(node* node) const {
if (IsRoot(node)) {
return FUSE_ROOT_ID;
@@ -287,6 +309,57 @@
return node::ToInode(node);
}
+ inline bool IsTranscodeSupportedPath(const string& path) {
+ // Keep in sync with MediaProvider#supportsTranscode
+ if (!android::base::EndsWithIgnoreCase(path, ".mp4")) {
+ return false;
+ }
+
+ const std::string& base_path = GetEffectiveRootPath() + "/";
+ for (const std::string& relative_path : supported_transcoding_relative_paths) {
+ if (android::base::StartsWithIgnoreCase(path, base_path + relative_path)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ inline bool IsUncachedPath(const std::string& path) {
+ const std::string base_path = GetEffectiveRootPath() + "/";
+ for (const std::string& relative_path : supported_uncached_relative_paths) {
+ if (android::base::StartsWithIgnoreCase(path, base_path + relative_path)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ inline bool ShouldNotCache(const std::string& path) {
+ if (uncached_mode) {
+ // Cache is disabled for the entire volume.
+ return true;
+ }
+
+ if (supported_uncached_relative_paths.empty()) {
+ // By default there is no supported uncached path. Just return early in this case.
+ return false;
+ }
+
+ if (!android::base::StartsWithIgnoreCase(path, PRIMARY_VOLUME_PREFIX)) {
+ // Uncached path config applies only to primary volumes.
+ return false;
+ }
+
+ if (android::base::EndsWith(path, "/")) {
+ return IsUncachedPath(path);
+ } else {
+ // Append a slash at the end to make sure that the exact match is picked up.
+ return IsUncachedPath(path + "/");
+ }
+ }
+
std::recursive_mutex lock;
const string path;
// The Inode tracker associated with this FUSE instance.
@@ -294,6 +367,8 @@
node* const root;
struct fuse_session* se;
+ const bool uncached_mode;
+
/*
* Used to make JNI calls to MediaProvider.
* Responsibility of freeing this object falls on corresponding
@@ -312,8 +387,20 @@
std::atomic_bool* active;
std::atomic_bool disable_dentry_cache;
std::atomic_bool passthrough;
+ std::atomic_bool bpf;
+
+ const int bpf_fd;
+
// FUSE device id.
std::atomic_uint dev;
+ const std::vector<string> supported_transcoding_relative_paths;
+ const std::vector<string> supported_uncached_relative_paths;
+};
+
+struct OpenInfo {
+ int flags;
+ bool for_write;
+ bool direct_io;
};
enum class FuseOp { lookup, readdir, mknod, mkdir, create };
@@ -327,7 +414,7 @@
return "?";
}
-static inline __u64 ptr_to_id(void* ptr) {
+static inline __u64 ptr_to_id(const void* ptr) {
return (__u64)(uintptr_t) ptr;
}
@@ -388,11 +475,15 @@
return std::regex_match(path, PATTERN_OWNED_PATH);
}
+static bool is_bpf_backing_path(const string& path) {
+ return std::regex_match(path, PATTERN_BPF_BACKING_PATH);
+}
+
// See fuse_lowlevel.h fuse_lowlevel_notify_inval_entry for how to call this safetly without
// deadlocking the kernel
static void fuse_inval(fuse_session* se, fuse_ino_t parent_ino, fuse_ino_t child_ino,
const string& child_name, const string& path) {
- if (mediaprovider::fuse::containsMount(path, MY_USER_ID_STRING)) {
+ if (mediaprovider::fuse::containsMount(path)) {
LOG(WARNING) << "Ignoring attempt to invalidate dentry for FUSE mounts";
return;
}
@@ -404,12 +495,12 @@
}
}
-static double get_entry_timeout(const string& path, node* node, struct fuse* fuse) {
+static double get_entry_timeout(const string& path, bool should_inval, struct fuse* fuse) {
string media_path = fuse->GetEffectiveRootPath() + "/Android/media";
- if (fuse->disable_dentry_cache || node->ShouldInvalidate() ||
- is_package_owned_path(path, fuse->path) || android::base::StartsWith(path, media_path)) {
+ if (fuse->disable_dentry_cache || should_inval || is_package_owned_path(path, fuse->path) ||
+ android::base::StartsWithIgnoreCase(path, media_path) || fuse->ShouldNotCache(path)) {
// We set dentry timeout to 0 for the following reasons:
- // 1. The dentry cache was completely disabled
+ // 1. The dentry cache was completely disabled for the entire volume.
// 2.1 Case-insensitive lookups need to invalidate other case-insensitive dentry matches
// 2.2 Nodes supporting transforms need to be invalidated, so that subsequent lookups by a
// uid requiring a transform is guaranteed to come to the FUSE daemon.
@@ -419,6 +510,7 @@
// 4. Installd might delete Android/media/<package> dirs when app data is cleared.
// This can leave a stale entry in the kernel dcache, and break subsequent creation of the
// dir via FUSE.
+ // 5. The dentry cache was completely disabled for the given path.
return 0;
}
return std::numeric_limits<double>::max();
@@ -437,13 +529,6 @@
path, fuse->GetTransformsDir() + "/" + TRANSFORM_SYNTHETIC_DIR);
}
-static inline bool is_transcode_supported_path(const string& path, struct fuse* fuse) {
- // Keep in sync with MediaProvider#supportsTranscode
- return android::base::EndsWithIgnoreCase(path, ".mp4") &&
- android::base::StartsWithIgnoreCase(path,
- fuse->GetEffectiveRootPath() + "/dcim/camera/");
-}
-
static inline bool is_transforms_dir_path(const string& path, struct fuse* fuse) {
return android::base::StartsWithIgnoreCase(path, fuse->GetTransformsDir());
}
@@ -484,7 +569,7 @@
return std::make_unique<mediaprovider::fuse::FileLookupResult>(0, 0, 0, true, false, "");
}
- if (!synthetic_path && !is_transcode_supported_path(path, fuse)) {
+ if (!synthetic_path && !fuse->IsTranscodeSupportedPath(path)) {
// Transforms are only supported for synthetic or transcode-supported paths
return std::make_unique<mediaprovider::fuse::FileLookupResult>(0, 0, 0, true, false, "");
}
@@ -496,13 +581,13 @@
if (!file_lookup_result) {
// Fail lookup if we can't fetch FileLookupResult for path
LOG(WARNING) << "Failed to fetch FileLookupResult for " << path;
- *error_code = ENOENT;
+ *error_code = EFAULT;
return nullptr;
}
const string& io_path = file_lookup_result->io_path;
- // Update size with io_path size if io_path is not same as path
- if (!io_path.empty() && (io_path != path) && (lstat(io_path.c_str(), &e->attr) < 0)) {
+ // Update size with io_path iff there's an io_path
+ if (!io_path.empty() && (lstat(io_path.c_str(), &e->attr) < 0)) {
*error_code = errno;
return nullptr;
}
@@ -525,32 +610,34 @@
return nullptr;
}
- const bool should_invalidate = file_lookup_result->transforms_supported;
+ bool should_invalidate = file_lookup_result->transforms_supported;
const bool transforms_complete = file_lookup_result->transforms_complete;
const int transforms = file_lookup_result->transforms;
const int transforms_reason = file_lookup_result->transforms_reason;
const string& io_path = file_lookup_result->io_path;
+ if (transforms) {
+ // If the node requires transforms, we MUST never cache it in the VFS
+ CHECK(should_invalidate);
+ }
node = parent->LookupChildByName(name, true /* acquire */, transforms);
if (!node) {
ino_t ino = e->attr.st_ino;
- node = ::node::Create(parent, name, io_path, should_invalidate, transforms_complete,
- transforms, transforms_reason, &fuse->lock, ino, &fuse->tracker);
- } else if (!mediaprovider::fuse::containsMount(path, std::to_string(getuid() / PER_USER_RANGE))) {
- // Only invalidate a path if it does not contain mount.
+ node = ::node::Create(parent, name, io_path, transforms_complete, transforms,
+ transforms_reason, &fuse->lock, ino, &fuse->tracker);
+ } else if (!mediaprovider::fuse::containsMount(path)) {
+ // Only invalidate a path if it does not contain mount and |name| != node->GetName.
// Invalidate both names to ensure there's no dentry left in the kernel after the following
// operations:
// 1) touch foo, touch FOO, unlink *foo*
// 2) touch foo, touch FOO, unlink *FOO*
// Invalidating lookup_name fixes (1) and invalidating node_name fixes (2)
- // SetShouldInvalidate invalidates lookup_name by using 0 timeout below and we explicitly
- // invalidate node_name if different case
- // Note that we invalidate async otherwise we will deadlock the kernel
+ // -Set |should_invalidate| to true to invalidate lookup_name by using 0 timeout below
+ // -Explicitly invalidate node_name. Note that we invalidate async otherwise we will
+ // deadlock the kernel
if (name != node->GetName()) {
- // Record that we have made a case insensitive lookup, this allows us invalidate nodes
- // correctly on subsequent lookups for the case of |node|
- node->SetShouldInvalidate();
-
+ // Force node invalidation to fix the kernel dentry cache for case (1) above
+ should_invalidate = true;
// Make copies of the node name and path so we're not attempting to acquire
// any node locks from the invalidation thread. Depending on timing, we may end
// up invalidating the wrong inode but that shouldn't result in correctness issues.
@@ -559,6 +646,9 @@
const std::string& node_name = node->GetName();
std::thread t([=]() { fuse_inval(fuse->se, parent_ino, child_ino, node_name, path); });
t.detach();
+ // Update the name after |node_name| reference above has been captured in lambda
+ // This avoids invalidating the node again on subsequent accesses with |name|
+ node->SetName(name);
}
// This updated value allows us correctly decide if to keep_cache and use direct_io during
@@ -569,19 +659,41 @@
}
TRACE_NODE(node, req);
+ if (should_invalidate && fuse->IsTranscodeSupportedPath(path)) {
+ // Some components like the MTP stack need an efficient mechanism to determine if a file
+ // supports transcoding. This allows them workaround an issue with MTP clients on windows
+ // where those clients incorrectly use the original file size instead of the transcoded file
+ // size to copy files from the device. This size misuse causes transcoded files to be
+ // truncated to the original file size, hence corrupting the transcoded file.
+ //
+ // We expose the transcode bit via the st_nlink stat field. This should be safe because the
+ // field is not supported on FAT filesystems which FUSE is emulating.
+ // WARNING: Apps should never rely on this behavior as it is NOT supported API and will be
+ // removed in a future release when the MTP stack has better support for transcoded files on
+ // Windows OS.
+ e->attr.st_nlink = 2;
+ }
+
// This FS is not being exported via NFS so just a fixed generation number
// for now. If we do need this, we need to increment the generation ID each
// time the fuse daemon restarts because that's what it takes for us to
// reuse inode numbers.
e->generation = 0;
e->ino = fuse->ToInode(node);
- e->entry_timeout = get_entry_timeout(path, node, fuse);
- e->attr_timeout = std::numeric_limits<double>::max();
- return node;
-}
-static inline bool is_requesting_write(int flags) {
- return flags & (O_WRONLY | O_RDWR);
+ // When FUSE BPF is used, the caching of node attributes and lookups is
+ // disabled to avoid possible inconsistencies between the FUSE cache and
+ // the lower file system state.
+ // With FUSE BPF the file system requests are forwarded to the lower file
+ // system bypassing the FUSE daemon, so dropping the caching does not
+ // introduce a performance regression.
+ // Currently FUSE BPF is limited to the Android/data and Android/obb
+ // directories.
+ if (!fuse->bpf || !is_bpf_backing_path(path)) {
+ e->entry_timeout = get_entry_timeout(path, should_invalidate, fuse);
+ e->attr_timeout = std::numeric_limits<double>::max();
+ }
+ return node;
}
namespace mediaprovider {
@@ -649,12 +761,14 @@
}
// Return true if the path is accessible for that uid.
-static bool is_app_accessible_path(MediaProviderWrapper* mp, const string& path, uid_t uid) {
+static bool is_app_accessible_path(struct fuse* fuse, const string& path, uid_t uid) {
+ MediaProviderWrapper* mp = fuse->mp;
+
if (uid < AID_APP_START || uid == MY_UID) {
return true;
}
- if (path == "/storage/emulated") {
+ if (path == PRIMARY_VOLUME_PREFIX) {
// Apps should never refer to /storage/emulated - they should be using the user-spcific
// subdirs, eg /storage/emulated/0
return false;
@@ -668,7 +782,7 @@
if (pkg == ".nomedia") {
return true;
}
- if (android::base::StartsWith(path, "/storage/emulated")) {
+ if (!fuse->bpf && android::base::StartsWith(path, PRIMARY_VOLUME_PREFIX)) {
// Emulated storage bind-mounts app-private data directories, and so these
// should not be accessible through FUSE anyway.
LOG(WARNING) << "Rejected access to app-private dir on FUSE: " << path
@@ -683,9 +797,52 @@
return true;
}
+void fuse_bpf_fill_entries(const string& path, const int bpf_fd, struct fuse_entry_param* e,
+ int& backing_fd) {
+ /*
+ * The file descriptor `fd` must not be closed as it is closed
+ * automatically by the kernel as soon as it consumes the FUSE reply. This
+ * mechanism is necessary because userspace doesn't know when the kernel
+ * will consume the FUSE response containing `fd`, thus it may close the
+ * `fd` too soon, with the risk of assigning a backing file which is either
+ * invalid or corresponds to the wrong file in the lower file system.
+ */
+ backing_fd = open(path.c_str(), O_CLOEXEC | O_DIRECTORY | O_RDONLY);
+ if (backing_fd < 0) {
+ PLOG(ERROR) << "Failed to open: " << path;
+ return;
+ }
+
+ e->backing_action = FUSE_ACTION_REPLACE;
+ e->backing_fd = backing_fd;
+
+ if (bpf_fd >= 0) {
+ e->bpf_action = FUSE_ACTION_REPLACE;
+ e->bpf_fd = bpf_fd;
+ } else if (bpf_fd == static_cast<int>(BpfFd::REMOVE)) {
+ e->bpf_action = FUSE_ACTION_REMOVE;
+ } else {
+ e->bpf_action = FUSE_ACTION_KEEP;
+ }
+}
+
+void fuse_bpf_install(struct fuse* fuse, struct fuse_entry_param* e, const string& child_path,
+ int& backing_fd) {
+ // TODO(b/211873756) Enable only for the primary volume. Must be
+ // extended for other media devices.
+ if (android::base::StartsWith(child_path, PRIMARY_VOLUME_PREFIX)) {
+ if (is_bpf_backing_path(child_path)) {
+ fuse_bpf_fill_entries(child_path, fuse->bpf_fd, e, backing_fd);
+ } else if (is_package_owned_path(child_path, fuse->path)) {
+ fuse_bpf_fill_entries(child_path, static_cast<int>(BpfFd::REMOVE), e, backing_fd);
+ }
+ }
+}
+
static std::regex storage_emulated_regex("^\\/storage\\/emulated\\/([0-9]+)");
static node* do_lookup(fuse_req_t req, fuse_ino_t parent, const char* name,
- struct fuse_entry_param* e, int* error_code, const FuseOp op) {
+ struct fuse_entry_param* e, int* error_code, const FuseOp op,
+ int* backing_fd = NULL) {
struct fuse* fuse = get_fuse(req);
node* parent_node = fuse->FromInode(parent);
if (!parent_node) {
@@ -695,7 +852,7 @@
string parent_path = parent_node->BuildPath();
// We should always allow lookups on the root, because failing them could cause
// bind mounts to be invalidated.
- if (!fuse->IsRoot(parent_node) && !is_app_accessible_path(fuse->mp, parent_path, req->ctx.uid)) {
+ if (!fuse->IsRoot(parent_node) && !is_app_accessible_path(fuse, parent_path, req->ctx.uid)) {
*error_code = ENOENT;
return nullptr;
}
@@ -718,20 +875,27 @@
}
}
- return make_node_entry(req, parent_node, name, child_path, e, error_code, op);
+ auto node = make_node_entry(req, parent_node, name, child_path, e, error_code, op);
+
+ if (fuse->bpf && op == FuseOp::lookup) fuse_bpf_install(fuse, e, child_path, *backing_fd);
+
+ return node;
}
static void pf_lookup(fuse_req_t req, fuse_ino_t parent, const char* name) {
ATRACE_CALL();
struct fuse_entry_param e;
+ int backing_fd = -1;
int error_code = 0;
- if (do_lookup(req, parent, name, &e, &error_code, FuseOp::lookup)) {
+ if (do_lookup(req, parent, name, &e, &error_code, FuseOp::lookup, &backing_fd)) {
fuse_reply_entry(req, &e);
} else {
CHECK(error_code != 0);
fuse_reply_err(req, error_code);
}
+
+ if (backing_fd != -1) close(backing_fd);
}
static void do_forget(fuse_req_t req, struct fuse* fuse, fuse_ino_t ino, uint64_t nlookup) {
@@ -788,7 +952,7 @@
return;
}
const string& path = get_path(node);
- if (!is_app_accessible_path(fuse->mp, path, req->ctx.uid)) {
+ if (!is_app_accessible_path(fuse, path, req->ctx.uid)) {
fuse_reply_err(req, ENOENT);
return;
}
@@ -816,7 +980,7 @@
return;
}
const string& path = get_path(node);
- if (!is_app_accessible_path(fuse->mp, path, req->ctx.uid)) {
+ if (!is_app_accessible_path(fuse, path, req->ctx.uid)) {
fuse_reply_err(req, ENOENT);
return;
}
@@ -911,7 +1075,7 @@
node* node = fuse->FromInode(ino);
const string& path = node ? get_path(node) : "";
- if (node && is_app_accessible_path(fuse->mp, path, req->ctx.uid)) {
+ if (node && is_app_accessible_path(fuse, path, req->ctx.uid)) {
// TODO(b/147482155): Check that uid has access to |path| and its contents
fuse_reply_canonical_path(req, path.c_str());
return;
@@ -932,7 +1096,7 @@
return;
}
string parent_path = parent_node->BuildPath();
- if (!is_app_accessible_path(fuse->mp, parent_path, req->ctx.uid)) {
+ if (!is_app_accessible_path(fuse, parent_path, req->ctx.uid)) {
fuse_reply_err(req, ENOENT);
return;
}
@@ -970,7 +1134,7 @@
}
const struct fuse_ctx* ctx = fuse_req_ctx(req);
const string parent_path = parent_node->BuildPath();
- if (!is_app_accessible_path(fuse->mp, parent_path, ctx->uid)) {
+ if (!is_app_accessible_path(fuse, parent_path, ctx->uid)) {
fuse_reply_err(req, ENOENT);
return;
}
@@ -1011,7 +1175,7 @@
}
const struct fuse_ctx* ctx = fuse_req_ctx(req);
const string parent_path = parent_node->BuildPath();
- if (!is_app_accessible_path(fuse->mp, parent_path, ctx->uid)) {
+ if (!is_app_accessible_path(fuse, parent_path, ctx->uid)) {
fuse_reply_err(req, ENOENT);
return;
}
@@ -1040,7 +1204,7 @@
return;
}
const string parent_path = parent_node->BuildPath();
- if (!is_app_accessible_path(fuse->mp, parent_path, req->ctx.uid)) {
+ if (!is_app_accessible_path(fuse, parent_path, req->ctx.uid)) {
fuse_reply_err(req, ENOENT);
return;
}
@@ -1095,7 +1259,7 @@
if (!old_parent_node) return ENOENT;
const struct fuse_ctx* ctx = fuse_req_ctx(req);
const string old_parent_path = old_parent_node->BuildPath();
- if (!is_app_accessible_path(fuse->mp, old_parent_path, ctx->uid)) {
+ if (!is_app_accessible_path(fuse, old_parent_path, ctx->uid)) {
return ENOENT;
}
@@ -1105,10 +1269,16 @@
return ENOENT;
}
- node* new_parent_node = fuse->FromInode(new_parent);
- if (!new_parent_node) return ENOENT;
+ node* new_parent_node;
+ if (fuse->bpf) {
+ new_parent_node = fuse->FromInodeNoThrow(new_parent);
+ if (!new_parent_node) return EXDEV;
+ } else {
+ new_parent_node = fuse->FromInode(new_parent);
+ if (!new_parent_node) return ENOENT;
+ }
const string new_parent_path = new_parent_node->BuildPath();
- if (!is_app_accessible_path(fuse->mp, new_parent_path, ctx->uid)) {
+ if (!is_app_accessible_path(fuse, new_parent_path, ctx->uid)) {
return ENOENT;
}
@@ -1135,6 +1305,11 @@
// TODO(b/145663158): Lookups can go out of sync if file/directory is actually moved but
// EFAULT/EIO is reported due to JNI exception.
if (res == 0) {
+ // Mark any existing destination nodes as deleted. This fixes the following edge case:
+ // 1. New destination node is forgotten
+ // 2. Old destination node is not forgotten because there's still an open fd ref to it
+ // 3. Lookup for |new_name| returns old destination node with stale metadata
+ new_parent_node->SetDeletedForChild(new_name);
// TODO(b/169306422): Log each renamed node
old_parent_node->RenameChild(name, new_name, new_parent_node);
}
@@ -1157,6 +1332,7 @@
static handle* create_handle_for_node(struct fuse* fuse, const string& path, int fd, uid_t uid,
uid_t transforms_uid, node* node, const RedactionInfo* ri,
+ const bool allow_passthrough, const bool open_info_direct_io,
int* keep_cache) {
std::lock_guard<std::recursive_mutex> guard(fuse->lock);
@@ -1168,7 +1344,7 @@
CHECK(transforms);
}
- if (fuse->passthrough) {
+ if (fuse->passthrough && allow_passthrough) {
*keep_cache = transforms_complete;
// We only enabled passthrough iff these 2 conditions hold
// 1. Redaction is not needed
@@ -1178,7 +1354,7 @@
// arbitrary bytes the first time around. However, if we ensure that transforms are
// completed, then it's safe to use passthrough. Additionally, transcoded nodes never
// require redaction so (2) implies (1)
- handle = new struct handle(fd, ri, true /* cached */,
+ handle = new struct handle(fd, ri, !open_info_direct_io /* cached */,
!redaction_needed && transforms_complete /* passthrough */, uid,
transforms_uid);
} else {
@@ -1199,7 +1375,8 @@
bool is_redaction_change =
(redaction_needed && !has_redacted) || (!redaction_needed && has_redacted);
bool is_cached_file_open = node->HasCachedHandle();
- bool direct_io = (is_cached_file_open && is_redaction_change) || is_file_locked(fd, path);
+ bool direct_io = open_info_direct_io || (is_cached_file_open && is_redaction_change) ||
+ is_file_locked(fd, path) || fuse->ShouldNotCache(path);
if (!is_cached_file_open && is_redaction_change) {
node->SetRedactedCache(redaction_needed);
@@ -1216,7 +1393,7 @@
return handle;
}
-bool do_passthrough_enable(fuse_req_t req, struct fuse_file_info* fi, unsigned int fd) {
+static bool do_passthrough_enable(fuse_req_t req, struct fuse_file_info* fi, unsigned int fd) {
int passthrough_fh = fuse_passthrough_enable(req, fd);
if (passthrough_fh <= 0) {
@@ -1227,6 +1404,45 @@
return true;
}
+static OpenInfo parse_open_flags(const string& path, const int in_flags) {
+ const bool for_write = in_flags & (O_WRONLY | O_RDWR);
+ int out_flags = in_flags;
+ bool direct_io = false;
+
+ if (in_flags & O_DIRECT) {
+ // Set direct IO on the FUSE fs file
+ direct_io = true;
+
+ if (android::base::StartsWith(path, PRIMARY_VOLUME_PREFIX)) {
+ // Remove O_DIRECT because there are strict alignment requirements for direct IO and
+ // there were some historical bugs affecting encrypted block devices.
+ // Hence, this is only supported on public volumes.
+ out_flags &= ~O_DIRECT;
+ }
+ }
+ if (in_flags & O_WRONLY) {
+ // Replace O_WRONLY with O_RDWR because even if the FUSE fd is opened write-only, the FUSE
+ // driver might issue reads on the lower fs ith the writeback cache enabled
+ out_flags &= ~O_WRONLY;
+ out_flags |= O_RDWR;
+ }
+ if (in_flags & O_APPEND) {
+ // Remove O_APPEND because passing it to the lower fs can lead to file corruption when
+ // multiple FUSE threads race themselves reading. With writeback cache enabled, the FUSE
+ // driver already handles the O_APPEND
+ out_flags &= ~O_APPEND;
+ }
+
+ return {.flags = out_flags, .for_write = for_write, .direct_io = direct_io};
+}
+
+static void fill_fuse_file_info(const handle* handle, const OpenInfo* open_info,
+ const int keep_cache, struct fuse_file_info* fi) {
+ fi->fh = ptr_to_id(handle);
+ fi->keep_cache = keep_cache;
+ fi->direct_io = !handle->cached;
+}
+
static void pf_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) {
ATRACE_CALL();
struct fuse* fuse = get_fuse(req);
@@ -1238,30 +1454,26 @@
const struct fuse_ctx* ctx = fuse_req_ctx(req);
const string& io_path = get_path(node);
const string& build_path = node->BuildPath();
- if (!is_app_accessible_path(fuse->mp, io_path, ctx->uid)) {
+ if (!is_app_accessible_path(fuse, io_path, ctx->uid)) {
fuse_reply_err(req, ENOENT);
return;
}
- bool for_write = is_requesting_write(fi->flags);
+ const OpenInfo open_info = parse_open_flags(io_path, fi->flags);
- if (for_write && node->GetTransforms()) {
+ if (open_info.for_write && node->GetTransforms()) {
TRACE_NODE(node, req) << "write with transforms";
} else {
- TRACE_NODE(node, req) << (for_write ? "write" : "read");
- }
-
- if (fi->flags & O_DIRECT) {
- fi->flags &= ~O_DIRECT;
- fi->direct_io = true;
+ TRACE_NODE(node, req) << (open_info.for_write ? "write" : "read");
}
// Force permission check with the build path because the MediaProvider database might not be
// aware of the io_path
// We don't redact if the caller was granted write permission for this file
std::unique_ptr<FileOpenResult> result = fuse->mp->OnFileOpen(
- build_path, io_path, ctx->uid, ctx->pid, node->GetTransformsReason(), for_write,
- !for_write /* redact */, true /* log_transforms_metrics */);
+ build_path, io_path, ctx->uid, ctx->pid, node->GetTransformsReason(),
+ open_info.for_write, !open_info.for_write /* redact */,
+ true /* log_transforms_metrics */);
if (!result) {
fuse_reply_err(req, EFAULT);
return;
@@ -1272,47 +1484,43 @@
return;
}
- // With the writeback cache enabled, FUSE may generate READ requests even for files that
- // were opened O_WRONLY; so make sure we open it O_RDWR instead.
- int open_flags = fi->flags;
- if (open_flags & O_WRONLY) {
- open_flags &= ~O_WRONLY;
- open_flags |= O_RDWR;
- }
-
- if (open_flags & O_APPEND) {
- open_flags &= ~O_APPEND;
- }
-
- const int fd = open(io_path.c_str(), open_flags);
- if (fd < 0) {
- fuse_reply_err(req, errno);
- return;
+ int fd = -1;
+ const bool is_fd_from_java = result->fd >= 0;
+ if (is_fd_from_java) {
+ fd = result->fd;
+ TRACE_NODE(node, req) << "opened in Java";
+ } else {
+ fd = open(io_path.c_str(), open_info.flags);
+ if (fd < 0) {
+ fuse_reply_err(req, errno);
+ return;
+ }
}
int keep_cache = 1;
- handle* h = create_handle_for_node(fuse, io_path, fd, result->uid, result->transforms_uid, node,
- result->redaction_info.release(), &keep_cache);
- fi->fh = ptr_to_id(h);
- fi->keep_cache = keep_cache;
- fi->direct_io = !h->cached;
+ // If is_fd_from_java==true, we disallow passthrough because the fd can be pointing to the
+ // FUSE fs if gotten from another process
+ const handle* h = create_handle_for_node(fuse, io_path, fd, result->uid, result->transforms_uid,
+ node, result->redaction_info.release(),
+ /* allow_passthrough */ !is_fd_from_java,
+ open_info.direct_io, &keep_cache);
+ fill_fuse_file_info(h, &open_info, keep_cache, fi);
// TODO(b/173190192) ensuring that h->cached must be enabled in order to
// user FUSE passthrough is a conservative rule and might be dropped as
// soon as demonstrated its correctness.
- if (h->passthrough) {
- if (!do_passthrough_enable(req, fi, fd)) {
- // TODO: Should we crash here so we can find errors easily?
- PLOG(ERROR) << "Passthrough OPEN failed for " << io_path;
- fuse_reply_err(req, EFAULT);
- return;
- }
+ if (h->passthrough && !do_passthrough_enable(req, fi, fd)) {
+ // TODO: Should we crash here so we can find errors easily?
+ PLOG(ERROR) << "Passthrough OPEN failed for " << io_path;
+ fuse_reply_err(req, EFAULT);
+ return;
}
fuse_reply_open(req, fi);
}
-static void do_read(fuse_req_t req, size_t size, off_t off, struct fuse_file_info* fi) {
+static void do_read(fuse_req_t req, size_t size, off_t off, struct fuse_file_info* fi,
+ bool direct_io) {
handle* h = reinterpret_cast<handle*>(fi->fh);
struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size);
@@ -1320,8 +1528,13 @@
buf.buf[0].pos = off;
buf.buf[0].flags =
(enum fuse_buf_flags) (FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK);
-
- fuse_reply_data(req, &buf, (enum fuse_buf_copy_flags) 0);
+ if (direct_io) {
+ // sdcardfs does not register splice_read_file_operations and some requests fail with EFAULT
+ // Specifically, FUSE splice is only enabled for 8KB+ buffers, hence such reads fail
+ fuse_reply_data(req, &buf, (enum fuse_buf_copy_flags)FUSE_BUF_NO_SPLICE);
+ } else {
+ fuse_reply_data(req, &buf, (enum fuse_buf_copy_flags)0);
+ }
}
/**
@@ -1348,7 +1561,8 @@
buf->mem = nullptr;
}
-static void do_read_with_redaction(fuse_req_t req, size_t size, off_t off, fuse_file_info* fi) {
+static void do_read_with_redaction(fuse_req_t req, size_t size, off_t off, fuse_file_info* fi,
+ bool direct_io) {
handle* h = reinterpret_cast<handle*>(fi->fh);
std::vector<ReadRange> ranges;
@@ -1356,7 +1570,7 @@
// As an optimization, return early if there are no ranges to redact.
if (ranges.size() == 0) {
- do_read(req, size, off, fi);
+ do_read(req, size, off, fi, direct_io);
return;
}
@@ -1388,6 +1602,7 @@
struct fuse_file_info* fi) {
ATRACE_CALL();
handle* h = reinterpret_cast<handle*>(fi->fh);
+ const bool direct_io = !h->cached;
struct fuse* fuse = get_fuse(req);
node* node = fuse->FromInode(ino);
@@ -1405,9 +1620,9 @@
fuse->fadviser.Record(h->fd, size);
if (h->ri->isRedactionNeeded()) {
- do_read_with_redaction(req, size, off, fi);
+ do_read_with_redaction(req, size, off, fi, direct_io);
} else {
- do_read(req, size, off, fi);
+ do_read(req, size, off, fi, direct_io);
}
}
@@ -1476,6 +1691,25 @@
}
#endif
+/*
+ * This function does nothing except being a placeholder to keep the FUSE
+ * driver handling flushes on close(2).
+ * In fact, kernels prior to 5.8 stop attempting flushing the cache on close(2)
+ * if the .flush operation is not implemented by the FUSE daemon.
+ * This has been fixed in the kernel by commit 614c026e8a46 ("fuse: always
+ * flush dirty data on close(2)"), merged in Linux 5.8, but until then
+ * userspace must mitigate this behavior by not leaving the .flush function
+ * pointer empty.
+ */
+static void pf_flush(fuse_req_t req,
+ fuse_ino_t ino,
+ struct fuse_file_info* fi) {
+ ATRACE_CALL();
+ struct fuse* fuse = get_fuse(req);
+ TRACE_NODE(nullptr, req) << "noop";
+ fuse_reply_err(req, 0);
+}
+
static void pf_release(fuse_req_t req,
fuse_ino_t ino,
struct fuse_file_info* fi) {
@@ -1534,7 +1768,7 @@
}
const struct fuse_ctx* ctx = fuse_req_ctx(req);
const string path = node->BuildPath();
- if (!is_app_accessible_path(fuse->mp, path, ctx->uid)) {
+ if (!is_app_accessible_path(fuse, path, ctx->uid)) {
fuse_reply_err(req, ENOENT);
return;
}
@@ -1584,7 +1818,7 @@
return;
}
const string path = node->BuildPath();
- if (!is_app_accessible_path(fuse->mp, path, req->ctx.uid)) {
+ if (!is_app_accessible_path(fuse, path, req->ctx.uid)) {
fuse_reply_err(req, ENOENT);
return;
}
@@ -1732,7 +1966,7 @@
return;
}
const string path = node->BuildPath();
- if (path != "/storage/emulated" && !is_app_accessible_path(fuse->mp, path, req->ctx.uid)) {
+ if (path != PRIMARY_VOLUME_PREFIX && !is_app_accessible_path(fuse, path, req->ctx.uid)) {
fuse_reply_err(req, ENOENT);
return;
}
@@ -1756,7 +1990,7 @@
bool for_write = mask & W_OK;
bool is_directory = S_ISDIR(stat.st_mode);
if (is_directory) {
- if (path == "/storage/emulated" && mask == X_OK) {
+ if (path == PRIMARY_VOLUME_PREFIX && mask == X_OK) {
// Special case for this path: apps should be allowed to enter it,
// but not list directory contents (which would be user numbers).
int res = access(path.c_str(), X_OK);
@@ -1797,7 +2031,7 @@
return;
}
const string parent_path = parent_node->BuildPath();
- if (!is_app_accessible_path(fuse->mp, parent_path, req->ctx.uid)) {
+ if (!is_app_accessible_path(fuse, parent_path, req->ctx.uid)) {
fuse_reply_err(req, ENOENT);
return;
}
@@ -1806,26 +2040,16 @@
const string child_path = parent_path + "/" + name;
+ const OpenInfo open_info = parse_open_flags(child_path, fi->flags);
+
int mp_return_code = fuse->mp->InsertFile(child_path.c_str(), req->ctx.uid);
if (mp_return_code) {
fuse_reply_err(req, mp_return_code);
return;
}
- // With the writeback cache enabled, FUSE may generate READ requests even for files that
- // were opened O_WRONLY; so make sure we open it O_RDWR instead.
- int open_flags = fi->flags;
- if (open_flags & O_WRONLY) {
- open_flags &= ~O_WRONLY;
- open_flags |= O_RDWR;
- }
-
- if (open_flags & O_APPEND) {
- open_flags &= ~O_APPEND;
- }
-
mode = (mode & (~0777)) | 0664;
- int fd = open(child_path.c_str(), open_flags, mode);
+ int fd = open(child_path.c_str(), open_info.flags, mode);
if (fd < 0) {
int error_code = errno;
// We've already inserted the file into the MP database before the
@@ -1854,21 +2078,18 @@
// to the file before all the EXIF content is written. We could special case reads before the
// first close after a file has just been created.
int keep_cache = 1;
- handle* h = create_handle_for_node(fuse, child_path, fd, req->ctx.uid, 0 /* transforms_uid */,
- node, new RedactionInfo(), &keep_cache);
- fi->fh = ptr_to_id(h);
- fi->keep_cache = keep_cache;
- fi->direct_io = !h->cached;
+ const handle* h = create_handle_for_node(
+ fuse, child_path, fd, req->ctx.uid, 0 /* transforms_uid */, node, new RedactionInfo(),
+ /* allow_passthrough */ true, open_info.direct_io, &keep_cache);
+ fill_fuse_file_info(h, &open_info, keep_cache, fi);
// TODO(b/173190192) ensuring that h->cached must be enabled in order to
// user FUSE passthrough is a conservative rule and might be dropped as
// soon as demonstrated its correctness.
- if (h->passthrough) {
- if (!do_passthrough_enable(req, fi, fd)) {
- PLOG(ERROR) << "Passthrough CREATE failed for " << child_path;
- fuse_reply_err(req, EFAULT);
- return;
- }
+ if (h->passthrough && !do_passthrough_enable(req, fi, fd)) {
+ PLOG(ERROR) << "Passthrough CREATE failed for " << child_path;
+ fuse_reply_err(req, EFAULT);
+ return;
}
fuse_reply_create(req, &e, fi);
@@ -1934,7 +2155,7 @@
/*.link = pf_link,*/
.open = pf_open, .read = pf_read,
/*.write = pf_write,*/
- /*.flush = pf_flush,*/
+ .flush = pf_flush,
.release = pf_release, .fsync = pf_fsync, .opendir = pf_opendir, .readdir = pf_readdir,
.releasedir = pf_releasedir, .fsyncdir = pf_fsyncdir, .statfs = pf_statfs,
/*.setxattr = pf_setxattr,
@@ -2004,6 +2225,10 @@
return use_fuse;
}
+bool FuseDaemon::UsesFusePassthrough() const {
+ return fuse->passthrough;
+}
+
void FuseDaemon::InvalidateFuseDentryCache(const std::string& path) {
LOG(VERBOSE) << "Invalidating FUSE dentry cache";
if (active.load(std::memory_order_acquire)) {
@@ -2035,7 +2260,20 @@
return active.load(std::memory_order_acquire);
}
-void FuseDaemon::Start(android::base::unique_fd fd, const std::string& path) {
+bool IsFuseBpfEnabled() {
+ std::string bpf_override = android::base::GetProperty("persist.sys.fuse.bpf.override", "");
+ if (bpf_override == "true") {
+ return true;
+ } else if (bpf_override == "false") {
+ return false;
+ }
+ return android::base::GetBoolProperty("ro.fuse.bpf.enabled", false);
+}
+
+void FuseDaemon::Start(android::base::unique_fd fd, const std::string& path,
+ const bool uncached_mode,
+ const std::vector<std::string>& supported_transcoding_relative_paths,
+ const std::vector<std::string>& supported_uncached_relative_paths) {
android::base::SetDefaultTag(LOG_TAG);
struct fuse_args args;
@@ -2060,7 +2298,23 @@
return;
}
- struct fuse fuse_default(path, stat.st_ino);
+ bool bpf_enabled = IsFuseBpfEnabled();
+ int bpf_fd = -1;
+ if (bpf_enabled) {
+ LOG(INFO) << "Using FUSE BPF";
+
+ bpf_fd = android::bpf::bpfFdGet(FUSE_BPF_PROG_PATH, BPF_F_RDONLY);
+ if (bpf_fd < 0) {
+ PLOG(ERROR) << "Failed to fetch BPF prog fd: " << bpf_fd;
+ bpf_enabled = false;
+ } else {
+ LOG(INFO) << "BPF prog fd fetched";
+ }
+ }
+
+ struct fuse fuse_default(path, stat.st_ino, uncached_mode, bpf_enabled, bpf_fd,
+ supported_transcoding_relative_paths,
+ supported_uncached_relative_paths);
fuse_default.mp = ∓
// fuse_default is stack allocated, but it's safe to save it as an instance variable because
// this method blocks and FuseDaemon#active tells if we are currently blocking
@@ -2119,12 +2373,12 @@
return;
}
-const string FuseDaemon::GetOriginalMediaFormatFilePath(int fd) const {
+std::unique_ptr<FdAccessResult> FuseDaemon::CheckFdAccess(int fd, uid_t uid) const {
struct stat s;
memset(&s, 0, sizeof(s));
if (fstat(fd, &s) < 0) {
- PLOG(DEBUG) << "GetOriginalMediaFormatFilePath fstat failed.";
- return string();
+ PLOG(DEBUG) << "CheckFdAccess fstat failed.";
+ return std::make_unique<FdAccessResult>(string(), false);
}
ino_t ino = s.st_ino;
@@ -2132,17 +2386,17 @@
dev_t fuse_dev = fuse->dev.load(std::memory_order_acquire);
if (dev != fuse_dev) {
- PLOG(DEBUG) << "GetOriginalMediaFormatFilePath FUSE device id does not match.";
- return string();
+ PLOG(DEBUG) << "CheckFdAccess FUSE device id does not match.";
+ return std::make_unique<FdAccessResult>(string(), false);
}
const node* node = node::LookupInode(fuse->root, ino);
if (!node) {
- PLOG(DEBUG) << "GetOriginalMediaFormatFilePath no node found with given ino";
- return string();
+ PLOG(DEBUG) << "CheckFdAccess no node found with given ino";
+ return std::make_unique<FdAccessResult>(string(), false);
}
- return node->BuildPath();
+ return node->CheckHandleForUid(uid);
}
void FuseDaemon::InitializeDeviceId(const std::string& path) {
diff --git a/jni/FuseDaemon.h b/jni/FuseDaemon.h
index f7c5614..9db8583 100644
--- a/jni/FuseDaemon.h
+++ b/jni/FuseDaemon.h
@@ -17,13 +17,14 @@
#ifndef MEDIAPROVIDER_JNI_FUSEDAEMON_H_
#define MEDIAPROVIDER_JNI_FUSEDAEMON_H_
+#include <android-base/unique_fd.h>
+
#include <memory>
#include <string>
-#include <android-base/unique_fd.h>
-
#include "MediaProviderWrapper.h"
#include "jni.h"
+#include "node-inl.h"
struct fuse;
namespace mediaprovider {
@@ -37,7 +38,9 @@
/**
* Start the FUSE daemon loop that will handle filesystem calls.
*/
- void Start(android::base::unique_fd fd, const std::string& path);
+ void Start(android::base::unique_fd fd, const std::string& path, const bool uncached_mode,
+ const std::vector<std::string>& supported_transcoding_relative_paths,
+ const std::vector<std::string>& supported_uncached_relative_paths);
/**
* Checks if the FUSE daemon is started.
@@ -50,14 +53,19 @@
bool ShouldOpenWithFuse(int fd, bool for_read, const std::string& path);
/**
+ * Check if the FUSE daemon uses FUSE passthrough
+ */
+ bool UsesFusePassthrough() const;
+
+ /**
* Invalidate FUSE VFS dentry cache entry for path
*/
void InvalidateFuseDentryCache(const std::string& path);
/**
- * Return path of the original media format file for the given file descriptor.
+ * Checks if the given uid has access to the given fd with or without redaction.
*/
- const std::string GetOriginalMediaFormatFilePath(int fd) const;
+ std::unique_ptr<FdAccessResult> CheckFdAccess(int fd, uid_t uid) const;
/**
* Initialize device id for the FUSE daemon with the FUSE device id of the given path.
diff --git a/jni/FuseUtils.cpp b/jni/FuseUtils.cpp
index 7829888..7b08164 100644
--- a/jni/FuseUtils.cpp
+++ b/jni/FuseUtils.cpp
@@ -26,7 +26,7 @@
namespace mediaprovider {
namespace fuse {
-bool containsMount(const string& path, const string& userid) {
+bool containsMount(const string& path) {
// This method is called from lookup, so it's called rather frequently.
// Hence, we avoid concatenating the strings and we use 3 separate suffixes.
@@ -35,16 +35,17 @@
return false;
}
- const string& rest_of_path = path.substr(prefix.length());
- if (!android::base::StartsWithIgnoreCase(rest_of_path, userid)) {
+ size_t pos = path.find_first_of('/', prefix.length());
+ if (pos == std::string::npos) {
return false;
}
+ const string& path_suffix = path.substr(pos);
+
static const string android_suffix = "/Android";
static const string data_suffix = "/Android/data";
static const string obb_suffix = "/Android/obb";
- const string& path_suffix = rest_of_path.substr(userid.length());
return android::base::EqualsIgnoreCase(path_suffix, android_suffix) ||
android::base::EqualsIgnoreCase(path_suffix, data_suffix) ||
android::base::EqualsIgnoreCase(path_suffix, obb_suffix);
diff --git a/jni/FuseUtilsTest.cpp b/jni/FuseUtilsTest.cpp
index d9d28e6..d76a89c 100644
--- a/jni/FuseUtilsTest.cpp
+++ b/jni/FuseUtilsTest.cpp
@@ -20,47 +20,43 @@
#include <gtest/gtest.h>
-using namespace mediaprovider::fuse;
+namespace mediaprovider::fuse {
TEST(FuseUtilsTest, testContainsMount_isTrueForAndroidDataObb) {
- EXPECT_TRUE(containsMount("/storage/emulated/1234/Android", "1234"));
- EXPECT_TRUE(containsMount("/storage/emulated/1234/Android/data", "1234"));
- EXPECT_TRUE(containsMount("/storage/emulated/1234/Android/obb", "1234"));
+ EXPECT_TRUE(containsMount("/storage/emulated/1234/Android"));
+ EXPECT_TRUE(containsMount("/storage/emulated/5678/Android"));
+ EXPECT_TRUE(containsMount("/storage/emulated/1234/Android/data"));
+ EXPECT_TRUE(containsMount("/storage/emulated/5678/Android/obb"));
+ EXPECT_TRUE(containsMount("/storage/emulated/1234/Android/obb"));
+ EXPECT_TRUE(containsMount("/storage/emulated/5678/Android/obb"));
}
TEST(FuseUtilsTest, testContainsMount) {
- EXPECT_FALSE(containsMount("/random/path", "1234"));
- EXPECT_FALSE(containsMount("/storage/abc-123", "1234"));
- EXPECT_FALSE(containsMount("/storage/emulated/1234/Android/data/and/more", "1234"));
+ EXPECT_FALSE(containsMount("/random/path"));
+ EXPECT_FALSE(containsMount("/storage/abc-123"));
+ EXPECT_FALSE(containsMount("/storage/emulated/1234/Android/data/and/more"));
+ EXPECT_FALSE(containsMount("/storage/emulated"));
+ EXPECT_FALSE(containsMount("/storage/emulated/"));
+ EXPECT_FALSE(containsMount("/storage/emulated//"));
+ EXPECT_FALSE(containsMount("/storage/emulated/0/"));
}
TEST(FuseUtilsTest, testContainsMount_isCaseInsensitive) {
- EXPECT_TRUE(containsMount("/storage/emulated/1234/android", "1234"));
- EXPECT_TRUE(containsMount("/storage/emulated/1234/Android/Data", "1234"));
- EXPECT_TRUE(containsMount("/storage/emulated/1234/ANDroid/dATa", "1234"));
- EXPECT_TRUE(containsMount("/storage/emulated/1234/ANDROID/OBB", "1234"));
- EXPECT_TRUE(containsMount("/Storage/EMULATED/1234/Android/obb", "1234"));
-}
-
-TEST(FuseUtilsTest, testContainsMount_isCaseInsensitiveForUserid) {
- EXPECT_TRUE(containsMount("/storage/emulated/UserId/Android", "UserId"));
- EXPECT_TRUE(containsMount("/storage/emulated/userid/Android/obb", "Userid"));
- EXPECT_TRUE(containsMount("/storage/emulated/Userid/Android/obb", "userid"));
+ EXPECT_TRUE(containsMount("/storage/emulated/1234/android"));
+ EXPECT_TRUE(containsMount("/storage/emulated/1234/Android/Data"));
+ EXPECT_TRUE(containsMount("/storage/emulated/1234/ANDroid/dATa"));
+ EXPECT_TRUE(containsMount("/storage/emulated/1234/ANDROID/OBB"));
+ EXPECT_TRUE(containsMount("/Storage/EMULATED/1234/Android/obb"));
}
TEST(FuseUtilsTest, testContainsMount_isFalseForPathWithAdditionalSlash) {
- EXPECT_FALSE(containsMount("/storage/emulated/1234/Android/", "1234"));
- EXPECT_FALSE(containsMount("/storage/emulated/1234/Android/data/", "1234"));
- EXPECT_FALSE(containsMount("/storage/emulated/1234/Android/obb/", "1234"));
+ EXPECT_FALSE(containsMount("/storage/emulated/1234/Android/"));
+ EXPECT_FALSE(containsMount("/storage/emulated/1234/Android/data/"));
+ EXPECT_FALSE(containsMount("/storage/emulated/1234/Android/obb/"));
- EXPECT_FALSE(containsMount("//storage/emulated/1234/Android", "1234"));
- EXPECT_FALSE(containsMount("/storage/emulated//1234/Android/data", "1234"));
- EXPECT_FALSE(containsMount("/storage/emulated/1234//Android/data", "1234"));
+ EXPECT_FALSE(containsMount("//storage/emulated/1234/Android"));
+ EXPECT_FALSE(containsMount("/storage/emulated//1234/Android/data"));
+ EXPECT_FALSE(containsMount("/storage/emulated/1234//Android/data"));
}
-TEST(FuseUtilsTest, testContainsMount_isFalseForPathWithWrongUserid) {
- EXPECT_FALSE(containsMount("/storage/emulated/11234/Android", "1234"));
- EXPECT_FALSE(containsMount("/storage/emulated/0/Android/data", "1234"));
- EXPECT_FALSE(containsMount("/storage/emulated/12345/Android/obb", "1234"));
- EXPECT_FALSE(containsMount("/storage/emulated/1234/Android/obb", "5678"));
-}
+} // namespace mediaprovider::fuse
diff --git a/jni/MediaProviderWrapper.cpp b/jni/MediaProviderWrapper.cpp
index 9f8a759..253dbbe 100644
--- a/jni/MediaProviderWrapper.cpp
+++ b/jni/MediaProviderWrapper.cpp
@@ -31,7 +31,6 @@
namespace mediaprovider {
namespace fuse {
-using android::base::GetBoolProperty;
using std::string;
namespace {
@@ -41,6 +40,14 @@
constexpr uid_t ROOT_UID = 0;
constexpr uid_t SHELL_UID = 2000;
+// These need to stay in sync with MediaProvider.java's DIRECTORY_ACCESS_FOR_* constants.
+enum DirectoryAccessRequestType {
+ kReadDirectoryRequest = 1,
+ kWriteDirectoryRequest = 2,
+ kCreateDirectoryRequest = 3,
+ kDeleteDirectoryRequest = 4,
+};
+
/** Private helper functions **/
inline bool shouldBypassMediaProvider(uid_t uid) {
@@ -91,25 +98,12 @@
return res;
}
-int isMkdirOrRmdirAllowedInternal(JNIEnv* env, jobject media_provider_object,
- jmethodID mid_is_mkdir_or_rmdir_allowed, const string& path,
- uid_t uid, bool forCreate) {
+int isDirAccessAllowedInternal(JNIEnv* env, jobject media_provider_object,
+ jmethodID mid_is_diraccess_allowed, const string& path, uid_t uid,
+ int accessType) {
ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
- int res = env->CallIntMethod(media_provider_object, mid_is_mkdir_or_rmdir_allowed, j_path.get(),
- uid, forCreate);
-
- if (CheckForJniException(env)) {
- return EFAULT;
- }
- return res;
-}
-
-int isOpendirAllowedInternal(JNIEnv* env, jobject media_provider_object,
- jmethodID mid_is_opendir_allowed, const string& path, uid_t uid,
- bool forWrite) {
- ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
- int res = env->CallIntMethod(media_provider_object, mid_is_opendir_allowed, j_path.get(), uid,
- forWrite);
+ int res = env->CallIntMethod(media_provider_object, mid_is_diraccess_allowed, j_path.get(), uid,
+ accessType);
if (CheckForJniException(env)) {
return EFAULT;
@@ -228,37 +222,24 @@
media_provider_class_ = reinterpret_cast<jclass>(env->NewGlobalRef(media_provider_class_));
// Cache methods - Before calling a method, make sure you cache it here
- mid_insert_file_ = CacheMethod(env, "insertFileIfNecessary", "(Ljava/lang/String;I)I",
- /*is_static*/ false);
- mid_delete_file_ = CacheMethod(env, "deleteFile", "(Ljava/lang/String;I)I", /*is_static*/ false);
+ mid_insert_file_ = CacheMethod(env, "insertFileIfNecessary", "(Ljava/lang/String;I)I");
+ mid_delete_file_ = CacheMethod(env, "deleteFile", "(Ljava/lang/String;I)I");
mid_on_file_open_ = CacheMethod(env, "onFileOpen",
"(Ljava/lang/String;Ljava/lang/String;IIIZZZ)Lcom/android/"
- "providers/media/FileOpenResult;",
- /*is_static*/ false);
- mid_is_mkdir_or_rmdir_allowed_ = CacheMethod(env, "isDirectoryCreationOrDeletionAllowed",
- "(Ljava/lang/String;IZ)I", /*is_static*/ false);
- mid_is_opendir_allowed_ = CacheMethod(env, "isOpendirAllowed", "(Ljava/lang/String;IZ)I",
- /*is_static*/ false);
+ "providers/media/FileOpenResult;");
+ mid_is_diraccess_allowed_ = CacheMethod(env, "isDirAccessAllowed", "(Ljava/lang/String;II)I");
mid_get_files_in_dir_ =
- CacheMethod(env, "getFilesInDirectory", "(Ljava/lang/String;I)[Ljava/lang/String;",
- /*is_static*/ false);
- mid_rename_ = CacheMethod(env, "rename", "(Ljava/lang/String;Ljava/lang/String;I)I",
- /*is_static*/ false);
+ CacheMethod(env, "getFilesInDirectory", "(Ljava/lang/String;I)[Ljava/lang/String;");
+ mid_rename_ = CacheMethod(env, "rename", "(Ljava/lang/String;Ljava/lang/String;I)I");
mid_is_uid_allowed_access_to_data_or_obb_path_ =
- CacheMethod(env, "isUidAllowedAccessToDataOrObbPath", "(ILjava/lang/String;)Z",
- /*is_static*/ false);
- mid_on_file_created_ = CacheMethod(env, "onFileCreated", "(Ljava/lang/String;)V",
- /*is_static*/ false);
- mid_should_allow_lookup_ = CacheMethod(env, "shouldAllowLookup", "(II)Z",
- /*is_static*/ false);
- mid_is_app_clone_user_ = CacheMethod(env, "isAppCloneUser", "(I)Z",
- /*is_static*/ false);
- mid_transform_ = CacheMethod(env, "transform", "(Ljava/lang/String;Ljava/lang/String;IIIII)Z",
- /*is_static*/ false);
+ CacheMethod(env, "isUidAllowedAccessToDataOrObbPath", "(ILjava/lang/String;)Z");
+ mid_on_file_created_ = CacheMethod(env, "onFileCreated", "(Ljava/lang/String;)V");
+ mid_should_allow_lookup_ = CacheMethod(env, "shouldAllowLookup", "(II)Z");
+ mid_is_app_clone_user_ = CacheMethod(env, "isAppCloneUser", "(I)Z");
+ mid_transform_ = CacheMethod(env, "transform", "(Ljava/lang/String;Ljava/lang/String;IIIII)Z");
mid_file_lookup_ =
CacheMethod(env, "onFileLookup",
- "(Ljava/lang/String;II)Lcom/android/providers/media/FileLookupResult;",
- /*is_static*/ false);
+ "(Ljava/lang/String;II)Lcom/android/providers/media/FileLookupResult;");
// FileLookupResult
file_lookup_result_class_ = env->FindClass("com/android/providers/media/FileLookupResult");
@@ -289,6 +270,7 @@
fid_file_open_transforms_uid_ = CacheField(env, file_open_result_class_, "transformsUid", "I");
fid_file_open_redaction_ranges_ =
CacheField(env, file_open_result_class_, "redactionRanges", "[J");
+ fid_file_open_fd_ = CacheField(env, file_open_result_class_, "nativeFd", "I");
}
MediaProviderWrapper::~MediaProviderWrapper() {
@@ -323,7 +305,8 @@
bool log_transforms_metrics) {
JNIEnv* env = MaybeAttachCurrentThread();
if (shouldBypassMediaProvider(uid)) {
- return std::make_unique<FileOpenResult>(0, uid, 0 /* transforms_uid */, new RedactionInfo());
+ return std::make_unique<FileOpenResult>(0, uid, /* transforms_uid */ 0, /* nativeFd */ -1,
+ new RedactionInfo());
}
ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
@@ -337,10 +320,11 @@
return nullptr;
}
- int status = env->GetIntField(j_res_file_open_object.get(), fid_file_open_status_);
- int original_uid = env->GetIntField(j_res_file_open_object.get(), fid_file_open_uid_);
- int transforms_uid =
+ const int status = env->GetIntField(j_res_file_open_object.get(), fid_file_open_status_);
+ const int original_uid = env->GetIntField(j_res_file_open_object.get(), fid_file_open_uid_);
+ const int transforms_uid =
env->GetIntField(j_res_file_open_object.get(), fid_file_open_transforms_uid_);
+ const int fd = env->GetIntField(j_res_file_open_object.get(), fid_file_open_fd_);
if (redact) {
ScopedLocalRef<jlongArray> redaction_ranges_local_ref(
@@ -358,9 +342,10 @@
// No ranges to redact
ri = std::make_unique<RedactionInfo>();
}
- return std::make_unique<FileOpenResult>(status, original_uid, transforms_uid, ri.release());
+ return std::make_unique<FileOpenResult>(status, original_uid, transforms_uid, fd,
+ ri.release());
} else {
- return std::make_unique<FileOpenResult>(status, original_uid, transforms_uid,
+ return std::make_unique<FileOpenResult>(status, original_uid, transforms_uid, fd,
new RedactionInfo());
}
}
@@ -371,9 +356,8 @@
}
JNIEnv* env = MaybeAttachCurrentThread();
- return isMkdirOrRmdirAllowedInternal(env, media_provider_object_,
- mid_is_mkdir_or_rmdir_allowed_, path, uid,
- /*forCreate*/ true);
+ return isDirAccessAllowedInternal(env, media_provider_object_, mid_is_diraccess_allowed_, path,
+ uid, kCreateDirectoryRequest);
}
int MediaProviderWrapper::IsDeletingDirAllowed(const string& path, uid_t uid) {
@@ -382,9 +366,8 @@
}
JNIEnv* env = MaybeAttachCurrentThread();
- return isMkdirOrRmdirAllowedInternal(env, media_provider_object_,
- mid_is_mkdir_or_rmdir_allowed_, path, uid,
- /*forCreate*/ false);
+ return isDirAccessAllowedInternal(env, media_provider_object_, mid_is_diraccess_allowed_, path,
+ uid, kDeleteDirectoryRequest);
}
std::vector<std::shared_ptr<DirectoryEntry>> MediaProviderWrapper::GetDirectoryEntries(
@@ -417,8 +400,9 @@
}
JNIEnv* env = MaybeAttachCurrentThread();
- return isOpendirAllowedInternal(env, media_provider_object_, mid_is_opendir_allowed_, path, uid,
- forWrite);
+ return isDirAccessAllowedInternal(env, media_provider_object_, mid_is_diraccess_allowed_, path,
+ uid,
+ forWrite ? kWriteDirectoryRequest : kReadDirectoryRequest);
}
bool MediaProviderWrapper::isUidAllowedAccessToDataOrObbPath(uid_t uid, const string& path) {
@@ -532,15 +516,12 @@
* Finds MediaProvider method and adds it to methods map so it can be quickly called later.
*/
jmethodID MediaProviderWrapper::CacheMethod(JNIEnv* env, const char method_name[],
- const char signature[], bool is_static) {
+ const char signature[]) {
jmethodID mid;
string actual_method_name(method_name);
actual_method_name.append("ForFuse");
- if (is_static) {
- mid = env->GetStaticMethodID(media_provider_class_, actual_method_name.c_str(), signature);
- } else {
- mid = env->GetMethodID(media_provider_class_, actual_method_name.c_str(), signature);
- }
+ mid = env->GetMethodID(media_provider_class_, actual_method_name.c_str(), signature);
+
if (!mid) {
LOG(FATAL) << "Error caching method: " << method_name << signature;
}
diff --git a/jni/MediaProviderWrapper.h b/jni/MediaProviderWrapper.h
index 2ad1769..9fa6c4e 100644
--- a/jni/MediaProviderWrapper.h
+++ b/jni/MediaProviderWrapper.h
@@ -38,13 +38,18 @@
/** Represents file open result from MediaProvider */
struct FileOpenResult {
- FileOpenResult(const int status, const int uid, uid_t transforms_uid,
+ FileOpenResult(const int status, const int uid, const uid_t transforms_uid, const int fd,
const RedactionInfo* redaction_info)
- : status(status), uid(uid), transforms_uid(transforms_uid), redaction_info(redaction_info) {}
+ : status(status),
+ uid(uid),
+ transforms_uid(transforms_uid),
+ fd(fd),
+ redaction_info(redaction_info) {}
const int status;
const int uid;
const uid_t transforms_uid;
+ const int fd;
std::unique_ptr<const RedactionInfo> redaction_info;
};
@@ -259,8 +264,7 @@
jmethodID mid_delete_file_;
jmethodID mid_on_file_open_;
jmethodID mid_scan_file_;
- jmethodID mid_is_mkdir_or_rmdir_allowed_;
- jmethodID mid_is_opendir_allowed_;
+ jmethodID mid_is_diraccess_allowed_;
jmethodID mid_get_files_in_dir_;
jmethodID mid_rename_;
jmethodID mid_is_uid_allowed_access_to_data_or_obb_path_;
@@ -281,12 +285,12 @@
jfieldID fid_file_open_uid_;
jfieldID fid_file_open_transforms_uid_;
jfieldID fid_file_open_redaction_ranges_;
+ jfieldID fid_file_open_fd_;
/**
* Auxiliary for caching MediaProvider methods.
*/
- jmethodID CacheMethod(JNIEnv* env, const char method_name[], const char signature[],
- bool is_static);
+ jmethodID CacheMethod(JNIEnv* env, const char method_name[], const char signature[]);
// Attaches the current thread (if necessary) and returns the JNIEnv
// associated with it.
diff --git a/jni/RedactionInfoTest.cpp b/jni/RedactionInfoTest.cpp
index 76eec13..3a7bd27 100644
--- a/jni/RedactionInfoTest.cpp
+++ b/jni/RedactionInfoTest.cpp
@@ -24,9 +24,8 @@
#include "libfuse_jni/RedactionInfo.h"
-using namespace mediaprovider::fuse;
+namespace mediaprovider::fuse {
-using std::unique_ptr;
using std::vector;
std::ostream& operator<<(std::ostream& os, const ReadRange& rr) {
@@ -381,3 +380,5 @@
info.getReadRanges(0, 40, &out); // read offsets [0, 40)
EXPECT_EQ(0, out.size());
}
+
+} // namespace mediaprovider::fuse
diff --git a/jni/TEST_MAPPING b/jni/TEST_MAPPING
index 5ee1bc6..aec3dd0 100644
--- a/jni/TEST_MAPPING
+++ b/jni/TEST_MAPPING
@@ -9,5 +9,16 @@
{
"name": "fuse_node_test"
}
+ ],
+ "hwasan-postsubmit": [
+ {
+ "name": "FuseUtilsTest"
+ },
+ {
+ "name": "RedactionInfoTest"
+ },
+ {
+ "name": "fuse_node_test"
+ }
]
}
diff --git a/jni/com_android_providers_media_FuseDaemon.cpp b/jni/com_android_providers_media_FuseDaemon.cpp
index d67123d..0215fa8 100644
--- a/jni/com_android_providers_media_FuseDaemon.cpp
+++ b/jni/com_android_providers_media_FuseDaemon.cpp
@@ -17,6 +17,7 @@
// Need to use LOGE_EX.
#define LOG_TAG "FuseDaemonJNI"
+#include <nativehelper/scoped_local_ref.h>
#include <nativehelper/scoped_utf_chars.h>
#include <string>
@@ -29,8 +30,45 @@
namespace mediaprovider {
namespace {
-constexpr const char* CLASS_NAME = "com/android/providers/media/fuse/FuseDaemon";
+constexpr const char* FUSE_DAEMON_CLASS_NAME = "com/android/providers/media/fuse/FuseDaemon";
+constexpr const char* FD_ACCESS_RESULT_CLASS_NAME = "com/android/providers/media/FdAccessResult";
static jclass gFuseDaemonClass;
+static jclass gFdAccessResultClass;
+static jmethodID gFdAccessResultCtor;
+
+static std::vector<std::string> convert_object_array_to_string_vector(
+ JNIEnv* env, jobjectArray java_object_array, const std::string& element_description) {
+ ScopedLocalRef<jobjectArray> j_ref_object_array(env, java_object_array);
+ std::vector<std::string> utf_strings;
+
+ const int object_array_length = env->GetArrayLength(j_ref_object_array.get());
+ for (int i = 0; i < object_array_length; i++) {
+ ScopedLocalRef<jstring> j_ref_string(
+ env, (jstring)env->GetObjectArrayElement(j_ref_object_array.get(), i));
+ ScopedUtfChars utf_chars(env, j_ref_string.get());
+ const char* utf_string = utf_chars.c_str();
+
+ if (utf_string) {
+ utf_strings.push_back(utf_string);
+ } else {
+ LOG(ERROR) << "Error reading " << element_description << " at index: " << i;
+ }
+ }
+
+ return utf_strings;
+}
+
+static std::vector<std::string> get_supported_transcoding_relative_paths(
+ JNIEnv* env, jobjectArray java_supported_transcoding_relative_paths) {
+ return convert_object_array_to_string_vector(env, java_supported_transcoding_relative_paths,
+ "supported transcoding relative path");
+}
+
+static std::vector<std::string> get_supported_uncached_relative_paths(
+ JNIEnv* env, jobjectArray java_supported_uncached_relative_paths) {
+ return convert_object_array_to_string_vector(env, java_supported_uncached_relative_paths,
+ "supported uncached relative path");
+}
jlong com_android_providers_media_FuseDaemon_new(JNIEnv* env, jobject self,
jobject media_provider) {
@@ -38,8 +76,10 @@
return reinterpret_cast<jlong>(new fuse::FuseDaemon(env, media_provider));
}
-void com_android_providers_media_FuseDaemon_start(JNIEnv* env, jobject self, jlong java_daemon,
- jint fd, jstring java_path) {
+void com_android_providers_media_FuseDaemon_start(
+ JNIEnv* env, jobject self, jlong java_daemon, jint fd, jstring java_path,
+ jboolean uncached_mode, jobjectArray java_supported_transcoding_relative_paths,
+ jobjectArray java_supported_uncached_relative_paths) {
LOG(DEBUG) << "Starting the FUSE daemon...";
fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
@@ -50,7 +90,14 @@
return;
}
- daemon->Start(std::move(ufd), utf_chars_path.c_str());
+ const std::vector<std::string>& transcoding_relative_paths =
+ get_supported_transcoding_relative_paths(env,
+ java_supported_transcoding_relative_paths);
+ const std::vector<std::string>& uncached_relative_paths =
+ get_supported_uncached_relative_paths(env, java_supported_uncached_relative_paths);
+
+ daemon->Start(std::move(ufd), utf_chars_path.c_str(), uncached_mode, transcoding_relative_paths,
+ uncached_relative_paths);
}
bool com_android_providers_media_FuseDaemon_is_started(JNIEnv* env, jobject self,
@@ -84,6 +131,15 @@
return JNI_FALSE;
}
+jboolean com_android_providers_media_FuseDaemon_uses_fuse_passthrough(JNIEnv* env, jobject self,
+ jlong java_daemon) {
+ fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
+ if (daemon) {
+ return daemon->UsesFusePassthrough();
+ }
+ return JNI_FALSE;
+}
+
void com_android_providers_media_FuseDaemon_invalidate_fuse_dentry_cache(JNIEnv* env, jobject self,
jlong java_daemon,
jstring java_path) {
@@ -101,11 +157,13 @@
// TODO(b/145741152): Throw exception
}
-jstring com_android_providers_media_FuseDaemon_get_original_media_format_file_path(
- JNIEnv* env, jobject self, jlong java_daemon, jint fd) {
+jobject com_android_providers_media_FuseDaemon_check_fd_access(JNIEnv* env, jobject self,
+ jlong java_daemon, jint fd,
+ jint uid) {
fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
- const std::string path = daemon->GetOriginalMediaFormatFilePath(fd);
- return env->NewStringUTF(path.c_str());
+ const std::unique_ptr<fuse::FdAccessResult> result = daemon->CheckFdAccess(fd, uid);
+ return env->NewObject(gFdAccessResultClass, gFdAccessResultCtor,
+ env->NewStringUTF(result->file_path.c_str()), result->should_redact);
}
void com_android_providers_media_FuseDaemon_initialize_device_id(JNIEnv* env, jobject self,
@@ -127,12 +185,14 @@
const JNINativeMethod methods[] = {
{"native_new", "(Lcom/android/providers/media/MediaProvider;)J",
reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_new)},
- {"native_start", "(JILjava/lang/String;)V",
+ {"native_start", "(JILjava/lang/String;Z[Ljava/lang/String;[Ljava/lang/String;)V",
reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_start)},
{"native_delete", "(J)V",
reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_delete)},
{"native_should_open_with_fuse", "(JLjava/lang/String;ZI)Z",
reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_should_open_with_fuse)},
+ {"native_uses_fuse_passthrough", "(J)Z",
+ reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_uses_fuse_passthrough)},
{"native_is_fuse_thread", "()Z",
reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_is_fuse_thread)},
{"native_is_started", "(J)Z",
@@ -140,24 +200,35 @@
{"native_invalidate_fuse_dentry_cache", "(JLjava/lang/String;)V",
reinterpret_cast<void*>(
com_android_providers_media_FuseDaemon_invalidate_fuse_dentry_cache)},
- {"native_get_original_media_format_file_path", "(JI)Ljava/lang/String;",
- reinterpret_cast<void*>(
- com_android_providers_media_FuseDaemon_get_original_media_format_file_path)},
+ {"native_check_fd_access", "(JII)Lcom/android/providers/media/FdAccessResult;",
+ reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_check_fd_access)},
{"native_initialize_device_id", "(JLjava/lang/String;)V",
reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_initialize_device_id)}};
} // namespace
void register_android_providers_media_FuseDaemon(JavaVM* vm, JNIEnv* env) {
- gFuseDaemonClass = static_cast<jclass>(env->NewGlobalRef(env->FindClass(CLASS_NAME)));
+ gFuseDaemonClass =
+ static_cast<jclass>(env->NewGlobalRef(env->FindClass(FUSE_DAEMON_CLASS_NAME)));
+ gFdAccessResultClass =
+ static_cast<jclass>(env->NewGlobalRef(env->FindClass(FD_ACCESS_RESULT_CLASS_NAME)));
if (gFuseDaemonClass == nullptr) {
- LOG(FATAL) << "Unable to find class : " << CLASS_NAME;
+ LOG(FATAL) << "Unable to find class : " << FUSE_DAEMON_CLASS_NAME;
+ }
+
+ if (gFdAccessResultClass == nullptr) {
+ LOG(FATAL) << "Unable to find class : " << FD_ACCESS_RESULT_CLASS_NAME;
}
if (env->RegisterNatives(gFuseDaemonClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
LOG(FATAL) << "Unable to register native methods";
}
+ gFdAccessResultCtor = env->GetMethodID(gFdAccessResultClass, "<init>", "(Ljava/lang/String;Z)V");
+ if (gFdAccessResultCtor == nullptr) {
+ LOG(FATAL) << "Unable to find ctor for FdAccessResult";
+ }
+
fuse::MediaProviderWrapper::OneTimeInit(vm);
}
} // namespace mediaprovider
diff --git a/jni/include/libfuse_jni/FuseUtils.h b/jni/include/libfuse_jni/FuseUtils.h
index 88044db..0e35099 100644
--- a/jni/include/libfuse_jni/FuseUtils.h
+++ b/jni/include/libfuse_jni/FuseUtils.h
@@ -23,13 +23,13 @@
namespace fuse {
/**
- * Returns true if the given path (ignoring case) is mounted for the given
+ * Returns true if the given path (ignoring case) is mounted for any
* userid. Mounted paths are:
* "/storage/emulated/<userid>/Android"
* "/storage/emulated/<userid>/Android/data"
* "/storage/emulated/<userid>/Android/obb" *
*/
-bool containsMount(const std::string& path, const std::string& userid);
+bool containsMount(const std::string& path);
} // namespace fuse
} // namespace mediaprovider
diff --git a/jni/node-inl.h b/jni/node-inl.h
index e531a0a..15844e3 100644
--- a/jni/node-inl.h
+++ b/jni/node-inl.h
@@ -77,6 +77,15 @@
~dirhandle() { closedir(d); }
};
+/** Represents file open result from MediaProvider */
+struct FdAccessResult {
+ FdAccessResult(const std::string& file_path, const bool should_redact)
+ : file_path(file_path), should_redact(should_redact) {}
+
+ const std::string file_path;
+ const bool should_redact;
+};
+
// Whether inode tracking is enabled or not. When enabled, we maintain a
// separate mapping from inode numbers to "live" nodes so we can detect when
// we receive a request to a node that has been deleted.
@@ -90,6 +99,14 @@
public:
explicit NodeTracker(std::recursive_mutex* lock) : lock_(lock) {}
+ bool Exists(__u64 ino) const {
+ if (kEnableInodeTracking) {
+ const node* node = reinterpret_cast<const class node*>(ino);
+ std::lock_guard<std::recursive_mutex> guard(*lock_);
+ return active_nodes_.find(node) != active_nodes_.end();
+ }
+ }
+
void CheckTracked(__u64 ino) const {
if (kEnableInodeTracking) {
const node* node = reinterpret_cast<const class node*>(ino);
@@ -127,15 +144,15 @@
public:
// Creates a new node with the specified parent, name and lock.
static node* Create(node* parent, const std::string& name, const std::string& io_path,
- bool should_invalidate, bool transforms_complete, const int transforms,
+ const bool transforms_complete, const int transforms,
const int transforms_reason, std::recursive_mutex* lock, ino_t ino,
NodeTracker* tracker) {
// Place the entire constructor under a critical section to make sure
// node creation, tracking (if enabled) and the addition to a parent are
// atomic.
std::lock_guard<std::recursive_mutex> guard(*lock);
- return new node(parent, name, io_path, should_invalidate, transforms_complete, transforms,
- transforms_reason, lock, ino, tracker);
+ return new node(parent, name, io_path, transforms_complete, transforms, transforms_reason,
+ lock, ino, tracker);
}
// Creates a new root node. Root nodes have no parents by definition
@@ -143,9 +160,8 @@
static node* CreateRoot(const std::string& path, std::recursive_mutex* lock, ino_t ino,
NodeTracker* tracker) {
std::lock_guard<std::recursive_mutex> guard(*lock);
- node* root = new node(nullptr, path, path, false /* should_invalidate */,
- true /* transforms_complete */, 0 /* transforms */,
- 0 /* transforms_reason */, lock, ino, tracker);
+ node* root = new node(nullptr, path, path, true /* transforms_complete */,
+ 0 /* transforms */, 0 /* transforms_reason */, lock, ino, tracker);
// The root always has one extra reference to avoid it being
// accidentally collected.
@@ -159,6 +175,12 @@
return reinterpret_cast<node*>(static_cast<uintptr_t>(ino));
}
+ // TODO(b/215235604)
+ static inline node* FromInodeNoThrow(__u64 ino, const NodeTracker* tracker) {
+ if (!tracker->Exists(ino)) return nullptr;
+ return reinterpret_cast<node*>(static_cast<uintptr_t>(ino));
+ }
+
// Maps a node to its associated inode.
static __u64 ToInode(node* node) {
return static_cast<__u64>(reinterpret_cast<uintptr_t>(node));
@@ -319,14 +341,28 @@
return false;
}
- bool ShouldInvalidate() const {
+ std::unique_ptr<FdAccessResult> CheckHandleForUid(const uid_t uid) const {
std::lock_guard<std::recursive_mutex> guard(*lock_);
- return should_invalidate_;
+
+ bool found_handle = false;
+ bool redaction_not_needed = false;
+ for (const auto& handle : handles_) {
+ if (handle->uid == uid) {
+ found_handle = true;
+ redaction_not_needed |= !handle->ri->isRedactionNeeded();
+ }
+ }
+
+ if (found_handle) {
+ return std::make_unique<FdAccessResult>(BuildPath(), !redaction_not_needed);
+ }
+
+ return std::make_unique<FdAccessResult>(std::string(), false);
}
- void SetShouldInvalidate() {
+ void SetName(std::string name) {
std::lock_guard<std::recursive_mutex> guard(*lock_);
- should_invalidate_ = true;
+ name_ = std::move(name);
}
bool HasRedactedCache() const {
@@ -366,8 +402,8 @@
private:
node(node* parent, const std::string& name, const std::string& io_path,
- const bool should_invalidate, const bool transforms_complete, const int transforms,
- const int transforms_reason, std::recursive_mutex* lock, ino_t ino, NodeTracker* tracker)
+ const bool transforms_complete, const int transforms, const int transforms_reason,
+ std::recursive_mutex* lock, ino_t ino, NodeTracker* tracker)
: name_(name),
io_path_(io_path),
transforms_complete_(transforms_complete),
@@ -376,7 +412,6 @@
refcount_(0),
parent_(nullptr),
has_redacted_cache_(false),
- should_invalidate_(should_invalidate),
deleted_(false),
lock_(lock),
ino_(ino),
@@ -388,10 +423,6 @@
if (parent != nullptr) {
AddToParent(parent);
}
- // If the node requires transforms, we MUST never cache it in the VFS
- if (transforms) {
- CHECK(should_invalidate_);
- }
}
// Acquires a reference to a node. This maps to the "lookup count" specified
@@ -530,7 +561,6 @@
// List of directory handles associated with this node. Guarded by |lock_|.
std::vector<std::unique_ptr<dirhandle>> dirhandles_;
bool has_redacted_cache_;
- bool should_invalidate_;
bool deleted_;
std::recursive_mutex* lock_;
// Inode number of the file represented by this node.
diff --git a/jni/node_test.cpp b/jni/node_test.cpp
index e6870f8..f687cad 100644
--- a/jni/node_test.cpp
+++ b/jni/node_test.cpp
@@ -7,7 +7,6 @@
#include <memory>
#include <mutex>
-using mediaprovider::fuse::dirhandle;
using mediaprovider::fuse::handle;
using mediaprovider::fuse::node;
using mediaprovider::fuse::NodeTracker;
@@ -33,7 +32,7 @@
unique_node_ptr CreateNode(node* parent, const std::string& path, const int transforms = 0) {
return unique_node_ptr(
- node::Create(parent, path, "", true, true, transforms, 0, &lock_, 0, &tracker_),
+ node::Create(parent, path, "", true, transforms, 0, &lock_, 0, &tracker_),
&NodeTest::destroy);
}
@@ -68,7 +67,7 @@
}
TEST_F(NodeTest, TestRelease) {
- node* node = node::Create(nullptr, "/path", "", false, true, 0, 0, &lock_, 0, &tracker_);
+ node* node = node::Create(nullptr, "/path", "", true, 0, 0, &lock_, 0, &tracker_);
acquire(node);
acquire(node);
ASSERT_EQ(3, GetRefCount(node));
@@ -278,10 +277,10 @@
unique_node_ptr parent = CreateNode(nullptr, "/path");
// This is the tree that we intend to delete.
- node* child = node::Create(parent.get(), "subdir", "", false, true, 0, 0, &lock_, 0, &tracker_);
- node::Create(child, "s1", "", false, true, 0, 0, &lock_, 0, &tracker_);
- node* subchild2 = node::Create(child, "s2", "", false, true, 0, 0, &lock_, 0, &tracker_);
- node::Create(subchild2, "sc2", "", false, true, 0, 0, &lock_, 0, &tracker_);
+ node* child = node::Create(parent.get(), "subdir", "", true, 0, 0, &lock_, 0, &tracker_);
+ node::Create(child, "s1", "", true, 0, 0, &lock_, 0, &tracker_);
+ node* subchild2 = node::Create(child, "s2", "", true, 0, 0, &lock_, 0, &tracker_);
+ node::Create(subchild2, "sc2", "", true, 0, 0, &lock_, 0, &tracker_);
ASSERT_EQ(child, parent->LookupChildByName("subdir", false /* acquire */));
node::DeleteTree(child);
@@ -367,6 +366,80 @@
EXPECT_DEATH(node->DestroyHandle(h2.get()), "");
}
+TEST_F(NodeTest, CheckHandleForUid_foundSingle_shouldRedact) {
+ unique_node_ptr node = CreateNode(nullptr, "/path");
+
+ off64_t ranges[2] = {0, 1};
+ mediaprovider::fuse::RedactionInfo* infoWithLocation =
+ new mediaprovider::fuse::RedactionInfo(1, ranges);
+
+ handle* h = new handle(-1, infoWithLocation, true /* cached */, false /* passthrough */,
+ 1 /* uid */, 0 /* transforms_uid */);
+
+ node->AddHandle(h);
+ std::unique_ptr<mediaprovider::fuse::FdAccessResult> res(node->CheckHandleForUid(1));
+ ASSERT_TRUE(res->should_redact);
+ ASSERT_EQ(res->file_path, "/path");
+}
+
+TEST_F(NodeTest, CheckHandleForUid_foundSingle_shouldNotRedact) {
+ unique_node_ptr node = CreateNode(nullptr, "/path");
+
+ mediaprovider::fuse::RedactionInfo* infoWithoutLocation = new mediaprovider::fuse::RedactionInfo;
+
+ handle* h = new handle(-1, infoWithoutLocation, true /* cached */, false /* passthrough */,
+ 1 /* uid */, 0 /* transforms_uid */);
+
+ node->AddHandle(h);
+ std::unique_ptr<mediaprovider::fuse::FdAccessResult> res(node->CheckHandleForUid(1));
+ ASSERT_FALSE(res->should_redact);
+ ASSERT_EQ(res->file_path, "/path");
+}
+
+TEST_F(NodeTest, CheckHandleForUid_foundMultiple_shouldNotRedact) {
+ unique_node_ptr node = CreateNode(nullptr, "/path");
+
+ off64_t ranges[2] = {0, 1};
+ mediaprovider::fuse::RedactionInfo* infoWithLocation =
+ new mediaprovider::fuse::RedactionInfo(1, ranges);
+ mediaprovider::fuse::RedactionInfo* infoWithoutLocation = new mediaprovider::fuse::RedactionInfo;
+
+ handle* h1 = new handle(-1, infoWithLocation, true /* cached */, false /* passthrough */,
+ 1 /* uid */, 0 /* transforms_uid */);
+ handle* h2 = new handle(-1, infoWithoutLocation, true /* cached */, false /* passthrough */,
+ 1 /* uid */, 0 /* transforms_uid */);
+
+ node->AddHandle(h1);
+ node->AddHandle(h2);
+ std::unique_ptr<mediaprovider::fuse::FdAccessResult> res(node->CheckHandleForUid(1));
+ ASSERT_FALSE(res->should_redact);
+ ASSERT_EQ(res->file_path, "/path");
+}
+
+TEST_F(NodeTest, CheckHandleForUid_notFound_differentUid) {
+ unique_node_ptr node = CreateNode(nullptr, "/path");
+
+ off64_t ranges[2] = {0, 1};
+ mediaprovider::fuse::RedactionInfo* infoWithLocation =
+ new mediaprovider::fuse::RedactionInfo(1, ranges);
+
+ handle* h = new handle(-1, infoWithLocation, true /* cached */, false /* passthrough */,
+ 2 /* uid */, 0 /* transforms_uid */);
+
+ node->AddHandle(h);
+ std::unique_ptr<mediaprovider::fuse::FdAccessResult> res(node->CheckHandleForUid(1));
+ ASSERT_FALSE(res->should_redact);
+ ASSERT_EQ(res->file_path, "");
+}
+
+TEST_F(NodeTest, CheckHandleForUid_notFound_noHandle) {
+ unique_node_ptr node = CreateNode(nullptr, "/path");
+
+ std::unique_ptr<mediaprovider::fuse::FdAccessResult> res(node->CheckHandleForUid(1));
+ ASSERT_FALSE(res->should_redact);
+ ASSERT_EQ(res->file_path, "");
+}
+
TEST_F(NodeTest, CaseInsensitive) {
unique_node_ptr parent = CreateNode(nullptr, "/path");
unique_node_ptr mixed_child = CreateNode(parent.get(), "cHiLd");
diff --git a/legacy/Android.bp b/legacy/Android.bp
index dd9e5f8..14d7f55 100644
--- a/legacy/Android.bp
+++ b/legacy/Android.bp
@@ -1,11 +1,6 @@
-
package {
// See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "packages_providers_MediaProvider_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["packages_providers_MediaProvider_license"],
+ default_applicable_licenses: ["Android-Apache-2.0"],
}
android_app {
@@ -13,18 +8,19 @@
manifest: "AndroidManifest.xml",
static_libs: [
+ "mediaprovider-database",
"androidx.appcompat_appcompat",
"androidx.core_core",
- "guava",
- "modules-utils-build",
],
- libs: ["app-compat-annotations"],
+ libs: [
+ "app-compat-annotations",
+ "framework-mediaprovider",
+ ],
srcs: [
"src/**/*.aidl",
"src/**/*.java",
- ":mediaprovider-database-sources",
],
platform_apis: true,
diff --git a/legacy/AndroidManifest.xml b/legacy/AndroidManifest.xml
index 2396f58..2f7ea97 100644
--- a/legacy/AndroidManifest.xml
+++ b/legacy/AndroidManifest.xml
@@ -1,4 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
package="com.android.providers.media"
android:sharedUserId="android.media"
android:versionCode="1024">
@@ -19,5 +20,11 @@
android:authorities="media_legacy"
android:exported="true"
android:permission="android.permission.WRITE_MEDIA_STORAGE" />
+
+ <!-- (b/197891819) Remove startup provider due to resource loading issues. -->
+ <provider
+ android:name="androidx.startup.InitializationProvider"
+ android:authorities="${applicationId}.androidx-startup"
+ tools:node="remove" />
</application>
</manifest>
diff --git a/legacy/src/com/android/providers/media/LegacyMediaProvider.java b/legacy/src/com/android/providers/media/LegacyMediaProvider.java
index e921155..9951bc5 100644
--- a/legacy/src/com/android/providers/media/LegacyMediaProvider.java
+++ b/legacy/src/com/android/providers/media/LegacyMediaProvider.java
@@ -79,10 +79,10 @@
final File persistentDir = context.getDir("logs", Context.MODE_PRIVATE);
Logging.initPersistent(persistentDir);
- mInternalDatabase = new DatabaseHelper(context, INTERNAL_DATABASE_NAME,
- true, false, true, null, null, null, null, null);
- mExternalDatabase = new DatabaseHelper(context, EXTERNAL_DATABASE_NAME,
- false, false, true, null, null, null, null, null);
+ mInternalDatabase = new DatabaseHelper(context, INTERNAL_DATABASE_NAME, false, true, null,
+ null, null, null, null, null, false);
+ mExternalDatabase = new DatabaseHelper(context, EXTERNAL_DATABASE_NAME, false, true, null,
+ null, null, null, null, null, false);
return true;
}
diff --git a/lint-baseline.xml b/lint-baseline.xml
deleted file mode 100644
index 5f39c57..0000000
--- a/lint-baseline.xml
+++ /dev/null
@@ -1,180 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
-
- <issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.os.storage.StorageVolume#getStorageUuid`"
- errorLine1=" mTranscodeVolumeUuid = vol.getStorageUuid();"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
- line="311"
- column="44"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.content.pm.PackageManager#getProperty`"
- errorLine1=" Property mediaCapProperty = mPackageManager.getProperty("
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
- line="827"
- column="57"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.content.pm.PackageManager.Property#getResourceId`"
- errorLine1=" .getXml(mediaCapProperty.getResourceId());"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
- line="830"
- column="46"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.media.ApplicationMediaCapabilities#createFromXml`"
- errorLine1=" ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml("
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
- line="831"
- column="84"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.media.ApplicationMediaCapabilities#isFormatSpecified`"
- errorLine1=" if (capability.isFormatSpecified(MediaFormat.MIMETYPE_VIDEO_HEVC)) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
- line="852"
- column="24"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.media.ApplicationMediaCapabilities#isVideoMimeTypeSupported`"
- errorLine1=" if (capability.isVideoMimeTypeSupported(MediaFormat.MIMETYPE_VIDEO_HEVC)) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
- line="853"
- column="28"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.media.ApplicationMediaCapabilities#isFormatSpecified`"
- errorLine1=" if (capability.isFormatSpecified(MediaFeature.HdrType.HDR10)) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
- line="861"
- column="24"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.media.ApplicationMediaCapabilities#isHdrTypeSupported`"
- errorLine1=" if (capability.isHdrTypeSupported(MediaFeature.HdrType.HDR10)) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
- line="862"
- column="28"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.media.ApplicationMediaCapabilities#isFormatSpecified`"
- errorLine1=" if (capability.isFormatSpecified(MediaFeature.HdrType.HDR10_PLUS)) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
- line="869"
- column="24"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.media.ApplicationMediaCapabilities#isHdrTypeSupported`"
- errorLine1=" if (capability.isHdrTypeSupported(MediaFeature.HdrType.HDR10_PLUS)) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
- line="870"
- column="28"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.media.ApplicationMediaCapabilities#isFormatSpecified`"
- errorLine1=" if (capability.isFormatSpecified(MediaFeature.HdrType.HLG)) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
- line="877"
- column="24"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.media.ApplicationMediaCapabilities#isHdrTypeSupported`"
- errorLine1=" if (capability.isHdrTypeSupported(MediaFeature.HdrType.HLG)) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
- line="878"
- column="28"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.media.ApplicationMediaCapabilities#isFormatSpecified`"
- errorLine1=" if (capability.isFormatSpecified(MediaFeature.HdrType.DOLBY_VISION)) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
- line="885"
- column="24"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.media.ApplicationMediaCapabilities#isHdrTypeSupported`"
- errorLine1=" if (capability.isHdrTypeSupported(MediaFeature.HdrType.DOLBY_VISION)) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
- line="886"
- column="28"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.media.ApplicationMediaCapabilities.Builder#build`"
- errorLine1=" new ApplicationMediaCapabilities.Builder().build();"
- errorLine2=" ~~~~~">
- <location
- file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
- line="1079"
- column="68"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `new android.media.ApplicationMediaCapabilities.Builder`"
- errorLine1=" new ApplicationMediaCapabilities.Builder().build();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
- line="1079"
- column="25"/>
- </issue>
-
-</issues>
diff --git a/proguard.flags b/proguard.flags
index 862b47f..aa234cc 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -1 +1,27 @@
--keep class com.android.providers.media.** { *; }
+-keep public class android.provider.** { *; }
+-keep public class com.android.providers.media.CacheClearingActivity
+-keep public class com.android.providers.media.IdleService
+-keep public class com.android.providers.media.MediaApplication
+-keep public class com.android.providers.media.MediaDocumentsProvider
+-keep public class com.android.providers.media.MediaProvider
+-keep public class com.android.providers.media.MediaReceiver
+-keep public class com.android.providers.media.MediaService
+-keep public class com.android.providers.media.MediaUpgradeReceiver
+-keep public class com.android.providers.media.PermissionActivity
+-keep public class com.android.providers.media.fuse.ExternalStorageServiceImpl
+-keep public class com.android.providers.media.photopicker.PhotoPickerActivity
+-keep public class com.android.providers.media.photopicker.PhotoPickerProvider
+-keep public final class com.android.providers.media.FileLookupResult { *; }
+-keep public final class com.android.providers.media.FileOpenResult { *; }
+-keep public final class com.android.providers.media.FdAccessResult { *; }
+-keep public class * implements com.bumptech.glide.module.GlideModule
+-keep class * extends com.bumptech.glide.module.AppGlideModule {
+ <init>(...);
+}
+-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
+ **[] $VALUES;
+ public *;
+}
+-keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder {
+ *** rewind();
+}
diff --git a/res/anim/slide_down.xml b/res/anim/slide_down.xml
new file mode 100644
index 0000000..9b12c85
--- /dev/null
+++ b/res/anim/slide_down.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:duration="250"
+ android:interpolator="@android:interpolator/linear_out_slow_in"
+ android:fromYDelta="0%"
+ android:toYDelta="100%"/>
diff --git a/res/anim/slide_up.xml b/res/anim/slide_up.xml
new file mode 100644
index 0000000..dc59036
--- /dev/null
+++ b/res/anim/slide_up.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:duration="250"
+ android:interpolator="@android:interpolator/fast_out_linear_in"
+ android:fromYDelta="100%"
+ android:toYDelta="0%"/>
diff --git a/res/color-v31/surface_light.xml b/res/color-v31/surface_light.xml
new file mode 100644
index 0000000..928a6a9
--- /dev/null
+++ b/res/color-v31/surface_light.xml
@@ -0,0 +1,18 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral1_500" android:lStar="98" />
+</selector>
diff --git a/res/drawable/ic_arrow_back.xml b/res/drawable/ic_arrow_back.xml
new file mode 100644
index 0000000..85a418a
--- /dev/null
+++ b/res/drawable/ic_arrow_back.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:tint="?attr/pickerTextColor"
+ android:autoMirrored="true">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_artwork_camera.xml b/res/drawable/ic_artwork_camera.xml
new file mode 100644
index 0000000..dc22c49
--- /dev/null
+++ b/res/drawable/ic_artwork_camera.xml
@@ -0,0 +1,39 @@
+<?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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="120dp"
+ android:height="80dp"
+ android:viewportWidth="120"
+ android:viewportHeight="80">
+ <path
+ android:pathData="M96,14m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"
+ android:fillColor="#EA4335"/>
+ <path
+ android:pathData="M8,14h104v64h-104z"
+ android:fillColor="#DADCE0"/>
+ <path
+ android:pathData="M16,10h16v4h-16z"
+ android:fillColor="#5F6368"/>
+ <path
+ android:strokeWidth="1"
+ android:pathData="M60,48m-25.5,0a25.5,25.5 0,1 1,51 0a25.5,25.5 0,1 1,-51 0"
+ android:fillColor="#DADCE0"
+ android:strokeColor="#5F6368"/>
+ <path
+ android:pathData="M60,48m-20,0a20,20 0,1 1,40 0a20,20 0,1 1,-40 0"
+ android:fillColor="#BDC1C6"/>
+</vector>
diff --git a/res/drawable/ic_check_circle_filled.xml b/res/drawable/ic_check_circle_filled.xml
new file mode 100644
index 0000000..6dbfcf3
--- /dev/null
+++ b/res/drawable/ic_check_circle_filled.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="?attr/pickerSelectedColor"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12c0,5.52 4.48,10 10,10s10,-4.48 10,-10C22,6.48 17.52,2 12,2zM10,17l-4,-4l1.4,-1.4l2.6,2.6l6.6,-6.6L18,9L10,17z"/>
+</vector>
diff --git a/res/drawable/ic_close.xml b/res/drawable/ic_close.xml
new file mode 100644
index 0000000..5e5e885
--- /dev/null
+++ b/res/drawable/ic_close.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:tint="?attr/pickerTextColor">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12 19,6.41z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_collections.xml b/res/drawable/ic_collections.xml
new file mode 100644
index 0000000..7ac707d
--- /dev/null
+++ b/res/drawable/ic_collections.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/pickerSelectedColor">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,4v12L8,16L8,4h12m0,-2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM2,6v14c0,1.1 0.9,2 2,2h14v-2L4,20L4,6L2,6zM15.67,11l-2.5,2.98 -1.67,-2.18L9,15h10l-3.33,-4z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_drag.xml b/res/drawable/ic_drag.xml
new file mode 100644
index 0000000..4b5c91f
--- /dev/null
+++ b/res/drawable/ic_drag.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle" >
+ <size android:width="32dp"
+ android:height="4dp" />
+ <solid android:color="?attr/pickerDragBarColor" />
+ <corners android:radius="2dp" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/ic_gif.xml b/res/drawable/ic_gif.xml
new file mode 100644
index 0000000..f0d1c98
--- /dev/null
+++ b/res/drawable/ic_gif.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M11.5,9L13,9v6h-1.5L11.5,9zM9,9L6,9c-0.6,0 -1,0.5 -1,1v4c0,0.5 0.4,1 1,1h3c0.6,0 1,-0.5 1,-1v-2L8.5,12v1.5h-2v-3L10,10.5L10,10c0,-0.5 -0.4,-1 -1,-1zM19,10.5L19,9h-4.5v6L16,15v-2h2v-1.5h-2v-1h3z"/>
+</vector>
diff --git a/res/drawable/ic_lock.xml b/res/drawable/ic_lock.xml
new file mode 100644
index 0000000..7be17dd
--- /dev/null
+++ b/res/drawable/ic_lock.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM9,6c0,-1.66 1.34,-3 3,-3s3,1.34 3,3v2L9,8L9,6zM18,20L6,20L6,10h12v10zM12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2z"/>
+</vector>
diff --git a/res/drawable/ic_motion_photo.xml b/res/drawable/ic_motion_photo.xml
new file mode 100644
index 0000000..ea8bf73
--- /dev/null
+++ b/res/drawable/ic_motion_photo.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="18dp"
+ android:height="18dp"
+ android:viewportHeight="18"
+ android:viewportWidth="18">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M6.5 11.375V4.625L11 8L6.5 11.375ZM15.5 8C15.5 12.14 12.14 15.5 8 15.5C3.86 15.5 0.5 12.14 0.5 8C0.5 7.1075 0.665 6.26 0.95 5.465L2.36 5.975C2.1275 6.605 2 7.2875 2 8C2 11.3075 4.6925 14 8 14C11.3075 14 14 11.3075 14 8C14 4.6925 11.3075 2 8 2C7.2875 2 6.6125 2.1275 5.9825 2.36L5.4725 0.9425C6.2675 0.665 7.115 0.5 8 0.5C12.14 0.5 15.5 3.86 15.5 8ZM3.125 2C2.5025 2 2 2.5025 2 3.125C2 3.7475 2.5025 4.25 3.125 4.25C3.7475 4.25 4.25 3.7475 4.25 3.125C4.25 2.5025 3.7475 2 3.125 2Z"/>
+</vector>
diff --git a/res/drawable/ic_personal_mode.xml b/res/drawable/ic_personal_mode.xml
new file mode 100644
index 0000000..0d64a4c
--- /dev/null
+++ b/res/drawable/ic_personal_mode.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/pickerProfileButtonTextColor">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19.08,4.92C15.16,1.02 8.82,1.03 4.91,4.93C1.03,8.85 1.03,15.17 4.92,19.08C6.8,20.95 9.35,22 12,22c2.65,0 5.2,-1.05 7.08,-2.92C22.97,15.16 22.98,8.83 19.08,4.92zM6.34,17.66L6.34,17.66c0.86,-0.8 3.22,-2.16 5.67,-2.16c2.45,0 4.64,1.24 5.65,2.16C14.53,20.77 9.48,20.77 6.34,17.66zM18.93,16.03c-3.99,-3.36 -9.82,-3.36 -13.82,0c-1.77,-3.07 -1.38,-7.05 1.22,-9.69c3.13,-3.12 8.21,-3.13 11.34,-0.01C20.23,8.91 20.75,12.88 18.93,16.03zM15,8.99c0,1.66 -1.34,3 -3,3c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3C13.66,5.99 15,7.34 15,8.99z"
+ android:fillType="evenOdd"/>
+</vector>
diff --git a/res/drawable/ic_play_circle_filled.xml b/res/drawable/ic_play_circle_filled.xml
new file mode 100644
index 0000000..e7509a2
--- /dev/null
+++ b/res/drawable/ic_play_circle_filled.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="18dp"
+ android:height="18dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM10,16.5v-9l6,4.5 -6,4.5z"/>
+</vector>
diff --git a/res/drawable/ic_preview_pause.xml b/res/drawable/ic_preview_pause.xml
new file mode 100644
index 0000000..ce3e82e
--- /dev/null
+++ b/res/drawable/ic_preview_pause.xml
@@ -0,0 +1,29 @@
+<!--
+ ~ 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
+ <path
+ android:pathData="M24,0L24,0A24,24 0,0 1,48 24L48,24A24,24 0,0 1,24 48L24,48A24,24 0,0 1,0 24L0,24A24,24 0,0 1,24 0z"
+ android:fillColor="#000000"
+ android:fillAlpha="0.5"/>
+ <path
+ android:pathData="M31,31H25V17H31V31ZM27,29H29V19H27V29ZM23,31H17V17H23V31ZM19,29H21V19H19V29Z"
+ android:fillColor="#ffffff"/>
+</vector>
diff --git a/res/drawable/ic_preview_play.xml b/res/drawable/ic_preview_play.xml
new file mode 100644
index 0000000..115c9be
--- /dev/null
+++ b/res/drawable/ic_preview_play.xml
@@ -0,0 +1,30 @@
+<!--
+ ~ 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
+ <path
+ android:pathData="M24,0L24,0A24,24 0,0 1,48 24L48,24A24,24 0,0 1,24 48L24,48A24,24 0,0 1,0 24L0,24A24,24 0,0 1,24 0z"
+ android:fillColor="#000000"
+ android:fillAlpha="0.5"/>
+ <path
+ android:pathData="M32,24L18,33V15L32,24ZM27.26,24L20.55,19.68V28.32L27.26,24Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+</vector>
diff --git a/res/drawable/ic_radio_button_unchecked.xml b/res/drawable/ic_radio_button_unchecked.xml
new file mode 100644
index 0000000..40a7734
--- /dev/null
+++ b/res/drawable/ic_radio_button_unchecked.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@color/picker_unselected_check_color"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
+</vector>
diff --git a/res/drawable/ic_volume_off.xml b/res/drawable/ic_volume_off.xml
new file mode 100644
index 0000000..1f28454
--- /dev/null
+++ b/res/drawable/ic_volume_off.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20.5,22.6l-3.025,-3.025q-0.625,0.4 -1.325,0.688 -0.7,0.287 -1.45,0.462v-2.05q0.35,-0.125 0.688,-0.25 0.337,-0.125 0.637,-0.3L12.7,14.8L12.7,20l-5,-5h-4L3.7,9h3.2L2.1,4.2l1.4,-1.4 18.4,18.4zM20.3,16.8l-1.45,-1.45q0.425,-0.775 0.637,-1.625 0.213,-0.85 0.213,-1.75 0,-2.35 -1.375,-4.2t-3.625,-2.5v-2.05q3.1,0.7 5.05,3.138Q21.7,8.8 21.7,11.975q0,1.325 -0.363,2.55 -0.362,1.225 -1.037,2.275zM9.8,11.9zM16.95,13.45L14.7,11.2L14.7,7.95q1.175,0.55 1.838,1.65 0.662,1.1 0.662,2.4 0,0.375 -0.063,0.738 -0.062,0.362 -0.187,0.712zM12.7,9.2l-2.6,-2.6L12.7,4zM10.7,15.15L10.7,12.8L8.9,11L5.7,11v2h2.85z"/>
+</vector>
diff --git a/res/drawable/ic_volume_up.xml b/res/drawable/ic_volume_up.xml
new file mode 100644
index 0000000..261f975
--- /dev/null
+++ b/res/drawable/ic_volume_up.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M14,20.725v-2.05q2.25,-0.65 3.625,-2.5t1.375,-4.2q0,-2.35 -1.375,-4.2T14,5.275v-2.05q3.1,0.7 5.05,3.138Q21,8.8 21,11.975q0,3.175 -1.95,5.612 -1.95,2.438 -5.05,3.138zM3,15L3,9h4l5,-5v16l-5,-5zM14,16L14,7.95q1.125,0.525 1.813,1.625 0.687,1.1 0.687,2.425 0,1.325 -0.688,2.4Q15.126,15.475 14,16zM10,8.85L7.85,11L5,11v2h2.85L10,15.15zM7.5,12z"/>
+</vector>
diff --git a/res/drawable/ic_work_outline.xml b/res/drawable/ic_work_outline.xml
new file mode 100644
index 0000000..12bf61e
--- /dev/null
+++ b/res/drawable/ic_work_outline.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/pickerProfileButtonTextColor">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM10,4h4v2h-4L10,4zM20,19L4,19L4,8h16v11z"/>
+</vector>
diff --git a/res/drawable/picker_item_check.xml b/res/drawable/picker_item_check.xml
new file mode 100644
index 0000000..fb0ef88
--- /dev/null
+++ b/res/drawable/picker_item_check.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_selected="true">
+ <layer-list>
+ <item android:gravity="center"
+ android:width="18dp"
+ android:height="18dp">
+ <shape android:shape="oval">
+ <solid android:color="@color/picker_background_color"/>
+ </shape>
+ </item>
+ <item android:drawable="@drawable/ic_check_circle_filled"/>
+ </layer-list>
+ </item>
+ <item android:drawable="@drawable/ic_radio_button_unchecked"/>
+</selector>
diff --git a/res/drawable/picker_item_gradient.xml b/res/drawable/picker_item_gradient.xml
new file mode 100644
index 0000000..4793d35
--- /dev/null
+++ b/res/drawable/picker_item_gradient.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <gradient
+ android:startColor="@color/picker_item_gradient_color"
+ android:endColor="@android:color/transparent"
+ android:angle="270">
+ </gradient>
+</shape>
diff --git a/res/drawable/picker_tab_background.xml b/res/drawable/picker_tab_background.xml
new file mode 100644
index 0000000..2c0af95
--- /dev/null
+++ b/res/drawable/picker_tab_background.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:gravity="center">
+ <shape android:shape="rectangle">
+ <size
+ android:width="@dimen/picker_tab_width"
+ android:height="@dimen/picker_tab_height" />
+ <corners android:radius="@dimen/picker_tab_radius"/>
+ <solid android:color="?attr/pickerTabBackgroundColor"/>
+ </shape>
+ </item>
+</layer-list>
diff --git a/res/drawable/picker_tab_indicator.xml b/res/drawable/picker_tab_indicator.xml
new file mode 100644
index 0000000..626472f
--- /dev/null
+++ b/res/drawable/picker_tab_indicator.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:gravity="center">
+ <shape android:shape="rectangle">
+ <size
+ android:width="@dimen/picker_tab_width"
+ android:height="@dimen/picker_tab_height" />
+ <corners android:radius="@dimen/picker_tab_radius"/>
+ <solid android:color="?attr/pickerSelectedTabBackgroundColor"/>
+ </shape>
+ </item>
+</layer-list>
diff --git a/res/drawable/preview_check.xml b/res/drawable/preview_check.xml
new file mode 100644
index 0000000..c523a9e
--- /dev/null
+++ b/res/drawable/preview_check.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_selected="true"
+ android:drawable="@drawable/ic_check_circle_filled"/>
+ <item android:drawable="@drawable/ic_radio_button_unchecked"/>
+</selector>
diff --git a/res/drawable/preview_gradient_asc.xml b/res/drawable/preview_gradient_asc.xml
new file mode 100644
index 0000000..3f03abd
--- /dev/null
+++ b/res/drawable/preview_gradient_asc.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <gradient
+ android:startColor="@color/preview_gradient_color_light"
+ android:endColor="@color/preview_gradient_color_dark"
+ android:angle="270">
+ </gradient>
+</shape>
diff --git a/res/drawable/preview_gradient_desc.xml b/res/drawable/preview_gradient_desc.xml
new file mode 100644
index 0000000..c68c70e
--- /dev/null
+++ b/res/drawable/preview_gradient_desc.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <gradient
+ android:startColor="@color/preview_gradient_color_dark"
+ android:endColor="@color/preview_gradient_color_light"
+ android:angle="270">
+ </gradient>
+</shape>
diff --git a/res/layout/activity_photo_picker.xml b/res/layout/activity_photo_picker.xml
new file mode 100644
index 0000000..1680806
--- /dev/null
+++ b/res/layout/activity_photo_picker.xml
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<!-- CoordinatorLayout is necessary for various components (e.g. Snackbars, and
+ BottomSheet) to operate correctly. -->
+<androidx.coordinatorlayout.widget.CoordinatorLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".MainActivity">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/bottom_sheet"
+ android:background="@color/picker_background_color"
+ android:clipToOutline="true"
+ android:orientation="vertical"
+ app:behavior_hideable="true"
+ app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
+
+ <ImageView
+ android:id="@+id/drag_bar"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="@dimen/picker_drag_margin_bottom"
+ android:layout_marginTop="@dimen/picker_drag_margin_top"
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_drag"
+ android:contentDescription="@null"/>
+
+ <TextView
+ android:id="@+id/privacy_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginHorizontal="@dimen/picker_privacy_text_horizontal_gap"
+ android:layout_marginBottom="@dimen/picker_privacy_text_margin_bottom"
+ android:layout_marginTop="@dimen/picker_privacy_text_margin_top"
+ android:gravity="center_horizontal"
+ android:text="@string/picker_privacy_message"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="@dimen/picker_privacy_text_size"
+ style="?android:attr/textAppearanceListItem"/>
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <androidx.fragment.app.FragmentContainerView
+ android:id="@+id/fragment_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:importantForAccessibility="yes"/>
+
+ <com.google.android.material.appbar.AppBarLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:background="@android:color/transparent"
+ app:liftOnScroll="true">
+
+ <androidx.appcompat.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="@color/picker_background_color"
+ android:importantForAccessibility="yes"
+ android:accessibilityTraversalAfter="@+id/privacy_text"
+ android:accessibilityTraversalBefore="@+id/fragment_container"
+ app:titleTextColor="?attr/pickerTextColor"
+ app:titleTextAppearance="@style/PickerToolbarTitleTextAppearance">
+
+ <com.google.android.material.tabs.TabLayout
+ android:id="@+id/tab_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@color/picker_background_color"
+ android:layout_gravity="center"
+ app:tabBackground="@drawable/picker_tab_background"
+ app:tabIndicatorAnimationMode="linear"
+ app:tabIndicatorColor="?attr/pickerSelectedTabBackgroundColor"
+ app:tabIndicatorGravity="center"
+ app:tabMinWidth="@dimen/picker_tab_min_width"
+ app:tabPaddingStart="@dimen/picker_tab_horizontal_gap"
+ app:tabPaddingEnd="@dimen/picker_tab_horizontal_gap"
+ app:tabRippleColor="@null"
+ app:tabSelectedTextColor="?attr/pickerSelectedTabTextColor"
+ app:tabTextAppearance="@style/PickerTabTextAppearance"
+ app:tabTextColor="?android:attr/textColorSecondary" />
+
+ </androidx.appcompat.widget.Toolbar>
+
+ </com.google.android.material.appbar.AppBarLayout>
+
+ <FrameLayout
+ android:id="@+id/picker_bottom_bar"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/picker_bottom_bar_size"
+ android:layout_gravity="bottom"
+ android:background="@color/picker_background_color"
+ android:elevation="@dimen/picker_bottom_bar_elevation"
+ android:visibility="gone">
+
+ <Button
+ android:id="@+id/button_view_selected"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="@dimen/picker_bottom_bar_horizontal_gap"
+ android:layout_gravity="start|center_vertical"
+ android:paddingVertical="@dimen/picker_bottom_bar_buttons_vertical_gap"
+ android:text="@string/picker_view_selected"
+ android:textAllCaps="false"
+ android:textColor="?attr/pickerSelectedColor"
+ app:icon="@drawable/ic_collections"
+ app:iconPadding="@dimen/picker_viewselected_icon_padding"
+ app:iconSize="@dimen/picker_viewselected_icon_size"
+ app:iconTint="?attr/pickerSelectedColor"
+ style="@style/MaterialBorderlessButtonStyle"/>
+
+ <Button
+ android:id="@+id/button_add"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="@dimen/picker_bottom_bar_horizontal_gap"
+ android:layout_gravity="end|center_vertical"
+ android:paddingVertical="@dimen/picker_bottom_bar_buttons_vertical_gap"
+ android:text="@string/add"
+ android:textAllCaps="false"
+ android:textColor="?attr/pickerHighlightTextColor"
+ android:backgroundTint="?attr/pickerHighlightColor"
+ style="@style/MaterialButtonStyle"/>
+
+ </FrameLayout>
+
+ </FrameLayout>
+
+ </LinearLayout>
+
+ <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
+ android:id="@+id/profile_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/picker_profile_button_margin_bottom"
+ android:layout_gravity="bottom|center"
+ android:textAppearance="@style/PickerButtonTextAppearance"
+ android:textColor="?attr/pickerProfileButtonTextColor"
+ android:text="@string/picker_work_profile"
+ android:visibility="gone"
+ android:accessibilityTraversalAfter="@+id/toolbar"
+ android:accessibilityTraversalBefore="@+id/fragment_container"
+ app:backgroundTint="?attr/pickerProfileButtonColor"
+ app:borderWidth="0dp"
+ app:elevation="3dp"
+ app:icon="@drawable/ic_work_outline"/>
+
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/res/layout/fragment_picker_tab.xml b/res/layout/fragment_picker_tab.xml
new file mode 100644
index 0000000..ae3180d
--- /dev/null
+++ b/res/layout/fragment_picker_tab.xml
@@ -0,0 +1,60 @@
+<?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.
+ -->
+<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">
+
+ <LinearLayout
+ android:id="@android:id/empty"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="80dp"
+ android:orientation="vertical"
+ android:visibility="gone">
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_artwork_camera"
+ android:contentDescription="@null"/>
+
+ <TextView
+ android:id="@+id/empty_text_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/picker_empty_text_margin"
+ android:gravity="center_horizontal"
+ android:text="@string/picker_photos_empty_message"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="@dimen/picker_empty_text_size"
+ style="?android:attr/textAppearanceListItem"/>
+
+ </LinearLayout>
+
+ <com.android.providers.media.photopicker.ui.AutoFitRecyclerView
+ android:id="@+id/picker_tab_recyclerview"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false"
+ android:drawSelectorOnTop="true"
+ android:overScrollMode="never"/>
+
+</FrameLayout>
diff --git a/res/layout/fragment_picker_tab_container.xml b/res/layout/fragment_picker_tab_container.xml
new file mode 100644
index 0000000..41971b8
--- /dev/null
+++ b/res/layout/fragment_picker_tab_container.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+<androidx.viewpager2.widget.ViewPager2
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/picker_tab_viewpager"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
diff --git a/res/layout/fragment_preview.xml b/res/layout/fragment_preview.xml
new file mode 100644
index 0000000..7010580
--- /dev/null
+++ b/res/layout/fragment_preview.xml
@@ -0,0 +1,99 @@
+<!--
+ ~ 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.
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:background="@color/preview_background_color"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <androidx.viewpager2.widget.ViewPager2
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/preview_viewPager"
+ android:layout_gravity="center"
+ android:accessibilityTraversalBefore="@+id/toolbar"/>
+
+ <!-- Adds scrim for Toolbar -->
+ <FrameLayout
+ android:id="@+id/preview_top_scrim"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/preview_toolbar_scrim_height"
+ android:background="@drawable/preview_gradient_desc"
+ android:layout_gravity="top"
+ android:visibility="gone"/>
+
+ <!-- Adds scrim for deselect and Add button -->
+ <FrameLayout
+ android:id="@+id/preview_bottom_scrim"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/preview_deselect_scrim_height"
+ android:background="@drawable/preview_gradient_asc"
+ android:layout_gravity="bottom"
+ android:visibility="gone"/>
+
+ <FrameLayout
+ android:id="@+id/preview_bottom_bar"
+ android:layout_gravity="bottom"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/picker_bottom_bar_size"
+ android:paddingHorizontal="@dimen/preview_buttons_padding_horizontal"
+ android:importantForAccessibility="yes"
+ android:accessibilityTraversalAfter="@+id/toolbar">
+
+ <!-- Buttons for Preview on Long press. Visible by default -->
+ <Button
+ android:id="@+id/preview_add_or_select_button"
+ android:layout_width="@dimen/preview_add_or_select_width"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:backgroundTint="@color/preview_highlight_color"
+ android:paddingVertical="@dimen/picker_bottom_bar_buttons_vertical_gap"
+ android:text="@string/add"
+ android:textAllCaps="false"
+ android:textColor="@color/preview_default_grey"
+ style="@style/MaterialButtonStyle"/>
+
+ <!-- Buttons for Preview on View Selected. Hidden by default -->
+ <Button
+ android:id="@+id/preview_selected_check_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start|center_vertical"
+ android:paddingStart="@dimen/preview_deselect_padding_start"
+ android:paddingVertical="@dimen/picker_bottom_bar_buttons_vertical_gap"
+ android:background="@android:color/transparent"
+ android:drawableLeft="@drawable/preview_check"
+ android:drawableTint="@color/preview_highlight_color"
+ android:textAllCaps="false"
+ android:text="@string/selected"
+ android:textColor="@color/picker_default_white"
+ android:visibility="gone"
+ style="@style/MaterialBorderlessButtonStyle"/>
+
+ <Button
+ android:id="@+id/preview_add_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end|center_vertical"
+ android:backgroundTint="@color/preview_highlight_color"
+ android:paddingVertical="@dimen/picker_bottom_bar_buttons_vertical_gap"
+ android:text="@string/add"
+ android:textAllCaps="false"
+ android:textColor="@color/preview_default_grey"
+ android:visibility="gone"
+ style="@style/MaterialButtonStyle"/>
+ </FrameLayout>
+</FrameLayout>
diff --git a/res/layout/item_album_grid.xml b/res/layout/item_album_grid.xml
new file mode 100644
index 0000000..66b2fa7
--- /dev/null
+++ b/res/layout/item_album_grid.xml
@@ -0,0 +1,60 @@
+<?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.
+-->
+
+<LinearLayout
+ 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="wrap_content"
+ android:orientation="vertical"
+ android:focusable="true">
+
+ <com.google.android.material.card.MaterialCardView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:elevation="0dp"
+ android:duplicateParentState="true"
+ app:cardCornerRadius="@dimen/picker_album_grid_radius"
+ app:cardElevation="0dp"
+ app:strokeWidth="0dp">
+
+ <com.android.providers.media.photopicker.ui.SquareImageView
+ android:id="@+id/icon_thumbnail"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="centerCrop"
+ android:contentDescription="@null"/>
+
+ </com.google.android.material.card.MaterialCardView>
+
+ <TextView
+ android:id="@+id/album_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/picker_album_name_min_height"
+ android:layout_marginTop="@dimen/picker_album_name_margin"
+ android:textAppearance="@style/PickerHeaderTextAppearance"/>
+
+ <TextView
+ android:id="@+id/item_count"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/picker_album_item_count_height"
+ android:layout_marginTop="@dimen/picker_album_item_count_margin"
+ android:textAppearance="@android:style/TextAppearance.DeviceDefault.Small"
+ android:textColor="?android:attr/textColorSecondary"/>
+
+</LinearLayout>
diff --git a/res/layout/item_cloud_video_preview.xml b/res/layout/item_cloud_video_preview.xml
new file mode 100644
index 0000000..f8c3b6e
--- /dev/null
+++ b/res/layout/item_cloud_video_preview.xml
@@ -0,0 +1,61 @@
+<?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_date_header.xml b/res/layout/item_date_header.xml
new file mode 100644
index 0000000..d3a931a
--- /dev/null
+++ b/res/layout/item_date_header.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 20121 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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/date_header_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/picker_date_header_height"
+ android:padding="@dimen/picker_date_header_padding"
+ android:textAppearance="@style/PickerHeaderTextAppearance"/>
diff --git a/res/layout/item_image_preview.xml b/res/layout/item_image_preview.xml
new file mode 100644
index 0000000..932ce58
--- /dev/null
+++ b/res/layout/item_image_preview.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+ <ImageView
+ android:id="@+id/preview_imageView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:scaleType="fitCenter"
+ android:contentDescription="@null"/>
+</LinearLayout>
diff --git a/res/layout/item_photo_grid.xml b/res/layout/item_photo_grid.xml
new file mode 100644
index 0000000..f28305c
--- /dev/null
+++ b/res/layout/item_photo_grid.xml
@@ -0,0 +1,111 @@
+<?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.
+-->
+
+<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="wrap_content"
+ android:focusable="true">
+
+ <com.google.android.material.card.MaterialCardView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:elevation="0dp"
+ android:duplicateParentState="true"
+ app:cardElevation="0dp"
+ app:cardCornerRadius="0dp"
+ app:strokeWidth="0dp">
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <com.android.providers.media.photopicker.ui.SquareImageView
+ android:id="@+id/icon_thumbnail"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="centerCrop"
+ android:contentDescription="@null"/>
+
+ <FrameLayout
+ android:id="@+id/overlay_gradient"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/picker_item_gradient_height"
+ android:background="@drawable/picker_item_gradient"/>
+
+ <ImageView
+ android:id="@+id/icon_gif"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end|top"
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_gif"
+ android:contentDescription="@null"/>
+
+ <ImageView
+ android:id="@+id/icon_motion_photo"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end|top"
+ android:layout_marginEnd="@dimen/picker_item_badge_margin"
+ android:layout_marginTop="@dimen/picker_item_badge_margin"
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_motion_photo"
+ android:contentDescription="@null"/>
+
+ <LinearLayout
+ android:id="@+id/video_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end|top"
+ android:layout_marginEnd="@dimen/picker_item_badge_margin"
+ android:layout_marginTop="@dimen/picker_item_badge_margin"
+ android:orientation="horizontal"
+ android:contentDescription="@null">
+
+ <TextView
+ android:id="@+id/video_duration"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/picker_item_badge_text_margin"
+ android:layout_gravity="center_vertical"
+ android:textColor="@android:color/white"
+ android:textSize="@dimen/picker_item_badge_text_size"/>
+
+ <ImageView
+ android:id="@+id/icon_video"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_play_circle_filled"
+ android:contentDescription="@null"/>
+ </LinearLayout>
+ </FrameLayout>
+
+ </com.google.android.material.card.MaterialCardView>
+
+ <ImageView
+ android:id="@+id/icon_check"
+ android:layout_height="@dimen/picker_item_check_size"
+ android:layout_width="@dimen/picker_item_check_size"
+ android:layout_marginStart="@dimen/picker_item_check_margin"
+ android:layout_marginTop="@dimen/picker_item_check_margin"
+ android:src="@drawable/picker_item_check"
+ android:layout_gravity="top|start"
+ android:scaleType="fitCenter"/>
+
+</FrameLayout>
diff --git a/res/layout/item_video_preview.xml b/res/layout/item_video_preview.xml
new file mode 100644
index 0000000..1d7d32d
--- /dev/null
+++ b/res/layout/item_video_preview.xml
@@ -0,0 +1,42 @@
+<?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.
+-->
+
+<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"
+ 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"/>
+
+ <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/photo_picker.xml b/res/layout/photo_picker.xml
deleted file mode 100644
index 073a8bd..0000000
--- a/res/layout/photo_picker.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2020 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.
- -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context=".MainActivity">
-
- <Button
- android:id="@+id/button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Give up"
- />
-
- <ListView
- android:id="@+id/names_list"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- />
-
-</LinearLayout>
diff --git a/res/layout/preview_gif.xml b/res/layout/preview_gif.xml
new file mode 100644
index 0000000..1512184
--- /dev/null
+++ b/res/layout/preview_gif.xml
@@ -0,0 +1,30 @@
+<!--
+ ~ 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.
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingEnd="@dimen/preview_special_format_padding_end" >
+
+ <ImageView
+ android:id="@+id/icon_gif"
+ android:layout_width="@dimen/preview_gif_icon_size"
+ android:layout_height="@dimen/preview_gif_icon_size"
+ android:adjustViewBounds="true"
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_gif"
+ android:contentDescription="@null" />
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/preview_motion_photo.xml b/res/layout/preview_motion_photo.xml
new file mode 100644
index 0000000..4d46bc4
--- /dev/null
+++ b/res/layout/preview_motion_photo.xml
@@ -0,0 +1,39 @@
+<!--
+ ~ 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingEnd="@dimen/preview_special_format_padding_end" >
+
+ <ImageView
+ android:id="@+id/icon_motion_photo"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_motion_photo"
+ android:contentDescription="@null" />
+
+ <TextView
+ android:id="@+id/text_motion_photo"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/preview_special_format_text_margin"
+ android:layout_gravity="center_vertical"
+ android:textColor="@color/picker_default_white"
+ android:textSize="@dimen/preview_special_format_text_size"
+ android:text="@string/picker_motion_photo_text" />
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/preview_video_controls.xml b/res/layout/preview_video_controls.xml
new file mode 100644
index 0000000..39bddbe
--- /dev/null
+++ b/res/layout/preview_video_controls.xml
@@ -0,0 +1,40 @@
+<!--
+ ~ 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.
+ -->
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <FrameLayout
+ android:id="@id/exo_center_controls"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:background="@android:color/transparent"
+ android:gravity="center"
+ android:padding="@dimen/exo_styled_controls_padding"
+ android:clipToPadding="false">
+
+ <ImageButton android:id="@id/exo_play_pause"
+ style="@style/ExoStyledControls.Button.Center.PlayPause"/>
+ </FrameLayout>
+
+ <ImageButton android:id="@+id/preview_mute"
+ android:layout_height="@dimen/preview_mute_button_size"
+ android:layout_width="@dimen/preview_mute_button_size"
+ android:gravity="center"
+ android:src="@drawable/ic_volume_off"
+ android:layout_gravity="end|bottom"
+ android:layout_marginEnd="@dimen/preview_mute_marginEnd"
+ android:layout_marginBottom="@dimen/preview_mute_marginBottom"
+ android:background="@android:color/transparent" />
+</merge>
\ No newline at end of file
diff --git a/res/menu/picker_preview_menu.xml b/res/menu/picker_preview_menu.xml
new file mode 100644
index 0000000..5381358
--- /dev/null
+++ b/res/menu/picker_preview_menu.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+<!-- special format badges and text in preview screen -->
+<menu
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <item
+ android:id="@+id/preview_gif"
+ android:title="@null"
+ app:showAsAction="always"
+ app:actionLayout="@layout/preview_gif"/>
+ <item
+ android:id="@+id/preview_motion_photo"
+ android:title="@null"
+ app:showAsAction="always"
+ app:actionLayout="@layout/preview_motion_photo"/>
+</menu>
\ No newline at end of file
diff --git a/res/mipmap-hdpi/ic_launcher_gallery.png b/res/mipmap-hdpi/ic_launcher_gallery.png
deleted file mode 100644
index 23ea998..0000000
--- a/res/mipmap-hdpi/ic_launcher_gallery.png
+++ /dev/null
Binary files differ
diff --git a/res/mipmap-hdpi/picker_app_icon.png b/res/mipmap-hdpi/picker_app_icon.png
new file mode 100644
index 0000000..c911ffc
--- /dev/null
+++ b/res/mipmap-hdpi/picker_app_icon.png
Binary files differ
diff --git a/res/mipmap-mdpi/ic_launcher_gallery.png b/res/mipmap-mdpi/ic_launcher_gallery.png
deleted file mode 100644
index e1a9949..0000000
--- a/res/mipmap-mdpi/ic_launcher_gallery.png
+++ /dev/null
Binary files differ
diff --git a/res/mipmap-mdpi/picker_app_icon.png b/res/mipmap-mdpi/picker_app_icon.png
new file mode 100644
index 0000000..9a0f98a
--- /dev/null
+++ b/res/mipmap-mdpi/picker_app_icon.png
Binary files differ
diff --git a/res/mipmap-xhdpi/ic_launcher_gallery.png b/res/mipmap-xhdpi/ic_launcher_gallery.png
deleted file mode 100644
index 79544a2..0000000
--- a/res/mipmap-xhdpi/ic_launcher_gallery.png
+++ /dev/null
Binary files differ
diff --git a/res/mipmap-xhdpi/picker_app_icon.png b/res/mipmap-xhdpi/picker_app_icon.png
new file mode 100644
index 0000000..ca55edd
--- /dev/null
+++ 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
new file mode 100644
index 0000000..531c94f
--- /dev/null
+++ 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
new file mode 100644
index 0000000..ce72805
--- /dev/null
+++ 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 46d36bf..9eef54e 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Fotokieser"</string>
<string name="artist_label" msgid="8105600993099120273">"Kunstenaar"</string>
<string name="unknown" msgid="2059049215682829375">"Onbekend"</string>
<string name="root_images" msgid="5861633549189045666">"Prente"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Gaan voort"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Laat toe"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Weier"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Plus <xliff:g id="COUNT_1">^1</xliff:g> bykomende items</item>
- <item quantity="one">Plus <xliff:g id="COUNT_0">^1</xliff:g> bykomende item</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Plus <xliff:g id="COUNT_0">^1</xliff:g> bykomende item}other{Plus <xliff:g id="COUNT_1">^1</xliff:g> bykomende items}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Vee tydelike programlêers uit"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> wil \'n paar tydelike lêers uitvee. Dit kan verhoogde batterygebruik of sellulêre data tot gevolg hê."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Vee tans tydelike programlêers uit …"</string>
<string name="clear" msgid="5524638938415865915">"Vee uit"</string>
<string name="allow" msgid="8885707816848569619">"Laat toe"</string>
<string name="deny" msgid="6040983710442068936">"Weier"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> oudiolêers te wysig?</item>
- <item quantity="one">Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie oudiolêer te wysig?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">Wysig tans <xliff:g id="COUNT">^1</xliff:g> oudiolêers …</item>
- <item quantity="one">Wysig tans oudiolêer …</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> video\'s te wysig?</item>
- <item quantity="one">Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie video te wysig?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">Wysig tans <xliff:g id="COUNT">^1</xliff:g> video\'s …</item>
- <item quantity="one">Wysig tans video …</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> foto\'s te wysig?</item>
- <item quantity="one">Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie foto te wysig?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">Wysig tans <xliff:g id="COUNT">^1</xliff:g> foto\'s …</item>
- <item quantity="one">Wysig tans foto …</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> items te wysig?</item>
- <item quantity="one">Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie item te wysig?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">Wysig tans <xliff:g id="COUNT">^1</xliff:g> items …</item>
- <item quantity="one">Wysig tans item …</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> oudiolêers na die asblik toe te skuif?</item>
- <item quantity="one">Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie oudiolêer na die asblik toe te skuif?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">Skuif tans <xliff:g id="COUNT">^1</xliff:g> oudiolêers na asblik …</item>
- <item quantity="one">Skuif tans oudiolêer na asblik …</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> video\'s na die asblik toe te skuif?</item>
- <item quantity="one">Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie video na die asblik toe skuif?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">Skuif tans <xliff:g id="COUNT">^1</xliff:g> video\'s na asblik …</item>
- <item quantity="one">Skuif tans video na asblik …</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> foto\'s na die asblik toe te skuif?</item>
- <item quantity="one">Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie foto na die asblik toe skuif?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">Skuif tans <xliff:g id="COUNT">^1</xliff:g> foto\'s na asblik …</item>
- <item quantity="one">Skuif tans foto na asblik …</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> items na die asblik toe te skuif?</item>
- <item quantity="one">Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie item na die asblik toe skuif?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">Skuif tans <xliff:g id="COUNT">^1</xliff:g> items na asblik …</item>
- <item quantity="one">Skuif tans item na asblik …</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> oudiolêers uit die asblik uit te skuif?</item>
- <item quantity="one">Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie oudiolêer uit die asblik uit te skuif?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">Skuif tans <xliff:g id="COUNT">^1</xliff:g> oudiolêers uit die asblik uit …</item>
- <item quantity="one">Skuif tans oudiolêer uit die asblik uit …</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> video\'s uit die asblik uit te skuif?</item>
- <item quantity="one">Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie video uit die asblik uit te skuif?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">Skuif tans <xliff:g id="COUNT">^1</xliff:g> video\'s uit die asblik uit …</item>
- <item quantity="one">Skuif tans video uit die asblik uit …</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> foto\'s uit die asblik uit te skuif?</item>
- <item quantity="one">Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie foto uit die asblik uit te skuif?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">Skuif tans <xliff:g id="COUNT">^1</xliff:g> foto\'s uit die asblik uit …</item>
- <item quantity="one">Skuif tans foto uit die asblik uit …</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> items uit die asblik uit te skuif?</item>
- <item quantity="one">Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie item uit die asblik uit te skuif?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">Skuif tans <xliff:g id="COUNT">^1</xliff:g> items uit die asblik uit …</item>
- <item quantity="one">Skuif tans item uit die asblik uit …</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> oudiolêers uit te vee?</item>
- <item quantity="one">Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie oudiolêer uit te vee?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">Vee tans <xliff:g id="COUNT">^1</xliff:g> oudiolêers uit …</item>
- <item quantity="one">Vee tans oudiolêer uit</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> video\'s uit te vee?</item>
- <item quantity="one">Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie video uit te vee?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">Vee tans <xliff:g id="COUNT">^1</xliff:g> video\'s uit …</item>
- <item quantity="one">Vee tans video uit …</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> foto\'s uit te vee?</item>
- <item quantity="one">Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie foto uit te vee?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">Vee tans <xliff:g id="COUNT">^1</xliff:g> foto\'s uit …</item>
- <item quantity="one">Vee tans foto uit …</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> items uit te vee?</item>
- <item quantity="one">Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie item uit te vee?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">Vee tans <xliff:g id="COUNT">^1</xliff:g> items uit …</item>
- <item quantity="one">Vee tans item uit …</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Voeg by"</string>
+ <string name="deselect" msgid="4297825044827769490">"Ontkies"</string>
+ <string name="deselected" msgid="8488133193326208475">"Ontkies"</string>
+ <string name="select" msgid="2704765470563027689">"Kies"</string>
+ <string name="selected" msgid="9151797369975828124">"Gekies"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Kies tot <xliff:g id="COUNT_0">^1</xliff:g> item}other{Kies tot <xliff:g id="COUNT_1">^1</xliff:g> items}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Onlangs"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Geen foto\'s of video\'s nie"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Geen albums nie"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Bekyk geselekteerde"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Foto\'s"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albums"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Voorskou"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Skakel oor na werk"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Skakel oor na persoonlik"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Deur jou admin geblokkeer"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Toegang tot werkdata van \'n persoonlike program af word nie toegelaat nie"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Toegang tot persoonlike data van \'n werkprogram af word nie toegelaat nie"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Werkprogramme is onderbreek"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Om werkfoto\'s oop te maak, moet jy jou werkprogramme aanskakel en weer probeer"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Hierdie program het net toegang tot die foto\'s wat jy kies"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}other{<xliff:g id="COUNT_1">^1</xliff:g> items}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Voeg by (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Aflaaie"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Gunstelinge"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Skermskote"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Bewegingfoto"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> is op <xliff:g id="TIME">%2$s</xliff:g> geneem"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Video geneem op <xliff:g id="TIME">%1$s</xliff:g> se tydsduur is <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Foto"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Bewegingfoto"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Demp video"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Ontdemp video"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Speel video"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Onderbreek video"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Wolkmedia is nou deur <xliff:g id="PKG_NAME">%1$s</xliff:g> beskikbaar"</string>
+ <string name="not_selected" msgid="2244008151669896758">"nie gekies nie"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie oudiolêer te wysig?}other{Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> oudiolêers te wysig?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Wysig tans oudiolêer …}other{Wysig tans <xliff:g id="COUNT">^1</xliff:g> oudiolêers …}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie video te wysig?}other{Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> video\'s te wysig?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Wysig tans video …}other{Wysig tans <xliff:g id="COUNT">^1</xliff:g> video\'s …}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie foto te wysig?}other{Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> foto\'s te wysig?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Wysig tans foto …}other{Wysig tans <xliff:g id="COUNT">^1</xliff:g> foto\'s …}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie item te wysig?}other{Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> items te wysig?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Wysig tans item …}other{Wysig tans <xliff:g id="COUNT">^1</xliff:g> items …}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie oudiolêer na die asblik toe te skuif?}other{Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> oudiolêers na die asblik toe te skuif?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Skuif tans oudiolêer na asblik …}other{Skuif tans <xliff:g id="COUNT">^1</xliff:g> oudiolêers na asblik …}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie video na die asblik toe skuif?}other{Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> video\'s na die asblik toe te skuif?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Skuif tans video na asblik …}other{Skuif tans <xliff:g id="COUNT">^1</xliff:g> video\'s na asblik …}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie foto na die asblik toe skuif?}other{Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> foto\'s na die asblik toe te skuif?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Skuif tans foto na asblik …}other{Skuif tans <xliff:g id="COUNT">^1</xliff:g> foto\'s na asblik …}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie item na die asblik toe skuif?}other{Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> items na die asblik toe te skuif?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Skuif tans item na asblik …}other{Skuif tans <xliff:g id="COUNT">^1</xliff:g> items na asblik …}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie oudiolêer uit die asblik uit te skuif?}other{Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> oudiolêers uit die asblik uit te skuif?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Skuif tans oudiolêer uit die asblik uit …}other{Skuif tans <xliff:g id="COUNT">^1</xliff:g> oudiolêers uit die asblik uit …}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie video uit die asblik uit te skuif?}other{Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> video\'s uit die asblik uit te skuif?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Skuif tans video uit die asblik uit …}other{Skuif tans <xliff:g id="COUNT">^1</xliff:g> video\'s uit die asblik uit …}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie foto uit die asblik uit te skuif?}other{Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> foto\'s uit die asblik uit te skuif?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Skuif tans foto uit die asblik uit …}other{Skuif tans <xliff:g id="COUNT">^1</xliff:g> foto\'s uit die asblik uit …}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie item uit die asblik uit te skuif?}other{Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> items uit die asblik uit te skuif?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Skuif tans item uit die asblik uit …}other{Skuif tans <xliff:g id="COUNT">^1</xliff:g> items uit die asblik uit …}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie oudiolêer uit te vee?}other{Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> oudiolêers uit te vee?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Vee tans oudiolêer uit …}other{Vee tans <xliff:g id="COUNT">^1</xliff:g> oudiolêers uit …}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie video uit te vee?}other{Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> video\'s uit te vee?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Vee tans video uit …}other{Vee tans <xliff:g id="COUNT">^1</xliff:g> video\'s uit …}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie foto uit te vee?}other{Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> foto\'s uit te vee?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Vee tans foto uit …}other{Vee tans <xliff:g id="COUNT">^1</xliff:g> foto\'s uit …}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Laat <xliff:g id="APP_NAME_0">^1</xliff:g> toe om hierdie item uit te vee?}other{Laat <xliff:g id="APP_NAME_1">^1</xliff:g> toe om <xliff:g id="COUNT">^2</xliff:g> items uit te vee?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Vee tans item uit …}other{Vee tans <xliff:g id="COUNT">^1</xliff:g> items uit …}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> kan nie medialêers verwerk nie"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Mediaverwerking is gekanselleer"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Mediaverwerkingfout"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index fb69fe1..d99aa0b 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"ማህደረመረጃ"</string>
<string name="storage_description" msgid="4081716890357580107">"አካባቢያዊ ማከማቻ"</string>
<string name="app_label" msgid="9035307001052716210">"ማህደረ መረጃ ማከማቻ"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"ፎቶ መልቀሚያ"</string>
<string name="artist_label" msgid="8105600993099120273">"አርቲስት"</string>
<string name="unknown" msgid="2059049215682829375">"የማይታወቅ"</string>
<string name="root_images" msgid="5861633549189045666">"ምስሎች"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"ቀጥል"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"ፍቀድ"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"ከልክል"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">እንዲሁም <xliff:g id="COUNT_1">^1</xliff:g> ተጨማሪ ንጥሎች</item>
- <item quantity="other">እንዲሁም <xliff:g id="COUNT_1">^1</xliff:g> ተጨማሪ ንጥሎች</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}one{+<xliff:g id="COUNT_1">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{እንዲሁም <xliff:g id="COUNT_0">^1</xliff:g> ተጨማሪ ንጥል}one{እንዲሁም <xliff:g id="COUNT_1">^1</xliff:g> ተጨማሪ ንጥል}other{እንዲሁም <xliff:g id="COUNT_1">^1</xliff:g> ተጨማሪ ንጥሎች}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"ጊዜያዊ የመተግበሪያ ፋይሎች ይጸዱ?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> አንዳንድ ጊዜያዊ ፋይሎችን ማጽዳት ይፈልጋል። ይህ የበለጠ የባትሪ ኃይል ወይም የተንቀሳቃሽ ስልክ ውሂብ ፍጆታን ሊጨምር ይችላል።"</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"ጊዜያዊ የመተግበሪያ ፋይሎችን በማጽዳት ላይ…"</string>
<string name="clear" msgid="5524638938415865915">"አጽዳ"</string>
<string name="allow" msgid="8885707816848569619">"ፍቀድ"</string>
<string name="deny" msgid="6040983710442068936">"ከልክል"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ኦዲዮ ፋይሎችን እንዲቀይር ይፈቀድለት?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ኦዲዮ ፋይሎችን እንዲቀይር ይፈቀድለት?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> የኦዲዮ ፋይሎችን በመቀየር ላይ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> የኦዲዮ ፋይሎችን በመቀየር ላይ…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ቪዲዮዎችን እንዲቀይር ይፈቀድለት?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ቪዲዮዎችን እንዲቀይር ይፈቀድለት?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ቪዲዮዎችን በመቀየር ላይ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ቪዲዮዎችን በመቀየር ላይ…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ፎቶዎችን እንዲቀይር ይፈቀድለት?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ፎቶዎችን እንዲቀይር ይፈቀድለት?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ፎቶዎችን በመቀየር ላይ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ፎቶዎችን በመቀየር ላይ…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ንጥሎችን እንዲቀይር ይፈቀድለት?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ንጥሎችን እንዲቀይር ይፈቀድለት?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ንጥሎችን በመቀየር ላይ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ንጥሎችን በመቀየር ላይ…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ኦዲዮ ፋይሎችን ወደ መጣያ እንዲወስድ ይፈቀድለት?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ኦዲዮ ፋይሎችን ወደ መጣያ እንዲወስድ ይፈቀድለት?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> የድምጽ ፋይሎችን ወደ መጣያ በመውሰድ ላይ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> የድምጽ ፋይሎችን ወደ መጣያ በመውሰድ ላይ…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ቪዲዮዎችን ወደ መጣያ እንዲወስድ ይፈቀድለት?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ቪዲዮዎችን ወደ መጣያ እንዲወስድ ይፈቀድለት?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ቪዱዮዎችን ወደ መጣያ በመውሰድ ላይ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ቪዱዮዎችን ወደ መጣያ በመውሰድ ላይ…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ፎቶዎችን ወደ መጣያ እንዲወስድ ይፈቀድለት?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ፎቶዎችን ወደ መጣያ እንዲወስድ ይፈቀድለት?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ፎቶዎችን ወደ መጣያ በመውሰድ ላይ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ፎቶዎችን ወደ መጣያ በመውሰድ ላይ…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ንጥሎችን ወደ መጣያ እንዲወስድ ይፈቀድለት?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ንጥሎችን ወደ መጣያ እንዲወስድ ይፈቀድለት?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ንጥሎችን ወደ መጣያ በመውሰድ ላይ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ንጥሎችን ወደ መጣያ በመውሰድ ላይ…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ኦዲዮ ፋይሎችን ከመጣያ እንዲያስወጣ ይፈቀድለት?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ኦዲዮ ፋይሎችን ከመጣያ እንዲያስወጣ ይፈቀድለት?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> የኦዲዮ ፋይሎችን ከመጣያ በማስወጣት ላይ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> የኦዲዮ ፋይሎችን ከመጣያ በማስወጣት ላይ…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ቪዲዮዎችን ከመጣያ እንዲያስወጣ ይፈቀድለት?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ቪዲዮዎችን ከመጣያ እንዲያስወጣ ይፈቀድለት?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ቪዲዮዎችን ከመጣያ በማስወጣት ላይ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ቪዲዮዎችን ከመጣያ በማስወጣት ላይ…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ፎቶዎችን ከመጣያ እንዲያስወጣ ይፈቀድለት?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ፎቶዎችን ከመጣያ እንዲያስወጣ ይፈቀድለት?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ፎቶዎችን ከመጣያ በማስወጣት ላይ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ፎቶዎችን ከመጣያ በማስወጣት ላይ…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ንጥሎችን ከመጣያ እንዲያስወጣ ይፈቀድለት?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ንጥሎችን ከመጣያ እንዲያስወጣ ይፈቀድለት?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ንጥሎችን ከመጣያ በማስወጣት ላይ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ንጥሎችን ከመጣያ በማስወጣት ላይ…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> የኦዲዮ ፋይሎችን እንዲሰረዝ ይፈቀድለት?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> የኦዲዮ ፋይሎችን እንዲሰረዝ ይፈቀድለት?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> የኦዲዮ ፋይሎችን በመሰረዝ ላይ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> የኦዲዮ ፋይሎችን በመሰረዝ ላይ…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ቪዲዮዎችን እንዲሰረዝ ይፈቀድለት?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ቪዲዮዎችን እንዲሰረዝ ይፈቀድለት?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ቪዲዮዎችን በመሰረዝ ላይ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ቪዲዮዎችን በመሰረዝ ላይ…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ፎቶዎችን እንዲሰረዝ ይፈቀድለት?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ፎቶዎችን እንዲሰረዝ ይፈቀድለት?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ፎቶዎችን በመሰረዝ ላይ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ፎቶዎችን በመሰረዝ ላይ…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ንጥሎችን እንዲሰረዝ ይፈቀድለት?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ንጥሎችን እንዲሰረዝ ይፈቀድለት?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ንጥሎችን በመሰረዝ ላይ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ንጥሎችን በመሰረዝ ላይ…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"አክል"</string>
+ <string name="deselect" msgid="4297825044827769490">"አትምረጥ"</string>
+ <string name="deselected" msgid="8488133193326208475">"አልተመረጠም"</string>
+ <string name="select" msgid="2704765470563027689">"ምረጥ"</string>
+ <string name="selected" msgid="9151797369975828124">"ተመርጧል"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{እስከ <xliff:g id="COUNT_0">^1</xliff:g> ንጥል ድረስ ይምረጡ}one{እስከ <xliff:g id="COUNT_1">^1</xliff:g> ንጥል ድረስ ይምረጡ}other{እስከ <xliff:g id="COUNT_1">^1</xliff:g> ንጥሎች ድረስ ይምረጡ}}"</string>
+ <string name="recent" msgid="6694613584743207874">"የቅርብ ጊዜ"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"ምንም ፎቶዎች ወይም ቪዲዮዎች የሉም"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"ምንም አልበሞች የሉም"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"የተመረጡትን አሳይ"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"ፎቶዎች"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"አልበሞች"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"ቅድመ-ዕይታ"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"ወደ የሥራ ቀይር"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"ወደ የግል ቀይር"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"በእርስዎ አስተዳዳሪ ታግዷል"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"የስራ ውሂብን ከግል መተግበሪያ መድረስ አይፈቀድም"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"የግል ውሂብን ከሥራ መተግበሪያ መድረስ አይፈቀድም"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"የስራ መተግበሪያዎች ባሉበት ቆመዋል"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"የሥራ ፎቶዎችን ለመክፈት የሥራ መተግበሪያዎችዎን ያብሩ እና እንደገና ይሞክሩ"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"ይህ መተግበሪያ መድረስ የሚችለው እርስዎ የሚመርጧቸውን ፎቶዎች ብቻ ነው"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> ንጥል}one{<xliff:g id="COUNT_1">^1</xliff:g> ንጥል}other{<xliff:g id="COUNT_1">^1</xliff:g> ንጥሎች}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"(<xliff:g id="COUNT">^1</xliff:g>) አክል"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"ካሜራ"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"ውርዶች"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"ተወዳጆች"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"ቅጽበታዊ ገጽ እይታዎች"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"የእንቅስቃሴ ፎቶ"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> <xliff:g id="TIME">%2$s</xliff:g> ላይ ተነስቷል"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"ቪዲዮ በ<xliff:g id="TIME">%1$s</xliff:g> ከ<xliff:g id="DURATION">%2$s</xliff:g> ቆይታ የተወሰደ"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"ፎቶ"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"ጂአይኤፍ"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"የእንቅስቃሴ ፎቶ"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"ቪዲዮ ላይ ድምፀ-ከል አድርግ"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"የቪዲዮን ድምፀ-ከል አንሣ"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"ቪድዮ አጫውት"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"ቪዲዮን ባለበት አቁም"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"የደመና ሚዲያ አሁን ከ<xliff:g id="PKG_NAME">%1$s</xliff:g> ይገኛል"</string>
+ <string name="not_selected" msgid="2244008151669896758">"አልተመረጠም"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ይህን ኦዲዮ ፋይል እንዲቀይር ይፈቀድለት?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ኦዲዮ ፋይልን እንዲቀይር ይፈቀድለት?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ኦዲዮ ፋይሎችን እንዲቀይር ይፈቀድለት?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{የኦዲዮ ፋይልን በመቀየር ላይ…}one{<xliff:g id="COUNT">^1</xliff:g> የኦዲዮ ፋይልን በመቀየር ላይ…}other{<xliff:g id="COUNT">^1</xliff:g> የኦዲዮ ፋይሎችን በመቀየር ላይ…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ይህን ቪዲዮ እንዲቀይር ይፈቀድለት?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ቪዲዮን እንዲቀይር ይፈቀድለት?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ቪዲዮዎችን እንዲቀይር ይፈቀድለት?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{ቪዲዮን በመቀየር ላይ…}one{<xliff:g id="COUNT">^1</xliff:g> ቪዲዮን በመቀየር ላይ…}other{<xliff:g id="COUNT">^1</xliff:g> ቪዲዮዎችን በመቀየር ላይ…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ይህን ፎቶ እንዲቀይር ይፈቀድለት?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ፎቶን እንዲቀይር ይፈቀድለት?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ፎቶዎችን እንዲቀይር ይፈቀድለት?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{ፎቶን በመቀየር ላይ…}one{<xliff:g id="COUNT">^1</xliff:g> ፎቶን በመቀየር ላይ…}other{<xliff:g id="COUNT">^1</xliff:g> ፎቶዎችን በመቀየር ላይ…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ይህን ንጥል እንዲቀይር ይፈቀድለት?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ንጥልን እንዲቀይር ይፈቀድለት?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ንጥሎችን እንዲቀይር ይፈቀድለት?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{ንጥልን በመቀየር ላይ…}one{<xliff:g id="COUNT">^1</xliff:g> ንጥልን በመቀየር ላይ…}other{<xliff:g id="COUNT">^1</xliff:g> ንጥሎችን በመቀየር ላይ…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ይህን ኦዲዮ ፋይል ወደ መጣያ እንዲወስድ ይፈቀድለት?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ኦዲዮ ፋይልን ወደ መጣያ እንዲወስድ ይፈቀድለት?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ኦዲዮ ፋይሎችን ወደ መጣያ እንዲወስድ ይፈቀድለት?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{የኦዲዮ ፋይልን ወደ መጣያ በመውሰድ ላይ…}one{<xliff:g id="COUNT">^1</xliff:g> የኦዲዮ ፋይልን ወደ መጣያ በመውሰድ ላይ…}other{<xliff:g id="COUNT">^1</xliff:g> የኦዲዮ ፋይሎችን ወደ መጣያ በመውሰድ ላይ…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ይህን ቪዲዮ ወደ መጣያ እንዲወስድ ይፈቀድለት?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ቪዲዮን ወደ መጣያ እንዲወስድ ይፈቀድለት?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ቪዲዮዎችን ወደ መጣያ እንዲወስድ ይፈቀድለት?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{ቪዲዮን ወደ መጣያ በመውሰድ ላይ…}one{<xliff:g id="COUNT">^1</xliff:g> ቪዲዮን ወደ መጣያ በመውሰድ ላይ…}other{<xliff:g id="COUNT">^1</xliff:g> ቪዲዮዎችን ወደ መጣያ በመውሰድ ላይ…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ይህን ፎቶ ወደ መጣያ እንዲወስድ ይፈቀድለት?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ፎቶን ወደ መጣያ እንዲወስድ ይፈቀድለት?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ፎቶዎችን ወደ መጣያ እንዲወስድ ይፈቀድለት?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{ፎቶን ወደ መጣያ በመውሰድ ላይ…}one{<xliff:g id="COUNT">^1</xliff:g> ፎቶን ወደ መጣያ በመውሰድ ላይ…}other{<xliff:g id="COUNT">^1</xliff:g> ፎቶዎችን ወደ መጣያ በመውሰድ ላይ…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ይህን ንጥል ወደ መጣያ እንዲወስድ ይፈቀድለት?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ንጥልን ወደ መጣያ እንዲወስድ ይፈቀድለት?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ንጥሎችን ወደ መጣያ እንዲወስድ ይፈቀድለት?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{ንጥልን ወደ መጣያ በመውሰድ ላይ…}one{<xliff:g id="COUNT">^1</xliff:g> ንጥልን ወደ መጣያ በመውሰድ ላይ…}other{<xliff:g id="COUNT">^1</xliff:g> ንጥሎችን ወደ መጣያ በመውሰድ ላይ…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ይህን ኦዲዮ ፋይል ከመጣያ እንዲያስወጣ ይፈቀድለት?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ኦዲዮ ፋይልን ከመጣያ እንዲያስወጣ ይፈቀድለት?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ኦዲዮ ፋይሎችን ከመጣያ እንዲያስወጣ ይፈቀድለት?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{የኦዲዮ ፋይልን ከመጣያ በማስወጣት ላይ…}one{<xliff:g id="COUNT">^1</xliff:g> የኦዲዮ ፋይልን ከመጣያ በማስወጣት ላይ…}other{<xliff:g id="COUNT">^1</xliff:g> የኦዲዮ ፋይሎችን ከመጣያ በማስወጣት ላይ…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ይህን ቪዲዮ ከመጣያ እንዲያስወጣ ይፈቀድለት?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ቪዲዮን ከመጣያ እንዲያስወጣ ይፈቀድለት?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ቪዲዮዎችን ከመጣያ እንዲያስወጣ ይፈቀድለት?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{ቪዲዮን ከመጣያ በማስወጣት ላይ…}one{<xliff:g id="COUNT">^1</xliff:g> ቪዲዮን ከመጣያ በማስወጣት ላይ…}other{<xliff:g id="COUNT">^1</xliff:g> ቪዲዮዎችን ከመጣያ በማስወጣት ላይ…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ይህን ፎቶ ከመጣያ እንዲያስወጣ ይፈቀድለት?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ፎቶን ከመጣያ እንዲያስወጣ ይፈቀድለት?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ፎቶዎችን ከመጣያ እንዲያስወጣ ይፈቀድለት?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{ፎቶን ከመጣያ በማስወጣት ላይ…}one{<xliff:g id="COUNT">^1</xliff:g> ፎቶን ከመጣያ በማስወጣት ላይ…}other{<xliff:g id="COUNT">^1</xliff:g> ፎቶዎችን ከመጣያ በማስወጣት ላይ…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ይህን ንጥል ከመጣያ እንዲያስወጣ ይፈቀድለት?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ንጥልን ከመጣያ እንዲያስወጣ ይፈቀድለት?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ንጥሎችን ከመጣያ እንዲያስወጣ ይፈቀድለት?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{ንጥልን ከመጣያ በማስወጣት ላይ…}one{<xliff:g id="COUNT">^1</xliff:g> ንጥልን ከመጣያ በማስወጣት ላይ…}other{<xliff:g id="COUNT">^1</xliff:g> ንጥሎችን ከመጣያ በማስወጣት ላይ…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ይህን የኦዲዮ ፋይል እንዲሰረዝ ይፈቀድለት?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> የኦዲዮ ፋይልን እንዲሰረዝ ይፈቀድለት?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> የኦዲዮ ፋይሎችን እንዲሰረዝ ይፈቀድለት?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{የኦዲዮ ፋይልን በመሰረዝ ላይ…}one{<xliff:g id="COUNT">^1</xliff:g> የኦዲዮ ፋይልን በመሰረዝ ላይ…}other{<xliff:g id="COUNT">^1</xliff:g> የኦዲዮ ፋይሎችን በመሰረዝ ላይ…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ይህን ቪዲዮ እንዲሰረዝ ይፈቀድለት?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ቪዲዮን እንዲሰረዝ ይፈቀድለት?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ቪዲዮዎችን እንዲሰረዝ ይፈቀድለት?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{ቪዲዮን በመሰረዝ ላይ…}one{<xliff:g id="COUNT">^1</xliff:g> ቪዲዮን በመሰረዝ ላይ…}other{<xliff:g id="COUNT">^1</xliff:g> ቪዲዮዎችን በመሰረዝ ላይ…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ይህን ፎቶ እንዲሰረዝ ይፈቀድለት?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ፎቶን እንዲሰረዝ ይፈቀድለት?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ፎቶዎችን እንዲሰረዝ ይፈቀድለት?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{ፎቶን በመሰረዝ ላይ…}one{<xliff:g id="COUNT">^1</xliff:g> ፎቶን በመሰረዝ ላይ…}other{<xliff:g id="COUNT">^1</xliff:g> ፎቶዎችን በመሰረዝ ላይ…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ይህን ንጥል እንዲሰረዝ ይፈቀድለት?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ንጥልን እንዲሰረዝ ይፈቀድለት?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ንጥሎችን እንዲሰረዝ ይፈቀድለት?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{ንጥልን በመሰረዝ ላይ…}one{<xliff:g id="COUNT">^1</xliff:g> ንጥልን በመሰረዝ ላይ…}other{<xliff:g id="COUNT">^1</xliff:g> ንጥሎችን በመሰረዝ ላይ…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> የሚዲያ ፋይሎችን ማሄድ አይችልም"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"ሚዲያን ማሰናዳት ተሰርዟል"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"ሚዲያን የማሰናዳት ስህተት"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 9fef177..cde7793 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"الوسائط"</string>
<string name="storage_description" msgid="4081716890357580107">"التخزين المحلي"</string>
<string name="app_label" msgid="9035307001052716210">"تخزين الوسائط"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"أداة اختيار الصور"</string>
<string name="artist_label" msgid="8105600993099120273">"الفنان"</string>
<string name="unknown" msgid="2059049215682829375">"غير معروف"</string>
<string name="root_images" msgid="5861633549189045666">"الصور"</string>
@@ -29,284 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"متابعة"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"سماح"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"رفض"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="zero">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="two">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="few">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="many">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="zero">و<xliff:g id="COUNT_1">^1</xliff:g> عنصر إضافي</item>
- <item quantity="two">وعنصران إضافيان (<xliff:g id="COUNT_1">^1</xliff:g>)</item>
- <item quantity="few">و<xliff:g id="COUNT_1">^1</xliff:g> عناصر إضافية</item>
- <item quantity="many">و<xliff:g id="COUNT_1">^1</xliff:g> عنصرًا إضافيًا</item>
- <item quantity="other">و<xliff:g id="COUNT_1">^1</xliff:g> عنصر إضافي</item>
- <item quantity="one">وعنصر إضافي واحد (<xliff:g id="COUNT_0">^1</xliff:g>)</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}zero{+<xliff:g id="COUNT_1">^1</xliff:g>}two{+<xliff:g id="COUNT_1">^1</xliff:g>}few{+<xliff:g id="COUNT_1">^1</xliff:g>}many{+<xliff:g id="COUNT_1">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{وعنصر إضافي واحد (<xliff:g id="COUNT_0">^1</xliff:g>)}zero{و<xliff:g id="COUNT_1">^1</xliff:g> عنصر إضافي}two{وعنصران إضافيان (<xliff:g id="COUNT_1">^1</xliff:g>)}few{و<xliff:g id="COUNT_1">^1</xliff:g> عناصر إضافية}many{و<xliff:g id="COUNT_1">^1</xliff:g> عنصرًا إضافيًا}other{و<xliff:g id="COUNT_1">^1</xliff:g> عنصر إضافي}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"محو ملفات التطبيق المؤقتة"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"يريد تطبيق <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> محو بعض الملفات المؤقتة. قد يؤدي هذا إلى زيادة استهلاك شحن البطارية أو بيانات شبكة الجوّال."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"جارٍ محو ملفات التطبيق المؤقتة…"</string>
<string name="clear" msgid="5524638938415865915">"محو"</string>
<string name="allow" msgid="8885707816848569619">"سماح"</string>
<string name="deny" msgid="6040983710442068936">"رفض"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="zero">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> ملف صوتي؟</item>
- <item quantity="two">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل ملفين صوتيين (<xliff:g id="COUNT">^2</xliff:g>)؟</item>
- <item quantity="few">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> ملفات صوتية؟</item>
- <item quantity="many">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> ملفًا صوتيًا؟</item>
- <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> ملف صوتي؟</item>
- <item quantity="one">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بتعديل هذا الملف الصوتي؟</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="zero">جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> ملف صوتي…</item>
- <item quantity="two">جارٍ تعديل ملفَين صوتين (<xliff:g id="COUNT">^1</xliff:g>)…</item>
- <item quantity="few">جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> ملفات صوتية…</item>
- <item quantity="many">جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> ملفًا صوتيًا…</item>
- <item quantity="other">جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> ملف صوتي…</item>
- <item quantity="one">جارٍ تعديل ملف صوتي واحد…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="zero">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> فيديو؟</item>
- <item quantity="two">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل فيديوهين (<xliff:g id="COUNT">^2</xliff:g>)؟</item>
- <item quantity="few">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> فيديوهات؟</item>
- <item quantity="many">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> فيديو؟</item>
- <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> فيديو؟</item>
- <item quantity="one">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بتعديل هذا الفيديو؟</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="zero">جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> فيديو…</item>
- <item quantity="two">جارٍ تعديل فيديوهين (<xliff:g id="COUNT">^1</xliff:g>)…</item>
- <item quantity="few">جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> فيديوهات…</item>
- <item quantity="many">جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> فيديو…</item>
- <item quantity="other">جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> فيديو…</item>
- <item quantity="one">جارٍ تعديل فيديو واحد…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="zero">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> صورة؟</item>
- <item quantity="two">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل صورتين (<xliff:g id="COUNT">^2</xliff:g>)؟</item>
- <item quantity="few">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> صور؟</item>
- <item quantity="many">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> صورة؟</item>
- <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> صورة؟</item>
- <item quantity="one">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بتعديل هذه الصورة؟</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="zero">جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> صورة…</item>
- <item quantity="two">جارٍ تعديل صورتين (<xliff:g id="COUNT">^1</xliff:g>)…</item>
- <item quantity="few">جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> صور…</item>
- <item quantity="many">جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> صورة…</item>
- <item quantity="other">جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> صورة…</item>
- <item quantity="one">جارٍ تعديل صورة واحدة…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="zero">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> عنصر؟</item>
- <item quantity="two">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل عنصرين (<xliff:g id="COUNT">^2</xliff:g>)؟</item>
- <item quantity="few">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> عناصر؟</item>
- <item quantity="many">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> عنصرًا؟</item>
- <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> عنصر؟</item>
- <item quantity="one">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بتعديل هذا العنصر؟</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="zero">جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> عنصر…</item>
- <item quantity="two">جارٍ تعديل عنصرين (<xliff:g id="COUNT">^1</xliff:g>)…</item>
- <item quantity="few">جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> عناصر…</item>
- <item quantity="many">جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> عنصرًا…</item>
- <item quantity="other">جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> عنصر…</item>
- <item quantity="one">جارٍ تعديل عنصر واحد…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="zero">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> ملف صوتي إلى المهملات؟</item>
- <item quantity="two">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل ملفين صوتيين (<xliff:g id="COUNT">^2</xliff:g>) إلى المهملات؟</item>
- <item quantity="few">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> ملفات صوتية إلى المهملات؟</item>
- <item quantity="many">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> ملفًا صوتية إلى المهملات؟</item>
- <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> ملف صوتي إلى المهملات؟</item>
- <item quantity="one">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بنقل هذا الملف الصوتي إلى المهملات؟</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="zero">جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> ملف صوتي إلى المهملات…</item>
- <item quantity="two">جارٍ نقل ملفَين صوتين (<xliff:g id="COUNT">^1</xliff:g>) إلى المهملات…</item>
- <item quantity="few">جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> ملفات صوتية إلى المهملات…</item>
- <item quantity="many">جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> ملفًا صوتيًا إلى المهملات…</item>
- <item quantity="other">جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> ملف صوتي إلى المهملات…</item>
- <item quantity="one">جارٍ نقل ملف صوتي واحد إلى المهملات…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="zero">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> فيديو إلى المهملات؟</item>
- <item quantity="two">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل فيديوهين (<xliff:g id="COUNT">^2</xliff:g>) إلى المهملات؟</item>
- <item quantity="few">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> فيديوهات إلى المهملات؟</item>
- <item quantity="many">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> فيديو إلى المهملات؟</item>
- <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> فيديو إلى المهملات؟</item>
- <item quantity="one">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بنقل هذا الفيديو إلى المهملات؟</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="zero">جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> فيديو إلى المهملات…</item>
- <item quantity="two">جارٍ نقل فيديوهين (<xliff:g id="COUNT">^1</xliff:g>) إلى المهملات…</item>
- <item quantity="few">جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> فيديوهات إلى المهملات…</item>
- <item quantity="many">جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> فيديو إلى المهملات…</item>
- <item quantity="other">جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> فيديو إلى المهملات…</item>
- <item quantity="one">جارٍ نقل فيديو واحد إلى المهملات…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="zero">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> صورة إلى المهملات؟</item>
- <item quantity="two">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل صورتين (<xliff:g id="COUNT">^2</xliff:g>) إلى المهملات؟</item>
- <item quantity="few">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> صور إلى المهملات؟</item>
- <item quantity="many">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> صورة إلى المهملات؟</item>
- <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> صورة إلى المهملات؟</item>
- <item quantity="one">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بنقل هذه الصورة إلى المهملات؟</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="zero">جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> صورة إلى المهملات…</item>
- <item quantity="two">جارٍ نقل صورتين (<xliff:g id="COUNT">^1</xliff:g>) إلى المهملات…</item>
- <item quantity="few">جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> صور إلى المهملات…</item>
- <item quantity="many">جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> صورة إلى المهملات…</item>
- <item quantity="other">جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> صورة إلى المهملات…</item>
- <item quantity="one">جارٍ نقل صورة واحدة إلى المهملات…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="zero">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> عنصر إلى المهملات؟</item>
- <item quantity="two">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل عنصرين (<xliff:g id="COUNT">^2</xliff:g>) إلى المهملات؟</item>
- <item quantity="few">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> عناصر إلى المهملات؟</item>
- <item quantity="many">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> عنصرًا إلى المهملات؟</item>
- <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> عنصر إلى المهملات؟</item>
- <item quantity="one">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بنقل هذا العنصر إلى المهملات؟</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="zero">جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> عنصر إلى المهملات…</item>
- <item quantity="two">جارٍ نقل عنصرين (<xliff:g id="COUNT">^1</xliff:g>) إلى المهملات…</item>
- <item quantity="few">جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> عناصر إلى المهملات…</item>
- <item quantity="many">جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> عنصرًا إلى المهملات…</item>
- <item quantity="other">جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> عنصر إلى المهملات…</item>
- <item quantity="one">جارٍ نقل عنصر واحد إلى المهملات…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="zero">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> ملف صوتي خارج المهملات؟</item>
- <item quantity="two">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل ملفين صوتيين (<xliff:g id="COUNT">^2</xliff:g>) خارج المهملات؟</item>
- <item quantity="few">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> ملفات صوتية خارج المهملات؟</item>
- <item quantity="many">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> ملفًا صوتيًا خارج المهملات؟</item>
- <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> ملف صوتي خارج المهملات؟</item>
- <item quantity="one">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بنقل الملف الصوتي هذا خارج المهملات؟</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="zero">جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> ملف صوتي من المهملات…</item>
- <item quantity="two">جارٍ إخراج ملفَين صوتين (<xliff:g id="COUNT">^1</xliff:g>) من المهملات…</item>
- <item quantity="few">جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> ملفات صوتية من المهملات…</item>
- <item quantity="many">جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> ملفًا صوتيًا من المهملات…</item>
- <item quantity="other">جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> ملف صوتي من المهملات…</item>
- <item quantity="one">جارٍ إخراج ملف صوتي واحد من المهملات…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="zero">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> فيديو خارج المهملات؟</item>
- <item quantity="two">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل فيديوهين (<xliff:g id="COUNT">^2</xliff:g>) خارج المهملات؟</item>
- <item quantity="few">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> فيديوهات خارج المهملات؟</item>
- <item quantity="many">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> فيديو خارج المهملات؟</item>
- <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> فيديو خارج المهملات؟</item>
- <item quantity="one">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بنقل هذا الفيديو خارج المهملات؟</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="zero">جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> فيديو من المهملات…</item>
- <item quantity="two">جارٍ إخراج فيديوهين (<xliff:g id="COUNT">^1</xliff:g>) من المهملات…</item>
- <item quantity="few">جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> فيديوهات من المهملات…</item>
- <item quantity="many">جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> فيديو من المهملات…</item>
- <item quantity="other">جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> فيديو من المهملات…</item>
- <item quantity="one">جارٍ إخراج فيديو واحد من المهملات…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="zero">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> صورة خارج المهملات؟</item>
- <item quantity="two">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل صورتين (<xliff:g id="COUNT">^2</xliff:g>) خارج المهملات؟</item>
- <item quantity="few">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> صور خارج المهملات؟</item>
- <item quantity="many">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> صورة خارج المهملات؟</item>
- <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> صورة خارج المهملات؟</item>
- <item quantity="one">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بنقل هذه الصورة خارج المهملات؟</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="zero">جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> صورة من المهملات…</item>
- <item quantity="two">جارٍ إخراج صورتين (<xliff:g id="COUNT">^1</xliff:g>) من المهملات…</item>
- <item quantity="few">جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> صور من المهملات…</item>
- <item quantity="many">جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> صورة من المهملات…</item>
- <item quantity="other">جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> صورة من المهملات…</item>
- <item quantity="one">جارٍ إخراج صورة واحدة من المهملات…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="zero">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> عنصر خارج المهملات؟</item>
- <item quantity="two">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل عنصرين (<xliff:g id="COUNT">^2</xliff:g>) خارج المهملات؟</item>
- <item quantity="few">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> عناصر خارج المهملات؟</item>
- <item quantity="many">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> عنصرًا خارج المهملات؟</item>
- <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> عنصر خارج المهملات؟</item>
- <item quantity="one">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بنقل هذا العنصر خارج المهملات؟</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="zero">جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> عنصر من المهملات…</item>
- <item quantity="two">جارٍ إخراج عنصرين (<xliff:g id="COUNT">^1</xliff:g>) من المهملات…</item>
- <item quantity="few">جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> عناصر من المهملات…</item>
- <item quantity="many">جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> عنصرًا من المهملات…</item>
- <item quantity="other">جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> عنصر من المهملات…</item>
- <item quantity="one">جارٍ إخراج عنصر واحد من المهملات…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="zero">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> ملف صوتي؟</item>
- <item quantity="two">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف ملفين صوتيين (<xliff:g id="COUNT">^2</xliff:g>)؟</item>
- <item quantity="few">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> ملفات صوتية؟</item>
- <item quantity="many">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> ملفًا صوتيًا؟</item>
- <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> ملف صوتي؟</item>
- <item quantity="one">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بحذف هذا الملف الصوتي؟</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="zero">جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> ملف صوتي…</item>
- <item quantity="two">جارٍ حذف ملفَين صوتين (<xliff:g id="COUNT">^1</xliff:g>)…</item>
- <item quantity="few">جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> ملفات صوتية…</item>
- <item quantity="many">جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> ملفًا صوتيًا…</item>
- <item quantity="other">جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> ملف صوتي…</item>
- <item quantity="one">جارٍ حذف ملف صوتي واحد…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="zero">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> فيديو؟</item>
- <item quantity="two">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف فيديوهين (<xliff:g id="COUNT">^2</xliff:g>)؟</item>
- <item quantity="few">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> فيديوهات؟</item>
- <item quantity="many">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> فيديو؟</item>
- <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> فيديو؟</item>
- <item quantity="one">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بحذف هذا الفيديو؟</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="zero">جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> فيديو…</item>
- <item quantity="two">جارٍ حذف فيديوهين (<xliff:g id="COUNT">^1</xliff:g>)…</item>
- <item quantity="few">جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> فيديوهات…</item>
- <item quantity="many">جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> فيديو…</item>
- <item quantity="other">جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> فيديو…</item>
- <item quantity="one">جارٍ حذف فيديو واحد…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="zero">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> صورة؟</item>
- <item quantity="two">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف صورتين (<xliff:g id="COUNT">^2</xliff:g>)؟</item>
- <item quantity="few">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> صور؟</item>
- <item quantity="many">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> صورة؟</item>
- <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> صورة؟</item>
- <item quantity="one">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بحذف صورة واحدة؟</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="zero">جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> صورة…</item>
- <item quantity="two">جارٍ حذف صورتين (<xliff:g id="COUNT">^1</xliff:g>)…</item>
- <item quantity="few">جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> صور…</item>
- <item quantity="many">جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> صورة…</item>
- <item quantity="other">جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> صورة…</item>
- <item quantity="one">جارٍ حذف صورة واحدة…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="zero">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> عنصر؟</item>
- <item quantity="two">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف عنصرين (<xliff:g id="COUNT">^2</xliff:g>)؟</item>
- <item quantity="few">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> عناصر؟</item>
- <item quantity="many">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> عنصرًا؟</item>
- <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> عنصر؟</item>
- <item quantity="one">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بحذف هذا العنصر؟</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="zero">جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> عنصر…</item>
- <item quantity="two">جارٍ حذف عنصرين (<xliff:g id="COUNT">^1</xliff:g>)…</item>
- <item quantity="few">جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> عناصر…</item>
- <item quantity="many">جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> عنصرًا…</item>
- <item quantity="other">جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> عنصر…</item>
- <item quantity="one">جارٍ حذف عنصر واحد…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"إضافة"</string>
+ <string name="deselect" msgid="4297825044827769490">"إلغاء الاختيار"</string>
+ <string name="deselected" msgid="8488133193326208475">"تم إلغاء الاختيار"</string>
+ <string name="select" msgid="2704765470563027689">"اختيار"</string>
+ <string name="selected" msgid="9151797369975828124">"تم الاختيار"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{اختَر ما يصل إلى عنصر واحد (<xliff:g id="COUNT_0">^1</xliff:g>).}zero{اختَر ما يصل إلى <xliff:g id="COUNT_1">^1</xliff:g> عنصر.}two{اختَر ما يصل إلى عنصرين (<xliff:g id="COUNT_1">^1</xliff:g>).}few{اختَر ما يصل إلى <xliff:g id="COUNT_1">^1</xliff:g> عناصر.}many{اختَر ما يصل إلى <xliff:g id="COUNT_1">^1</xliff:g> عنصرًا.}other{اختَر ما يصل إلى <xliff:g id="COUNT_1">^1</xliff:g> عنصر.}}"</string>
+ <string name="recent" msgid="6694613584743207874">"الأحدث"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"ما مِن صور أو فيديوهات"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"ما مِن ألبومات"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"عرض ما تم اختياره"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"الصور"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"الألبومات"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"معاينة"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"التبديل إلى الملف الشخصي للعمل"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"التبديل إلى الملف الشخصي"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"حظر المشرف هذه الميزة"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"غير مسموح بالوصول إلى بيانات العمل من تطبيق شخصي."</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"غير مسموح بالوصول إلى البيانات الشخصية من تطبيق عمل."</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"تطبيقات العمل متوقفة مؤقتًا"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"لفتح صور العمل، عليك تفعيل تطبيقات العمل ثم إعادة المحاولة."</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"يمكن لهذا التطبيق الوصول إلى الصور التي تختارها فقط."</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{عنصر واحد (<xliff:g id="COUNT_0">^1</xliff:g>)}zero{<xliff:g id="COUNT_1">^1</xliff:g> عنصر}two{عنصران (<xliff:g id="COUNT_1">^1</xliff:g>)}few{<xliff:g id="COUNT_1">^1</xliff:g> عناصر}many{<xliff:g id="COUNT_1">^1</xliff:g> عنصرًا}other{<xliff:g id="COUNT_1">^1</xliff:g> عنصر}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"إضافة (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"الكاميرا"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"العناصر التي تم تنزيلها"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"العناصر المفضّلة"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"لقطات الشاشة"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"الصورة الحيّة"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> تم الالتقاط في <xliff:g id="TIME">%2$s</xliff:g>."</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"فيديو تم التقاطه في <xliff:g id="TIME">%1$s</xliff:g>، ومدته <xliff:g id="DURATION">%2$s</xliff:g>."</string>
+ <string name="picker_photo" msgid="1739342083494962153">"صورة"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"صور حيّة"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"كتم صوت الفيديو"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"إعادة صوت الفيديو"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"تشغيل الفيديو"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"إيقاف الفيديو مؤقتًا"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"يتوفّر محتوى الوسائط على السحابة الإلكترونية الآن من خلال تطبيق <xliff:g id="PKG_NAME">%1$s</xliff:g>."</string>
+ <string name="not_selected" msgid="2244008151669896758">"غير محدّد"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بتعديل هذا الملف الصوتي؟}zero{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> ملف صوتي؟}two{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل ملفَين صوتيين (<xliff:g id="COUNT">^2</xliff:g>)؟}few{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> ملفات صوتية؟}many{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> ملفًا صوتيًا؟}other{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> ملف صوتي؟}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{جارٍ تعديل ملف صوتي واحد…}zero{جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> ملف صوتي…}two{جارٍ تعديل ملفَين صوتين (<xliff:g id="COUNT">^1</xliff:g>)…}few{جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> ملفات صوتية…}many{جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> ملفًا صوتيًا…}other{جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> ملف صوتي…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بتعديل هذا الفيديو؟}zero{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> فيديو؟}two{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل فيديوهين (<xliff:g id="COUNT">^2</xliff:g>)؟}few{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> فيديوهات؟}many{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> فيديو؟}other{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> فيديو؟}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{جارٍ تعديل فيديو واحد…}zero{جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> فيديو…}two{جارٍ تعديل فيديوهين (<xliff:g id="COUNT">^1</xliff:g>)…}few{جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> فيديوهات…}many{جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> فيديو…}other{جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> فيديو…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بتعديل هذه الصورة؟}zero{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> صورة؟}two{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل صورتين (<xliff:g id="COUNT">^2</xliff:g>)؟}few{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> صور؟}many{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> صورة؟}other{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> صورة؟}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{جارٍ تعديل صورة واحدة…}zero{جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> صورة…}two{جارٍ تعديل صورتين (<xliff:g id="COUNT">^1</xliff:g>)…}few{جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> صور…}many{جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> صورة…}other{جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> صورة…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بتعديل هذا العنصر؟}zero{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> عنصر؟}two{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل عنصرين (<xliff:g id="COUNT">^2</xliff:g>)؟}few{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> عناصر؟}many{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> عنصرًا؟}other{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g id="COUNT">^2</xliff:g> عنصر؟}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{جارٍ تعديل عنصر واحد…}zero{جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> عنصر…}two{جارٍ تعديل عنصرين (<xliff:g id="COUNT">^1</xliff:g>)…}few{جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> عناصر…}many{جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> عنصرًا…}other{جارٍ تعديل <xliff:g id="COUNT">^1</xliff:g> عنصر…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بنقل هذا الملف الصوتي إلى المهملات؟}zero{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> ملف صوتي إلى المهملات؟}two{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل ملفَين صوتيين (<xliff:g id="COUNT">^2</xliff:g>) إلى المهملات؟}few{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> ملفات صوتية إلى المهملات؟}many{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> ملفًا صوتيًا إلى المهملات؟}other{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> ملف صوتي إلى المهملات؟}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{جارٍ نقل ملف صوتي واحد إلى المهملات…}zero{جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> ملف صوتي إلى المهملات…}two{جارٍ نقل ملفَين صوتين (<xliff:g id="COUNT">^1</xliff:g>) إلى المهملات…}few{جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> ملفات صوتية إلى المهملات…}many{جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> ملفًا صوتيًا إلى المهملات…}other{جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> ملف صوتي إلى المهملات…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بنقل هذا الفيديو إلى المهملات؟}zero{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> فيديو إلى المهملات؟}two{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل فيديوهين (<xliff:g id="COUNT">^2</xliff:g>) إلى المهملات؟}few{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> فيديوهات إلى المهملات؟}many{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> فيديو إلى المهملات؟}other{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> فيديو إلى المهملات؟}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{جارٍ نقل فيديو واحد إلى المهملات…}zero{جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> فيديو إلى المهملات…}two{جارٍ نقل فيديوهين (<xliff:g id="COUNT">^1</xliff:g>) إلى المهملات…}few{جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> فيديوهات إلى المهملات…}many{جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> فيديو إلى المهملات…}other{جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> فيديو إلى المهملات…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بنقل هذه الصورة إلى المهملات؟}zero{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> صورة إلى المهملات؟}two{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل صورتين (<xliff:g id="COUNT">^2</xliff:g>) إلى المهملات؟}few{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> صور إلى المهملات؟}many{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> صورة إلى المهملات؟}other{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> صورة إلى المهملات؟}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{جارٍ نقل صورة واحدة إلى المهملات…}zero{جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> صورة إلى المهملات…}two{جارٍ نقل صورتين (<xliff:g id="COUNT">^1</xliff:g>) إلى المهملات…}few{جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> صور إلى المهملات…}many{جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> صورة إلى المهملات…}other{جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> صورة إلى المهملات…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بنقل هذا العنصر إلى المهملات؟}zero{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> عنصر إلى المهملات؟}two{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل عنصرين (<xliff:g id="COUNT">^2</xliff:g>) إلى المهملات؟}few{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> عناصر إلى المهملات؟}many{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> عنصرًا إلى المهملات؟}other{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> عنصر إلى المهملات؟}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{جارٍ نقل عنصر واحد إلى المهملات…}zero{جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> عنصر إلى المهملات…}two{جارٍ نقل عنصرين (<xliff:g id="COUNT">^1</xliff:g>) إلى المهملات…}few{جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> عناصر إلى المهملات…}many{جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> عنصرًا إلى المهملات…}other{جارٍ نقل <xliff:g id="COUNT">^1</xliff:g> عنصر إلى المهملات…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بنقل الملف الصوتي هذا خارج المهملات؟}zero{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> ملف صوتي خارج المهملات؟}two{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل ملفَين صوتيين (<xliff:g id="COUNT">^2</xliff:g>) خارج المهملات؟}few{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> ملفات صوتية خارج المهملات؟}many{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> ملفًا صوتيًا خارج المهملات؟}other{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> ملف صوتي خارج المهملات؟}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{جارٍ إخراج ملف صوتي واحد من المهملات…}zero{جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> ملف صوتي من المهملات…}two{جارٍ إخراج ملفَين صوتين (<xliff:g id="COUNT">^1</xliff:g>) من المهملات…}few{جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> ملفات صوتية من المهملات…}many{جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> ملفًا صوتيًا من المهملات…}other{جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> ملف صوتي من المهملات…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بنقل هذا الفيديو خارج المهملات؟}zero{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> فيديو خارج المهملات؟}two{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل فيديوهين (<xliff:g id="COUNT">^2</xliff:g>) خارج المهملات؟}few{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> فيديوهات خارج المهملات؟}many{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> فيديو خارج المهملات؟}other{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> فيديو خارج المهملات؟}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{جارٍ إخراج فيديو واحد من المهملات…}zero{جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> فيديو من المهملات…}two{جارٍ إخراج فيديوهين (<xliff:g id="COUNT">^1</xliff:g>) من المهملات…}few{جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> فيديوهات من المهملات…}many{جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> فيديو من المهملات…}other{جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> فيديو من المهملات…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بنقل هذه الصورة خارج المهملات؟}zero{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> صورة خارج المهملات؟}two{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل صورتين (<xliff:g id="COUNT">^2</xliff:g>) خارج المهملات؟}few{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> صور خارج المهملات؟}many{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> صورة خارج المهملات؟}other{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> صورة خارج المهملات؟}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{جارٍ إخراج صورة واحدة من المهملات…}zero{جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> صورة من المهملات…}two{جارٍ إخراج صورتين (<xliff:g id="COUNT">^1</xliff:g>) من المهملات…}few{جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> صور من المهملات…}many{جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> صورة من المهملات…}other{جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> صورة من المهملات…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بنقل هذا العنصر خارج المهملات؟}zero{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> عنصر خارج المهملات؟}two{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل عنصرين (<xliff:g id="COUNT">^2</xliff:g>) خارج المهملات؟}few{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> عناصر خارج المهملات؟}many{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> عنصرًا خارج المهملات؟}other{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g id="COUNT">^2</xliff:g> عنصر خارج المهملات؟}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{جارٍ إخراج عنصر واحد من المهملات…}zero{جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> عنصر من المهملات…}two{جارٍ إخراج عنصرين (<xliff:g id="COUNT">^1</xliff:g>) من المهملات…}few{جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> عناصر من المهملات…}many{جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> عنصرًا من المهملات…}other{جارٍ إخراج <xliff:g id="COUNT">^1</xliff:g> عنصر من المهملات…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بحذف هذا الملف الصوتي؟}zero{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> ملف صوتي؟}two{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف ملفَين صوتيين (<xliff:g id="COUNT">^2</xliff:g>)؟}few{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> ملفات صوتية؟}many{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> ملفًا صوتيًا؟}other{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> ملف صوتي؟}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{جارٍ حذف ملف صوتي واحد…}zero{جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> ملف صوتي…}two{جارٍ حذف ملفَين صوتين (<xliff:g id="COUNT">^1</xliff:g>)…}few{جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> ملفات صوتية…}many{جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> ملفًا صوتيًا…}other{جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> ملف صوتي…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بحذف هذا الفيديو؟}zero{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> فيديو؟}two{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف فيديوهين (<xliff:g id="COUNT">^2</xliff:g>)؟}few{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> فيديوهات؟}many{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> فيديو؟}other{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> فيديو؟}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{جارٍ حذف فيديو واحد…}zero{جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> فيديو…}two{جارٍ حذف فيديوهين (<xliff:g id="COUNT">^1</xliff:g>)…}few{جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> فيديوهات…}many{جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> فيديو…}other{جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> فيديو…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بحذف هذه الصورة؟}zero{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> صورة؟}two{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف صورتين (<xliff:g id="COUNT">^2</xliff:g>)؟}few{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> صور؟}many{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> صورة؟}other{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> صورة؟}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{جارٍ حذف صورة واحدة…}zero{جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> صورة…}two{جارٍ حذف صورتين (<xliff:g id="COUNT">^1</xliff:g>)…}few{جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> صور…}many{جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> صورة…}other{جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> صورة…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_0">^1</xliff:g> بحذف هذا العنصر؟}zero{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> عنصر؟}two{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف عنصرين (<xliff:g id="COUNT">^2</xliff:g>)؟}few{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> عناصر؟}many{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> عنصرًا؟}other{هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g id="COUNT">^2</xliff:g> عنصر؟}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{جارٍ حذف عنصر واحد…}zero{جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> عنصر…}two{جارٍ حذف عنصرين (<xliff:g id="COUNT">^1</xliff:g>)…}few{جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> عناصر…}many{جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> عنصرًا…}other{جارٍ حذف <xliff:g id="COUNT">^1</xliff:g> عنصر…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"يتعذّر على التطبيق <xliff:g id="APP_NAME">%s</xliff:g> معالجة ملفات الوسائط."</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"تم إلغاء معالجة الوسائط."</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"حدث خطأ أثناء معالجة الوسائط."</string>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 3877e00..379a2ed 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"মিডিয়া"</string>
<string name="storage_description" msgid="4081716890357580107">"স্থানীয় ষ্ট’ৰেজ"</string>
<string name="app_label" msgid="9035307001052716210">"মিডিয়া ষ্ট’ৰেজ"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"ফট’ বাছনিকৰ্তা"</string>
<string name="artist_label" msgid="8105600993099120273">"শিল্পী"</string>
<string name="unknown" msgid="2059049215682829375">"অজ্ঞাত"</string>
<string name="root_images" msgid="5861633549189045666">"প্ৰতিচ্ছবি"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"অব্যাহত ৰাখক"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"অনুমতি দিয়ক"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"অস্বীকাৰ কৰক"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">+<xliff:g id="COUNT_1">^1</xliff:g> টা</item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g> টা</item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">আৰু <xliff:g id="COUNT_1">^1</xliff:g> টা অতিৰিক্ত বস্তু</item>
- <item quantity="other">আৰু <xliff:g id="COUNT_1">^1</xliff:g> টা অতিৰিক্ত বস্তু</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g> টা}one{+<xliff:g id="COUNT_1">^1</xliff:g> টা}other{+<xliff:g id="COUNT_1">^1</xliff:g> টা}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{আৰু <xliff:g id="COUNT_0">^1</xliff:g> টা অতিৰিক্ত বস্তু}one{আৰু <xliff:g id="COUNT_1">^1</xliff:g> টা অতিৰিক্ত বস্তু}other{আৰু <xliff:g id="COUNT_1">^1</xliff:g> টা অতিৰিক্ত বস্তু}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"অস্থায়ী এপ্ ফাইলসমূহ মচক"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>এ কিছুমান অস্থায়ী ফাইল মচিব বিচাৰিছে। ইয়াৰ ফলত বেটাৰী অথবা চেলুলাৰ ডেটাৰ ব্যৱহাৰ বৃদ্ধি হ’ব পাৰে।"</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"অস্থায়ী এপ্ ফাইলসমূহ মচি থকা হৈছে…"</string>
<string name="clear" msgid="5524638938415865915">"মচক"</string>
<string name="allow" msgid="8885707816848569619">"অনুমতি দিয়ক"</string>
<string name="deny" msgid="6040983710442068936">"অস্বীকাৰ কৰক"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা অডিঅ’ ফাইল সংশোধন কৰিবলৈ অনুমতি দিবনে?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা অডিঅ’ ফাইল সংশোধন কৰিবলৈ অনুমতি দিবনে?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> টা অডিঅ’ ফাইল সংশোধন কৰি থকা হৈছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> টা অডিঅ’ ফাইল সংশোধন কৰি থকা হৈছে…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা ভিডিঅ’ সংশোধন কৰিবলৈ অনুমতি দিবনে?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা ভিডিঅ’ সংশোধন কৰিবলৈ অনুমতি দিবনে?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> টা ভিডিঅ’ সংশোধন কৰি থকা হৈছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> টা ভিডিঅ’ সংশোধন কৰি থকা হৈছে…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> খন ফট’ সংশোধন কৰিবলৈ অনুমতি দিবনে?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> খন ফট’ সংশোধন কৰিবলৈ অনুমতি দিবনে?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> খন ফট’ সংশোধন কৰি থকা হৈছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> খন ফট’ সংশোধন কৰি থকা হৈছে…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা বস্তু সংশোধন কৰিবলৈ অনুমতি দিবনে?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা বস্তু সংশোধন কৰিবলৈ অনুমতি দিবনে?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> টা বস্তু সংশোধন কৰি থকা হৈছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> টা বস্তু সংশোধন কৰি থকা হৈছে…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা অডিঅ’ ফাইল ট্ৰেশ্বলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা অডিঅ’ ফাইল ট্ৰেশ্বলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> টা অডিঅ’ ফাইল ট্ৰেশ্বলৈ নি থকা হৈছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> টা অডিঅ’ ফাইল ট্ৰেশ্বলৈ নি থকা হৈছে…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা ভিডিঅ’ ট্ৰেশ্বলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা ভিডিঅ’ ট্ৰেশ্বলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> টা ভিডিঅ’ ট্ৰেশ্বলৈ নি থকা হৈছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> টা ভিডিঅ’ ট্ৰেশ্বলৈ নি থকা হৈছে…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> খন ফট’ ট্ৰেশ্বলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> খন ফট’ ট্ৰেশ্বলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> খন ফট’ ট্ৰেশ্বলৈ নি থকা হৈছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> খন ফট’ ট্ৰেশ্বলৈ নি থকা হৈছে…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা বস্তু ট্ৰেশ্বলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা বস্তু ট্ৰেশ্বলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> টা বস্তু ট্ৰেশ্বলৈ নি থকা হৈছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> টা বস্তু ট্ৰেশ্বলৈ নি থকা হৈছে…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা অডিঅ’ ফাইল ট্ৰেশ্বৰ পৰা বাহিৰলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা অডিঅ’ ফাইল ট্ৰেশ্বৰ পৰা বাহিৰলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> টা অডিঅ’ ফাইল ট্ৰেশ্বৰ পৰা বাহিৰলৈ নি থকা হৈছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> টা অডিঅ’ ফাইল ট্ৰেশ্বৰ পৰা বাহিৰলৈ নি থকা হৈছে…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা ভিডিঅ’ ট্ৰেশ্বৰ পৰা বাহিৰলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা ভিডিঅ’ ট্ৰেশ্বৰ পৰা বাহিৰলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> টা ভিডিঅ’ ট্ৰেশ্বৰ পৰা বাহিৰলৈ নি থকা হৈছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> টা ভিডিঅ’ ট্ৰেশ্বৰ পৰা বাহিৰলৈ নি থকা হৈছে…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> খন ফট’ ট্ৰেশ্বৰ পৰা বাহিৰলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> খন ফট’ ট্ৰেশ্বৰ পৰা বাহিৰলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> খন ফট’ ট্ৰেশ্বৰ পৰা বাহিৰলৈ নি থকা হৈছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> খন ফট’ ট্ৰেশ্বৰ পৰা বাহিৰলৈ নি থকা হৈছে…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা বস্তু ট্ৰেশ্বৰ পৰা বাহিৰলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা বস্তু ট্ৰেশ্বৰ পৰা বাহিৰলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> টা বস্তু ট্ৰেশ্বৰ পৰা বাহিৰলৈ নি থকা হৈছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> টা বস্তু ট্ৰেশ্বৰ পৰা বাহিৰলৈ নি থকা হৈছে…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা অডিঅ’ ফাইল মচিবলৈ অনুমতি দিবনে?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা অডিঅ’ ফাইল মচিবলৈ অনুমতি দিবনে?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> টা অডিঅ’ ফাইল মচি থকা হৈছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> টা অডিঅ’ ফাইল মচি থকা হৈছে…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা ভিডিঅ’ মচিবলৈ অনুমতি দিবনে?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা ভিডিঅ’ মচিবলৈ অনুমতি দিবনে?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> টা ভিডিঅ’ মচি থকা হৈছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> টা ভিডিঅ’ মচি থকা হৈছে…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> খন ফট’ মচিবলৈ অনুমতি দিবনে?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> খন ফট’ মচিবলৈ অনুমতি দিবনে?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> খন ফট’ মচি থকা হৈছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> খন ফট’ মচি থকা হৈছে…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা বস্তু মচিবলৈ অনুমতি দিবনে?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা বস্তু মচিবলৈ অনুমতি দিবনে?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> টা বস্তু মচি থকা হৈছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> টা বস্তু মচি থকা হৈছে…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"যোগ দিয়ক"</string>
+ <string name="deselect" msgid="4297825044827769490">"বাছনিৰ পৰা আঁতৰাওক"</string>
+ <string name="deselected" msgid="8488133193326208475">"বাছনিৰ পৰা আঁতৰোৱা হ’ল"</string>
+ <string name="select" msgid="2704765470563027689">"বাছনি কৰক"</string>
+ <string name="selected" msgid="9151797369975828124">"বাছনি কৰা হ’ল"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> টালৈকে বস্তু বাছনি কৰক}one{<xliff:g id="COUNT_1">^1</xliff:g> টালৈকে বস্তু বাছনি কৰক}other{<xliff:g id="COUNT_1">^1</xliff:g> টালৈকে বস্তু বাছনি কৰক}}"</string>
+ <string name="recent" msgid="6694613584743207874">"শেহতীয়া"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"কোনো ফট’ অথবা ভিডিঅ’ নাই"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"কোনো এলবাম নাই"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"ভিউ বাছনি কৰা হৈছে"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"ফট’"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"এলবাম"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"পূৰ্বদৰ্শন কৰক"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"কৰ্মস্থানৰ প্ৰ’ফাইললৈ সলনি কৰক"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"ব্যক্তিগত প্ৰ’ফাইললৈ সলনি কৰক"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"আপোনাৰ প্ৰশাসকে অৱৰোধ কৰিছে"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"ব্যক্তিগত এপৰ পৰা কৰ্মস্থানৰ ডেটা এক্সেছ কৰাৰ অনুমতি নাই"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"কাম সম্পর্কীয় এপৰ পৰা ব্যক্তিগত ডেটা এক্সেছ কৰাৰ অনুমতি নাই"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"কাম সম্পর্কীয় এপ্সমূহ পজ কৰা আছে"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"কৰ্মস্থানৰ ফট’ খুলিবলৈ আপোনাৰ কাম সম্পর্কীয় এপ্সমূহ অন কৰক তাৰ পাছত পুনৰ চেষ্টা কৰক"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"এই এপ্টোৱে কেৱল আপুনি বাছনি কৰা ফট\'সমূহ এক্সেছ কৰিব পাৰে"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> টা বস্তু}one{<xliff:g id="COUNT_1">^1</xliff:g> টা বস্তু}other{<xliff:g id="COUNT_1">^1</xliff:g> টা বস্তু}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"(<xliff:g id="COUNT">^1</xliff:g> টা) যোগ দিয়ক"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"কেমেৰা"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"ডাউনল’ড"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"প্ৰিয়"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"স্ক্ৰীনশ্বট"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"গতিশীল ফট’"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> খন <xliff:g id="TIME">%2$s</xliff:g> তাৰিখে তোলা হৈছিল"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"<xliff:g id="TIME">%1$s</xliff:g>ত <xliff:g id="DURATION">%2$s</xliff:g>ৰ সময়ৰ এটি ভিডিঅ’ ৰেকৰ্ড কৰা হৈছে"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"ফট’"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"গতিশীল ফট’"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"ভিডিঅ’ মিউট কৰক"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"ভিডিঅ’ আনমিউট কৰক"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"ভিডিঅ’ প্লে’ কৰক"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"ভিডিঅ’ পজ কৰক"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"এতিয়া <xliff:g id="PKG_NAME">%1$s</xliff:g>ৰ পৰা ক্লাউড মিডিয়া উপলব্ধ"</string>
+ <string name="not_selected" msgid="2244008151669896758">"বাছনি কৰা হোৱা নাই"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ক এই অডিঅ’ ফাইলটো সংশোধন কৰিবলৈ অনুমতি দিবনে?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা অডিঅ’ ফাইল সংশোধন কৰিবলৈ অনুমতি দিবনে?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা অডিঅ’ ফাইল সংশোধন কৰিবলৈ অনুমতি দিবনে?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{অডিঅ’ ফাইলটো সংশোধন কৰি থকা হৈছে…}one{<xliff:g id="COUNT">^1</xliff:g> টা অডিঅ’ ফাইল সংশোধন কৰি থকা হৈছে…}other{<xliff:g id="COUNT">^1</xliff:g> টা অডিঅ’ ফাইল সংশোধন কৰি থকা হৈছে…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ক এই ভিডিঅ’টো সংশোধন কৰিবলৈ অনুমতি দিবনে?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা ভিডিঅ’ সংশোধন কৰিবলৈ অনুমতি দিবনে?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা ভিডিঅ’ সংশোধন কৰিবলৈ অনুমতি দিবনে?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{ভিডিঅ’টো সংশোধন কৰি থকা হৈছে…}one{<xliff:g id="COUNT">^1</xliff:g> টা ভিডিঅ’ সংশোধন কৰি থকা হৈছে…}other{<xliff:g id="COUNT">^1</xliff:g> টা ভিডিঅ’ সংশোধন কৰি থকা হৈছে…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ক এই ফট’খন সংশোধন কৰিবলৈ অনুমতি দিবনে?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> খন ফট’ সংশোধন কৰিবলৈ অনুমতি দিবনে?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> খন ফট’ সংশোধন কৰিবলৈ অনুমতি দিবনে?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{ফট’খন সংশোধন কৰি থকা হৈছে…}one{<xliff:g id="COUNT">^1</xliff:g> খন ফট’ সংশোধন কৰি থকা হৈছে…}other{<xliff:g id="COUNT">^1</xliff:g> খন ফট’ সংশোধন কৰি থকা হৈছে…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ক এই বস্তুটো সংশোধন কৰিবলৈ অনুমতি দিবনে?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা বস্তু সংশোধন কৰিবলৈ অনুমতি দিবনে?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা বস্তু সংশোধন কৰিবলৈ অনুমতি দিবনে?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{বস্তুটো সংশোধন কৰি থকা হৈছে…}one{<xliff:g id="COUNT">^1</xliff:g> টা বস্তু সংশোধন কৰি থকা হৈছে…}other{<xliff:g id="COUNT">^1</xliff:g> টা বস্তু সংশোধন কৰি থকা হৈছে…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ক এই অডিঅ’ ফাইলটো ট্ৰেশ্বলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা অডিঅ’ ফাইল ট্ৰেশ্বলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা অডিঅ’ ফাইল ট্ৰেশ্বলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{অডিঅ’ ফাইলটো ট্ৰেশ্বলৈ নি থকা হৈছে…}one{<xliff:g id="COUNT">^1</xliff:g> টা অডিঅ’ ফাইল ট্ৰেশ্বলৈ নি থকা হৈছে…}other{<xliff:g id="COUNT">^1</xliff:g> টা অডিঅ’ ফাইল ট্ৰেশ্বলৈ নি থকা হৈছে…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ক এই ভিডিঅ’টো ট্ৰেশ্বলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা ভিডিঅ’ ট্ৰেশ্বলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা ভিডিঅ’ ট্ৰেশ্বলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{ভিডিঅ’টো ট্ৰেশ্বলৈ নি থকা হৈছে…}one{<xliff:g id="COUNT">^1</xliff:g> টা ভিডিঅ’ ট্ৰেশ্বলৈ নি থকা হৈছে…}other{<xliff:g id="COUNT">^1</xliff:g> টা ভিডিঅ’ ট্ৰেশ্বলৈ নি থকা হৈছে…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ক এই ফট’খন ট্ৰেশ্বলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> খন ফট’ ট্ৰেশ্বলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> খন ফট’ ট্ৰেশ্বলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{ফট’খন ট্ৰেশ্বলৈ নি থকা হৈছে…}one{<xliff:g id="COUNT">^1</xliff:g> খন ফট’ ট্ৰেশ্বলৈ নি থকা হৈছে…}other{<xliff:g id="COUNT">^1</xliff:g> খন ফট’ ট্ৰেশ্বলৈ নি থকা হৈছে…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ক এই বস্তুটো ট্ৰেশ্বলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা বস্তু ট্ৰেশ্বলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা বস্তু ট্ৰেশ্বলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{বস্তুটো ট্ৰেশ্বলৈ নি থকা হৈছে…}one{<xliff:g id="COUNT">^1</xliff:g> টা বস্তু ট্ৰেশ্বলৈ নি থকা হৈছে…}other{<xliff:g id="COUNT">^1</xliff:g> টা বস্তু ট্ৰেশ্বলৈ নি থকা হৈছে…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ক এই অডিঅ’ ফাইলটো ট্ৰেশ্বৰ পৰা বাহিৰলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা অডিঅ’ ফাইল ট্ৰেশ্বৰ পৰা বাহিৰলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা অডিঅ’ ফাইল ট্ৰেশ্বৰ পৰা বাহিৰলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{অডিঅ’ ফাইলটো ট্ৰেশ্বৰ পৰা বাহিৰলৈ নি থকা হৈছে…}one{<xliff:g id="COUNT">^1</xliff:g> টা অডিঅ’ ফাইল ট্ৰেশ্বৰ পৰা বাহিৰলৈ নি থকা হৈছে…}other{<xliff:g id="COUNT">^1</xliff:g> টা অডিঅ’ ফাইল ট্ৰেশ্বৰ পৰা বাহিৰলৈ নি থকা হৈছে…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ক এই ভিডিঅ’টো ট্ৰেশ্বৰ পৰা বাহিৰলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা ভিডিঅ’ ট্ৰেশ্বৰ পৰা বাহিৰলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা ভিডিঅ’ ট্ৰেশ্বৰ পৰা বাহিৰলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{ভিডিঅ’টো ট্ৰেশ্বৰ পৰা বাহিৰলৈ নি থকা হৈছে…}one{<xliff:g id="COUNT">^1</xliff:g> টা ভিডিঅ’ ট্ৰেশ্বৰ পৰা বাহিৰলৈ নি থকা হৈছে…}other{<xliff:g id="COUNT">^1</xliff:g> টা ভিডিঅ’ ট্ৰেশ্বৰ পৰা বাহিৰলৈ নি থকা হৈছে…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ক এই ফট’খন ট্ৰেশ্বৰ পৰা বাহিৰলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> খন ফট’ ট্ৰেশ্বৰ পৰা বাহিৰলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> খন ফট’ ট্ৰেশ্বৰ পৰা বাহিৰলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{ফট’খন ট্ৰেশ্বৰ পৰা বাহিৰলৈ নি থকা হৈছে…}one{<xliff:g id="COUNT">^1</xliff:g> খন ফট’ ট্ৰেশ্বৰ পৰা বাহিৰলৈ নি থকা হৈছে…}other{<xliff:g id="COUNT">^1</xliff:g> খন ফট’ ট্ৰেশ্বৰ পৰা বাহিৰলৈ নি থকা হৈছে…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ক এই বস্তুটো ট্ৰেশ্বৰ পৰা বাহিৰলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা বস্তু ট্ৰেশ্বৰ পৰা বাহিৰলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা বস্তু ট্ৰেশ্বৰ পৰা বাহিৰলৈ স্থানান্তৰ কৰিবলৈ অনুমতি দিবনে?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{বস্তুটো ট্ৰেশ্বৰ পৰা বাহিৰলৈ নি থকা হৈছে…}one{<xliff:g id="COUNT">^1</xliff:g> টা বস্তু ট্ৰেশ্বৰ পৰা বাহিৰলৈ নি থকা হৈছে…}other{<xliff:g id="COUNT">^1</xliff:g> টা বস্তু ট্ৰেশ্বৰ পৰা বাহিৰলৈ নি থকা হৈছে…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ক এই অডিঅ’ ফাইলটো মচিবলৈ অনুমতি দিবনে?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা অডিঅ’ ফাইল মচিবলৈ অনুমতি দিবনে?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা অডিঅ’ ফাইল মচিবলৈ অনুমতি দিবনে?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{অডিঅ’ ফাইলটো মচি থকা হৈছে…}one{<xliff:g id="COUNT">^1</xliff:g> টা অডিঅ’ ফাইল মচি থকা হৈছে…}other{<xliff:g id="COUNT">^1</xliff:g> টা অডিঅ’ ফাইল মচি থকা হৈছে…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ক এই ভিডিঅ’টো মচিবলৈ অনুমতি দিবনে?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা ভিডিঅ’ মচিবলৈ অনুমতি দিবনে?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা ভিডিঅ’ মচিবলৈ অনুমতি দিবনে?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{ভিডিঅ’টো মচি থকা হৈছে…}one{<xliff:g id="COUNT">^1</xliff:g> টা ভিডিঅ’ মচি থকা হৈছে…}other{<xliff:g id="COUNT">^1</xliff:g> টা ভিডিঅ’ মচি থকা হৈছে…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ক এই ফট’খন মচিবলৈ অনুমতি দিবনে?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> খন ফট’ মচিবলৈ অনুমতি দিবনে?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> খন ফট’ মচিবলৈ অনুমতি দিবনে?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{ফট’খন মচি থকা হৈছে…}one{<xliff:g id="COUNT">^1</xliff:g> খন ফট’ মচি থকা হৈছে…}other{<xliff:g id="COUNT">^1</xliff:g> খন ফট’ মচি থকা হৈছে…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ক এই বস্তুটো মচিবলৈ অনুমতি দিবনে?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা বস্তু মচিবলৈ অনুমতি দিবনে?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা বস্তু মচিবলৈ অনুমতি দিবনে?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{বস্তুটো মচি থকা হৈছে…}one{<xliff:g id="COUNT">^1</xliff:g> টা বস্তু মচি থকা হৈছে…}other{<xliff:g id="COUNT">^1</xliff:g> টা বস্তু মচি থকা হৈছে…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g>এ মিডিয়া ফাইলৰ প্ৰক্ৰিয়াকৰণ কৰিব নোৱাৰে"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"মিডিয়াৰ প্ৰক্ৰিয়াকৰণ বাতিল কৰা হৈছে"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"মিডিয়াৰ প্ৰক্ৰিয়াকৰণত আসোঁৱাহ হৈছে"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 785ada1..a0d97d6 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Foto seçici"</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>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Davam edin"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"İcazə verin"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Rədd edin"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Üstəgəl <xliff:g id="COUNT_1">^1</xliff:g> əlavə element</item>
- <item quantity="one">Üstəgəl <xliff:g id="COUNT_0">^1</xliff:g> əlavə element</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Üstəgəl <xliff:g id="COUNT_0">^1</xliff:g> əlavə element}other{Üstəgəl <xliff:g id="COUNT_1">^1</xliff:g> əlavə element}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Müvəqqəti tətbiq fayllarını silin"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> bəzi müvəqqəti faylları silmək istəyir. Bu, batareya və ya mobil data istifadəsinin artmasına səbəb ola bilər."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Müvəqqəti tətbiq faylları silinir…"</string>
<string name="clear" msgid="5524638938415865915">"Silin"</string>
<string name="allow" msgid="8885707816848569619">"İcazə verin"</string>
<string name="deny" msgid="6040983710442068936">"Rədd edin"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> audio fayla dəyişiklik etmək icazəsi verilsin?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu audio fayla dəyişiklik etmək icazəsi verilsin?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> audio fayl dəyişdirilir…</item>
- <item quantity="one">Audio fayl dəyişdirilir…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> videoya dəyişiklik etmək icazəsi verilsin?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu videoya dəyişiklik etmək icazəsi verilsin?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> video dəyişdirilir…</item>
- <item quantity="one">Video dəyişdirilir…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> fotoya dəyişiklik etmək icazəsi verilsin?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu fotoya dəyişiklik etmək icazəsi verilsin?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> foto dəyişdirilir…</item>
- <item quantity="one">Foto dəyişdirilir…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> elementə dəyişiklik etmək icazəsi verilsin?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu elementə dəyişiklik etmək icazəsi verilsin?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> element dəyişdirilir…</item>
- <item quantity="one">Element dəyişdirilir…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> audio faylı zibil qutusuna köçürmək icazəsi verilsin?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu audio faylı zibil qutusuna köçürmək icazəsi verilsin?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> audio fayl zibil qutusuna köçürülür…</item>
- <item quantity="one">Audio fayl zibil qutusuna köçürülür…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> videonu zibil qutusuna köçürmək icazəsi verilsin?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu videonu zibil qutusuna köçürmək icazəsi verilsin?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> video zibil qutusuna köçürülür…</item>
- <item quantity="one">Video zibil qutusuna köçürülür…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> fotonu zibil qutusuna köçürmək icazəsi verilsin?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu fotonu zibil qutusuna köçürmək icazəsi verilsin?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> foto zibil qutusuna köçürülür…</item>
- <item quantity="one">Foto zibil qutusuna köçürülür…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> elementi zibil qutusuna köçürmək icazəsi verilsin?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu elementi zibil qutusuna köçürmək icazəsi verilsin?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> element zibil qutusuna köçürülür…</item>
- <item quantity="one">Element zibil qutusuna köçürülür…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> audio faylı zibil qutusundan çıxarmaq icazəsi verilsin?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu audio faylı zibil qutusundan çıxarmaq icazəsi verilsin?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> audio fayl zibil qutusundan çıxarılır…</item>
- <item quantity="one">Audio fayl zibil qutusundan çıxarılır…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> videonu zibil qutusundan çıxarmaq icazəsi verilsin?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu videonu zibil qutusundan çıxarmaq icazəsi verilsin?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> video zibil qutusundan çıxarılır…</item>
- <item quantity="one">Video zibil qutusundan çıxarılır…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> fotonu zibil qutusundan çıxarmaq icazəsi verilsin?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu fotonu zibil qutusundan çıxarmaq icazəsi verilsin?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> foto zibil qutusundan çıxarılır…</item>
- <item quantity="one">Foto zibil qutusundan çıxarılır…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> elementi zibil qutusundan çıxarmaq icazəsi verilsin?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu elementi zibil qutusundan çıxarmaq icazəsi verilsin?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> element zibil qutusundan çıxarılır…</item>
- <item quantity="one">Element zibil qutusundan çıxarılır…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> audio faylı silmək icazəsi verilsin?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu audio faylı silmək icazəsi verilsin?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> audio fayl silinir…</item>
- <item quantity="one">Audio fayl silinir…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> videonu silmək icazəsi verilsin?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu videonu silmək icazəsi verilsin?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> video silinir…</item>
- <item quantity="one">Video silinir…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> fotonu silmək icazəsi verilsin?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu fotonu silmək icazəsi verilsin?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> foto silinir…</item>
- <item quantity="one">Foto silinir…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> elementi silmək icazəsi verilsin?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu elementi silmək icazəsi verilsin?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> element silinir…</item>
- <item quantity="one">Element silinir…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Əlavə edin"</string>
+ <string name="deselect" msgid="4297825044827769490">"Seçimi ləğv edin"</string>
+ <string name="deselected" msgid="8488133193326208475">"Seçimi ləğv edilib"</string>
+ <string name="select" msgid="2704765470563027689">"Seçin"</string>
+ <string name="selected" msgid="9151797369975828124">"Seçilib"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Maksimum <xliff:g id="COUNT_0">^1</xliff:g> element seçin}other{Maksimum <xliff:g id="COUNT_1">^1</xliff:g> element seçin}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Son"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Foto və ya video yoxdur"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Albom yoxdur"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Seçilənə baxın"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Fotolar"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albomlar"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Önbaxış"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"İş profilinə keçirin"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Şəxsi profilə keçirin"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Admininiz tərəfindən bloklanıb"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Şəxsi tətbiqdən iş datasına girişə icazə verilmir"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"İş tətbiqindən şəxsi dataya girişə icazə verilmir"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"İş tətbiqləri durdurulub"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"İş şəkillərini açmaq üçün iş tətbiqlərinizi aktiv edib yenidən cəhd edin"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Bu tətbiq yalnız seçdiyiniz fotolara giriş edə bilər"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> element}other{<xliff:g id="COUNT_1">^1</xliff:g> element}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Əlavə edin (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Endirmələr"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Sevimlilər"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Skrinşotlar"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Hərəkətli Foto"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> <xliff:g id="TIME">%2$s</xliff:g> tarixində çəkilib"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Video <xliff:g id="DURATION">%2$s</xliff:g> müddətində bu vaxt çəkilib: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Foto"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Hərəkətli Foto"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Videonu səssiz edin"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Videonu səssiz rejimdən çıxarın"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Videonu oxudun"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Videonu durdurun"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Bulud mediası indi buradan əlçatandır: <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"seçilməyib"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu audio fayla dəyişiklik etmək icazəsi verilsin?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> audio fayla dəyişiklik etmək icazəsi verilsin?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Audio fayl dəyişdirilir…}other{<xliff:g id="COUNT">^1</xliff:g> audio fayl dəyişdirilir…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu videoya dəyişiklik etmək icazəsi verilsin?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> videoya dəyişiklik etmək icazəsi verilsin?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Video dəyişdirilir…}other{<xliff:g id="COUNT">^1</xliff:g> video dəyişdirilir…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu fotoya dəyişiklik etmək icazəsi verilsin?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> fotoya dəyişiklik etmək icazəsi verilsin?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Foto dəyişdirilir…}other{<xliff:g id="COUNT">^1</xliff:g> foto dəyişdirilir…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu elementə dəyişiklik etmək icazəsi verilsin?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> elementə dəyişiklik etmək icazəsi verilsin?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Element dəyişdirilir…}other{<xliff:g id="COUNT">^1</xliff:g> element dəyişdirilir…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu audio faylı zibil qutusuna köçürmək icazəsi verilsin?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> audio faylı zibil qutusuna köçürmək icazəsi verilsin?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Audio fayl zibil qutusuna köçürülür…}other{<xliff:g id="COUNT">^1</xliff:g> audio fayl zibil qutusuna köçürülür…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu videonu zibil qutusuna köçürmək icazəsi verilsin?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> videonu zibil qutusuna köçürmək icazəsi verilsin?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Video zibil qutusuna köçürülür…}other{<xliff:g id="COUNT">^1</xliff:g> video zibil qutusuna köçürülür…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu fotonu zibil qutusuna köçürmək icazəsi verilsin?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> fotonu zibil qutusuna köçürmək icazəsi verilsin?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Foto zibil qutusuna köçürülür…}other{<xliff:g id="COUNT">^1</xliff:g> foto zibil qutusuna köçürülür…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu elementi zibil qutusuna köçürmək icazəsi verilsin?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> elementi zibil qutusuna köçürmək icazəsi verilsin?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Element zibil qutusuna köçürülür…}other{<xliff:g id="COUNT">^1</xliff:g> element zibil qutusuna köçürülür…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu audio faylı zibil qutusundan çıxarmaq icazəsi verilsin?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> audio faylı zibil qutusundan çıxarmaq icazəsi verilsin?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Audio fayl zibil qutusundan çıxarılır…}other{<xliff:g id="COUNT">^1</xliff:g> audio fayl zibil qutusundan çıxarılır…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu videonu zibil qutusundan çıxarmaq icazəsi verilsin?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> videonu zibil qutusundan çıxarmaq icazəsi verilsin?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Video zibil qutusundan çıxarılır…}other{<xliff:g id="COUNT">^1</xliff:g> video zibil qutusundan çıxarılır…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu fotonu zibil qutusundan çıxarmaq icazəsi verilsin?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> fotonu zibil qutusundan çıxarmaq icazəsi verilsin?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Foto zibil qutusundan çıxarılır…}other{<xliff:g id="COUNT">^1</xliff:g> foto zibil qutusundan çıxarılır…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu elementi zibil qutusundan çıxarmaq icazəsi verilsin?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> elementi zibil qutusundan çıxarmaq icazəsi verilsin?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Element zibil qutusundan çıxarılır…}other{<xliff:g id="COUNT">^1</xliff:g> element zibil qutusundan çıxarılır…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu audio faylı silmək icazəsi verilsin?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> audio faylı silmək icazəsi verilsin?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Audio fayl silinir…}other{<xliff:g id="COUNT">^1</xliff:g> audio fayl silinir…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu videonu silmək icazəsi verilsin?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> videonu silmək icazəsi verilsin?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Video silinir…}other{<xliff:g id="COUNT">^1</xliff:g> video silinir…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu fotonu silmək icazəsi verilsin?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> fotonu silmək icazəsi verilsin?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Foto silinir…}other{<xliff:g id="COUNT">^1</xliff:g> foto silinir…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> tətbiqinə bu elementi silmək icazəsi verilsin?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> tətbiqinə <xliff:g id="COUNT">^2</xliff:g> elementi silmək icazəsi verilsin?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Element silinir…}other{<xliff:g id="COUNT">^1</xliff:g> element silinir…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> media fayllarını emal edə bilmir"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Media emalı ləğv edilib"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Media emalı zamanı xəta oldu"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index a4a20c8..632006c 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Mediji"</string>
<string name="storage_description" msgid="4081716890357580107">"Lokalni memorijski prostor"</string>
<string name="app_label" msgid="9035307001052716210">"Memorijski prostor za medije"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"Birač slika"</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>
@@ -29,182 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Nastavi"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Dozvoli"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Odbij"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">i još <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="few">i još <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">i još <xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">I još <xliff:g id="COUNT_1">^1</xliff:g> stavka</item>
- <item quantity="few">I još <xliff:g id="COUNT_1">^1</xliff:g> stavke</item>
- <item quantity="other">I još <xliff:g id="COUNT_1">^1</xliff:g> stavki</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{i još <xliff:g id="COUNT_0">^1</xliff:g>}one{i još <xliff:g id="COUNT_1">^1</xliff:g>}few{i još <xliff:g id="COUNT_1">^1</xliff:g>}other{i još <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{I još <xliff:g id="COUNT_0">^1</xliff:g> stavka}one{I još <xliff:g id="COUNT_1">^1</xliff:g> stavka}few{I još <xliff:g id="COUNT_1">^1</xliff:g> stavke}other{I još <xliff:g id="COUNT_1">^1</xliff:g> stavki}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Obrišite privremene datoteke aplikacija"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> želi da obriše neke privremene datoteke. Ovo može da dovede do povećane potrošnje baterije ili mobilnih podataka."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Brišu se privremene datoteke aplikacija…"</string>
<string name="clear" msgid="5524638938415865915">"Obriši"</string>
<string name="allow" msgid="8885707816848569619">"Dozvoli"</string>
<string name="deny" msgid="6040983710442068936">"Odbij"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izmeni <xliff:g id="COUNT">^2</xliff:g> audio datoteku?</item>
- <item quantity="few">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izmeni <xliff:g id="COUNT">^2</xliff:g> audio datoteke?</item>
- <item quantity="other">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izmeni <xliff:g id="COUNT">^2</xliff:g> audio datoteka?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one">Menja se <xliff:g id="COUNT">^1</xliff:g> audio fajl…</item>
- <item quantity="few">Menjaju se <xliff:g id="COUNT">^1</xliff:g> audio fajla…</item>
- <item quantity="other">Menja se <xliff:g id="COUNT">^1</xliff:g> audio fajlova…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izmeni <xliff:g id="COUNT">^2</xliff:g> video?</item>
- <item quantity="few">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izmeni <xliff:g id="COUNT">^2</xliff:g> video snimka?</item>
- <item quantity="other">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izmeni <xliff:g id="COUNT">^2</xliff:g> video snimaka?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one">Menja se <xliff:g id="COUNT">^1</xliff:g> video…</item>
- <item quantity="few">Menjaju se <xliff:g id="COUNT">^1</xliff:g> video snimka…</item>
- <item quantity="other">Menja se <xliff:g id="COUNT">^1</xliff:g> video snimaka…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izmeni <xliff:g id="COUNT">^2</xliff:g> sliku?</item>
- <item quantity="few">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izmeni <xliff:g id="COUNT">^2</xliff:g> slike?</item>
- <item quantity="other">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izmeni <xliff:g id="COUNT">^2</xliff:g> slika?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one">Menja se <xliff:g id="COUNT">^1</xliff:g> slika…</item>
- <item quantity="few">Menjaju se <xliff:g id="COUNT">^1</xliff:g> slike…</item>
- <item quantity="other">Menja se <xliff:g id="COUNT">^1</xliff:g> slika…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izmeni <xliff:g id="COUNT">^2</xliff:g> stavku?</item>
- <item quantity="few">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izmeni <xliff:g id="COUNT">^2</xliff:g> stavke?</item>
- <item quantity="other">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izmeni <xliff:g id="COUNT">^2</xliff:g> stavki?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one">Menja se <xliff:g id="COUNT">^1</xliff:g> stavka…</item>
- <item quantity="few">Menjaju se <xliff:g id="COUNT">^1</xliff:g> stavke…</item>
- <item quantity="other">Menja se <xliff:g id="COUNT">^1</xliff:g> stavki…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> audio datoteku u otpad?</item>
- <item quantity="few">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> audio datoteke u otpad?</item>
- <item quantity="other">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> audio datoteka u otpad?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> audio fajl se premešta u otpad…</item>
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> audio fajla se premeštaju u otpad…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> audio fajlova se premešta u otpad…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> video u otpad?</item>
- <item quantity="few">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> video snimka u otpad?</item>
- <item quantity="other">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> video snimaka u otpad?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> video se premešta u otpad…</item>
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> video snimka se premeštaju u otpad…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> video snimaka se premešta u otpad…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> sliku u otpad?</item>
- <item quantity="few">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> slike u otpad?</item>
- <item quantity="other">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> slika u otpad?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> slika se premešta u otpad…</item>
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> slike se premeštaju u otpad…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> slika se premešta u otpad…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> stavku u otpad?</item>
- <item quantity="few">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> stavke u otpad?</item>
- <item quantity="other">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> stavki u otpad?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> stavka se premešta u otpad…</item>
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> stavke se premeštaju u otpad…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> stavki se premešta u otpad…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> audio datoteku iz otpada?</item>
- <item quantity="few">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> audio datoteke iz otpada?</item>
- <item quantity="other">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> audio datoteka iz otpada?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> audio fajl se premešta iz otpada…</item>
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> audio fajla se premeštaju iz otpada…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> audio fajlova se premešta iz otpada…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> video iz otpada?</item>
- <item quantity="few">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> video snimka iz otpada?</item>
- <item quantity="other">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> video snimaka iz otpada?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> video se premešta iz otpada…</item>
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> video snimka se premeštaju iz otpada…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> video snimaka se premešta iz otpada…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> sliku iz otpada?</item>
- <item quantity="few">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> slike iz otpada?</item>
- <item quantity="other">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> slika iz otpada?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> slika se premešta iz otpada…</item>
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> slike se premeštaju iz otpada…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> slika se premešta iz otpada…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> stavku iz otpada?</item>
- <item quantity="few">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> stavke iz otpada?</item>
- <item quantity="other">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> stavki iz otpada?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> stavka se premešta iz otpada…</item>
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> stavke se premeštaju iz otpada…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> stavki se premešta iz otpada…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> audio datoteku?</item>
- <item quantity="few">Želite li da dozvolite <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> audio datoteke?</item>
- <item quantity="other">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> audio datoteka?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one">Briše se <xliff:g id="COUNT">^1</xliff:g> audio fajl…</item>
- <item quantity="few">Brišu se <xliff:g id="COUNT">^1</xliff:g> audio fajla…</item>
- <item quantity="other">Briše se <xliff:g id="COUNT">^1</xliff:g> audio fajlova…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> video?</item>
- <item quantity="few">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> video snimka?</item>
- <item quantity="other">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> video snimaka?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one">Briše se <xliff:g id="COUNT">^1</xliff:g> video…</item>
- <item quantity="few">Brišu se <xliff:g id="COUNT">^1</xliff:g> video snimka…</item>
- <item quantity="other">Briše se <xliff:g id="COUNT">^1</xliff:g> video snimaka…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> sliku?</item>
- <item quantity="few">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> slike?</item>
- <item quantity="other">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> slika?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one">Briše se <xliff:g id="COUNT">^1</xliff:g> slika…</item>
- <item quantity="few">Brišu se <xliff:g id="COUNT">^1</xliff:g> slike…</item>
- <item quantity="other">Briše se <xliff:g id="COUNT">^1</xliff:g> slika…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> stavku?</item>
- <item quantity="few">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> stavke?</item>
- <item quantity="other">Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> stavki?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one">Briše se <xliff:g id="COUNT">^1</xliff:g> stavka…</item>
- <item quantity="few">Brišu se <xliff:g id="COUNT">^1</xliff:g> stavke…</item>
- <item quantity="other">Briše se <xliff:g id="COUNT">^1</xliff:g> stavki…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Dodaj"</string>
+ <string name="deselect" msgid="4297825044827769490">"Opozovi izbor"</string>
+ <string name="deselected" msgid="8488133193326208475">"Opozvan je izbor"</string>
+ <string name="select" msgid="2704765470563027689">"Izaberi"</string>
+ <string name="selected" msgid="9151797369975828124">"Izabrano"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Izaberite najviše <xliff:g id="COUNT_0">^1</xliff:g> stavku}one{Izaberite najviše <xliff:g id="COUNT_1">^1</xliff:g> stavku}few{Izaberite najviše <xliff:g id="COUNT_1">^1</xliff:g> stavke}other{Izaberite najviše <xliff:g id="COUNT_1">^1</xliff:g> stavki}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Nedavno"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Nema slika niti video snimaka"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Nema albuma"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Prikaži izabrano"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Slike"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albumi"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Pregled"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Pređi na poslovni profil"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Pređi na lični profil"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Blokira administrator"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Pristup podacima o poslu iz lične aplikacije nije dozvoljen"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Pristup ličnim podacima iz poslovne aplikacije nije dozvoljen"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Poslovne aplikacije su pauzirane"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Da biste otvorili poslovne slike, uključite poslovne aplikacije, pa probajte ponovo"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Ova aplikacija može da pristupa samo slikama koje izaberete"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> stavka}one{<xliff:g id="COUNT_1">^1</xliff:g> stavka}few{<xliff:g id="COUNT_1">^1</xliff:g> stavke}other{<xliff:g id="COUNT_1">^1</xliff:g> stavki}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Dodaj (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Preuzeto"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Omiljeno"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Snimci ekrana"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Slike u pokretu"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g>: snimljeno <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Video je snimljen <xliff:g id="TIME">%1$s</xliff:g> i traje <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Slika"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Slika u pokretu"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Isključi zvuk videa"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Uključi zvuk videa"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Pusti video"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Pauziraj video"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"<xliff:g id="PKG_NAME">%1$s</xliff:g> sada nudi medijski sadržaj u klaudu"</string>
+ <string name="not_selected" msgid="2244008151669896758">"nije izabrano"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Želite li da dozvolite da <xliff:g id="APP_NAME_0">^1</xliff:g> izmeni ovaj audio fajl?}one{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izmeni <xliff:g id="COUNT">^2</xliff:g> audio fajl?}few{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izmeni <xliff:g id="COUNT">^2</xliff:g> audio fajla?}other{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izmeni <xliff:g id="COUNT">^2</xliff:g> audio fajlova?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Menja se audio fajl…}one{Menja se <xliff:g id="COUNT">^1</xliff:g> audio fajl…}few{Menjaju se <xliff:g id="COUNT">^1</xliff:g> audio fajla…}other{Menja se <xliff:g id="COUNT">^1</xliff:g> audio fajlova…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Želite li da dozvolite da <xliff:g id="APP_NAME_0">^1</xliff:g> izmeni ovaj video?}one{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izmeni <xliff:g id="COUNT">^2</xliff:g> video?}few{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izmeni <xliff:g id="COUNT">^2</xliff:g> video snimka?}other{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izmeni <xliff:g id="COUNT">^2</xliff:g> video snimaka?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Menja se video…}one{Menja se <xliff:g id="COUNT">^1</xliff:g> video…}few{Menjaju se <xliff:g id="COUNT">^1</xliff:g> video snimka…}other{Menja se <xliff:g id="COUNT">^1</xliff:g> video snimaka…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Želite li da dozvolite da <xliff:g id="APP_NAME_0">^1</xliff:g> izmeni ovu sliku?}one{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izmeni <xliff:g id="COUNT">^2</xliff:g> sliku?}few{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izmeni <xliff:g id="COUNT">^2</xliff:g> slike?}other{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izmeni <xliff:g id="COUNT">^2</xliff:g> slika?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Menja se slika…}one{Menja se <xliff:g id="COUNT">^1</xliff:g> slika…}few{Menjaju se <xliff:g id="COUNT">^1</xliff:g> slike…}other{Menja se <xliff:g id="COUNT">^1</xliff:g> slika…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Želite li da dozvolite da <xliff:g id="APP_NAME_0">^1</xliff:g> izmeni ovu stavku?}one{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izmeni <xliff:g id="COUNT">^2</xliff:g> stavku?}few{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izmeni <xliff:g id="COUNT">^2</xliff:g> stavke?}other{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izmeni <xliff:g id="COUNT">^2</xliff:g> stavki?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Menja se stavka…}one{Menja se <xliff:g id="COUNT">^1</xliff:g> stavka…}few{Menjaju se <xliff:g id="COUNT">^1</xliff:g> stavke…}other{Menja se <xliff:g id="COUNT">^1</xliff:g> stavki…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Želite li da dozvolite da <xliff:g id="APP_NAME_0">^1</xliff:g> premesti ovaj audio fajl u otpad?}one{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> audio fajl u otpad?}few{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> audio fajla u otpad?}other{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> audio fajlova u otpad?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Audio fajl se premešta u otpad…}one{<xliff:g id="COUNT">^1</xliff:g> audio fajl se premešta u otpad…}few{<xliff:g id="COUNT">^1</xliff:g> audio fajla se premeštaju u otpad…}other{<xliff:g id="COUNT">^1</xliff:g> audio fajlova se premešta u otpad…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Želite li da dozvolite da <xliff:g id="APP_NAME_0">^1</xliff:g> premesti ovaj video u otpad?}one{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> video u otpad?}few{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> video snimka u otpad?}other{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> video snimaka u otpad?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Video se premešta u otpad…}one{<xliff:g id="COUNT">^1</xliff:g> video se premešta u otpad…}few{<xliff:g id="COUNT">^1</xliff:g> video snimka se premeštaju u otpad…}other{<xliff:g id="COUNT">^1</xliff:g> video snimaka se premešta u otpad…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Želite li da dozvolite da <xliff:g id="APP_NAME_0">^1</xliff:g> premesti ovu sliku u otpad?}one{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> sliku u otpad?}few{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> slike u otpad?}other{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> slika u otpad?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Slika se premešta u otpad…}one{<xliff:g id="COUNT">^1</xliff:g> slika se premešta u otpad…}few{<xliff:g id="COUNT">^1</xliff:g> slike se premeštaju u otpad…}other{<xliff:g id="COUNT">^1</xliff:g> slika se premešta u otpad…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Želite li da dozvolite da <xliff:g id="APP_NAME_0">^1</xliff:g> premesti ovu stavku u otpad?}one{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> stavku u otpad?}few{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> stavke u otpad?}other{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> stavki u otpad?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Stavka se premešta u otpad…}one{<xliff:g id="COUNT">^1</xliff:g> stavka se premešta u otpad…}few{<xliff:g id="COUNT">^1</xliff:g> stavke se premeštaju u otpad…}other{<xliff:g id="COUNT">^1</xliff:g> stavki se premešta u otpad…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Želite li da dozvolite da <xliff:g id="APP_NAME_0">^1</xliff:g> premesti ovaj audio fajl iz otpada?}one{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> audio fajl iz otpada?}few{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> audio fajla iz otpada?}other{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> audio fajlova iz otpada?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Audio fajl se premešta iz otpada…}one{<xliff:g id="COUNT">^1</xliff:g> audio fajl se premešta iz otpada…}few{<xliff:g id="COUNT">^1</xliff:g> audio fajla se premeštaju iz otpada…}other{<xliff:g id="COUNT">^1</xliff:g> audio fajlova se premešta iz otpada…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Želite li da dozvolite da <xliff:g id="APP_NAME_0">^1</xliff:g> premesti ovaj video iz otpada?}one{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> video iz otpada?}few{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> video snimka iz otpada?}other{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> video snimaka iz otpada?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Video se premešta iz otpada…}one{<xliff:g id="COUNT">^1</xliff:g> video se premešta iz otpada…}few{<xliff:g id="COUNT">^1</xliff:g> video snimka se premeštaju iz otpada…}other{<xliff:g id="COUNT">^1</xliff:g> video snimaka se premešta iz otpada…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Želite li da dozvolite da <xliff:g id="APP_NAME_0">^1</xliff:g> premesti ovu sliku iz otpada?}one{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> sliku iz otpada?}few{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> slike iz otpada?}other{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> slika iz otpada?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Slika se premešta iz otpada…}one{<xliff:g id="COUNT">^1</xliff:g> slika se premešta iz otpada…}few{<xliff:g id="COUNT">^1</xliff:g> slike se premeštaju iz otpada…}other{<xliff:g id="COUNT">^1</xliff:g> slika se premešta iz otpada…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Želite li da dozvolite da <xliff:g id="APP_NAME_0">^1</xliff:g> premesti ovu stavku iz otpada?}one{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> stavku iz otpada?}few{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> stavke iz otpada?}other{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> premesti <xliff:g id="COUNT">^2</xliff:g> stavki iz otpada?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Stavka se premešta iz otpada…}one{<xliff:g id="COUNT">^1</xliff:g> stavka se premešta iz otpada…}few{<xliff:g id="COUNT">^1</xliff:g> stavke se premeštaju iz otpada…}other{<xliff:g id="COUNT">^1</xliff:g> stavki se premešta iz otpada…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Želite li da dozvolite da <xliff:g id="APP_NAME_0">^1</xliff:g> izbriše ovaj audio fajl?}one{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> audio fajl?}few{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> audio fajla?}other{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> audio fajlova?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Briše se audio fajl…}one{Briše se <xliff:g id="COUNT">^1</xliff:g> audio fajl…}few{Brišu se <xliff:g id="COUNT">^1</xliff:g> audio fajla…}other{Briše se <xliff:g id="COUNT">^1</xliff:g> audio fajlova…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Želite li da dozvolite da <xliff:g id="APP_NAME_0">^1</xliff:g> izbriše ovaj video?}one{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> video?}few{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> video snimka?}other{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> video snimaka?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Briše se video…}one{Briše se <xliff:g id="COUNT">^1</xliff:g> video…}few{Brišu se <xliff:g id="COUNT">^1</xliff:g> video snimka…}other{Briše se <xliff:g id="COUNT">^1</xliff:g> video snimaka…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Želite li da dozvolite da <xliff:g id="APP_NAME_0">^1</xliff:g> izbriše ovu sliku?}one{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> sliku?}few{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> slike?}other{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> slika?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Briše se slika…}one{Briše se <xliff:g id="COUNT">^1</xliff:g> slika…}few{Brišu se <xliff:g id="COUNT">^1</xliff:g> slike…}other{Briše se <xliff:g id="COUNT">^1</xliff:g> slika…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Želite li da dozvolite da <xliff:g id="APP_NAME_0">^1</xliff:g> izbriše ovu stavku?}one{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> stavku?}few{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> stavke?}other{Želite li da dozvolite da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> stavki?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Briše se stavka…}one{Briše se <xliff:g id="COUNT">^1</xliff:g> stavka…}few{Brišu se <xliff:g id="COUNT">^1</xliff:g> stavke…}other{Briše se <xliff:g id="COUNT">^1</xliff:g> stavki…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> ne može da obradi medijske fajlove"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Obrada medija je otkazana"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Greška pri obradi medija"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 18c1dcc..5e535f2 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Медыя"</string>
<string name="storage_description" msgid="4081716890357580107">"Лакальнае сховішча"</string>
<string name="app_label" msgid="9035307001052716210">"Медыясховішча"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"Інструмент выбару фота"</string>
<string name="artist_label" msgid="8105600993099120273">"Выканаўца"</string>
<string name="unknown" msgid="2059049215682829375">"Невядома"</string>
<string name="root_images" msgid="5861633549189045666">"Відарысы"</string>
@@ -29,216 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Працягнуць"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Дазволіць"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Адмовіць"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="few">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="many">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">Плюс <xliff:g id="COUNT_1">^1</xliff:g> дадатковы элемент</item>
- <item quantity="few">Плюс <xliff:g id="COUNT_1">^1</xliff:g> дадатковыя элементы</item>
- <item quantity="many">Плюс <xliff:g id="COUNT_1">^1</xliff:g> дадатковых элементаў</item>
- <item quantity="other">Плюс <xliff:g id="COUNT_1">^1</xliff:g> дадатковага элемента</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}one{+<xliff:g id="COUNT_1">^1</xliff:g>}few{+<xliff:g id="COUNT_1">^1</xliff:g>}many{+<xliff:g id="COUNT_1">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Плюс <xliff:g id="COUNT_0">^1</xliff:g> дадатковы элемент}one{Плюс <xliff:g id="COUNT_1">^1</xliff:g> дадатковы элемент}few{Плюс <xliff:g id="COUNT_1">^1</xliff:g> дадатковыя элементы}many{Плюс <xliff:g id="COUNT_1">^1</xliff:g> дадатковых элементаў}other{Плюс <xliff:g id="COUNT_1">^1</xliff:g> дадатковага элемента}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Ачысціць часовыя файлы праграм"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> запытвае дазвол на выдаленне некаторых часовых файлаў. У сувязі з гэтым можа павялічыцца ўзровень выкарыстання акумулятара ці памер сотавай перадачы даных."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Выдаляюцца часовыя файлы праграм…"</string>
<string name="clear" msgid="5524638938415865915">"Ачысціць"</string>
<string name="allow" msgid="8885707816848569619">"Дазволіць"</string>
<string name="deny" msgid="6040983710442068936">"Адмовіць"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайл?</item>
- <item quantity="few">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайлы?</item>
- <item quantity="many">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайлаў?</item>
- <item quantity="other">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайла?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one">Змяняецца <xliff:g id="COUNT">^1</xliff:g> аўдыяфайл…</item>
- <item quantity="few">Змяняюцца <xliff:g id="COUNT">^1</xliff:g> аўдыяфайлы…</item>
- <item quantity="many">Змяняюцца <xliff:g id="COUNT">^1</xliff:g> аўдыяфайлаў…</item>
- <item quantity="other">Змяняюцца <xliff:g id="COUNT">^1</xliff:g> аўдыяфайла…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> відэа?</item>
- <item quantity="few">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> відэа?</item>
- <item quantity="many">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> відэа?</item>
- <item quantity="other">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> відэа?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one">Змяняецца <xliff:g id="COUNT">^1</xliff:g> відэа…</item>
- <item quantity="few">Змяняюцца <xliff:g id="COUNT">^1</xliff:g> відэа…</item>
- <item quantity="many">Змяняюцца <xliff:g id="COUNT">^1</xliff:g> відэа…</item>
- <item quantity="other">Змяняюцца <xliff:g id="COUNT">^1</xliff:g> відэа…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> фота?</item>
- <item quantity="few">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> фота?</item>
- <item quantity="many">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> фота?</item>
- <item quantity="other">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> фота?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one">Змяняецца <xliff:g id="COUNT">^1</xliff:g> фота…</item>
- <item quantity="few">Змяняюцца <xliff:g id="COUNT">^1</xliff:g> фота…</item>
- <item quantity="many">Змяняюцца <xliff:g id="COUNT">^1</xliff:g> фота…</item>
- <item quantity="other">Змяняюцца <xliff:g id="COUNT">^1</xliff:g> фота…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> элемент?</item>
- <item quantity="few">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> элементы?</item>
- <item quantity="many">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> элементаў?</item>
- <item quantity="other">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> элемента?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one">Змяняецца <xliff:g id="COUNT">^1</xliff:g> элемент…</item>
- <item quantity="few">Змяняюцца <xliff:g id="COUNT">^1</xliff:g> элементы…</item>
- <item quantity="many">Змяняюцца <xliff:g id="COUNT">^1</xliff:g> элементаў…</item>
- <item quantity="other">Змяняюцца <xliff:g id="COUNT">^1</xliff:g> элемента…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайл у сметніцу?</item>
- <item quantity="few">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайлы ў сметніцу?</item>
- <item quantity="many">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайлаў у сметніцу?</item>
- <item quantity="other">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайла ў сметніцу?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> аўдыяфайл перамяшчаецца ў сметніцу…</item>
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> аўдыяфайлы перамяшчаюцца ў сметніцу…</item>
- <item quantity="many"><xliff:g id="COUNT">^1</xliff:g> аўдыяфайлаў перамяшчаюцца ў сметніцу…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> аўдыяфайла перамяшчаюцца ў сметніцу…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> відэа ў сметніцу?</item>
- <item quantity="few">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> відэа ў сметніцу?</item>
- <item quantity="many">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> відэа ў сметніцу?</item>
- <item quantity="other">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> відэа ў сметніцу?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> відэа перамяшчаецца ў сметніцу…</item>
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> відэа перамяшчаюцца ў сметніцу…</item>
- <item quantity="many"><xliff:g id="COUNT">^1</xliff:g> відэа перамяшчаюцца ў сметніцу…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> відэа перамяшчаюцца ў сметніцу…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> фота ў сметніцу?</item>
- <item quantity="few">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> фота ў сметніцу?</item>
- <item quantity="many">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> фота ў сметніцу?</item>
- <item quantity="other">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> фота ў сметніцу?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> фота перамяшчаецца ў сметніцу…</item>
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> фота перамяшчаюцца ў сметніцу…</item>
- <item quantity="many"><xliff:g id="COUNT">^1</xliff:g> фота перамяшчаюцца ў сметніцу…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> фота перамяшчаюцца ў сметніцу…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> элемент у сметніцу?</item>
- <item quantity="few">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> элементы ў сметніцу?</item>
- <item quantity="many">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> элементаў у сметніцу?</item>
- <item quantity="other">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> элемента ў сметніцу?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> элемент перамяшчаецца ў сметніцу…</item>
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> элементы перамяшчаюцца ў сметніцу…</item>
- <item quantity="many"><xliff:g id="COUNT">^1</xliff:g> элементаў перамяшчаюцца ў сметніцу…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> элемента перамяшчаюцца ў сметніцу…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайл са сметніцы?</item>
- <item quantity="few">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайлы са сметніцы?</item>
- <item quantity="many">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайлаў са сметніцы?</item>
- <item quantity="other">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайла са сметніцы?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> аўдыяфайл перамяшчаецца са сметніцы…</item>
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> аўдыяфайлы перамяшчаюцца са сметніцы…</item>
- <item quantity="many"><xliff:g id="COUNT">^1</xliff:g> аўдыяфайлаў перамяшчаюцца са сметніцы…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> аўдыяфайла перамяшчаюцца са сметніцы…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> відэа са сметніцы?</item>
- <item quantity="few">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> відэа са сметніцы?</item>
- <item quantity="many">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> відэа са сметніцы?</item>
- <item quantity="other">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> відэа са сметніцы?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> відэа перамяшчаецца са сметніцы…</item>
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> відэа перамяшчаюцца са сметніцы…</item>
- <item quantity="many"><xliff:g id="COUNT">^1</xliff:g> відэа перамяшчаюцца са сметніцы…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> відэа перамяшчаюцца са сметніцы…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> фота са сметніцы?</item>
- <item quantity="few">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> фота са сметніцы?</item>
- <item quantity="many">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> фота са сметніцы?</item>
- <item quantity="other">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> фота са сметніцы?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> фота перамяшчаецца са сметніцы…</item>
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> фота перамяшчаюцца са сметніцы…</item>
- <item quantity="many"><xliff:g id="COUNT">^1</xliff:g> фота перамяшчаюцца са сметніцы…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> фота перамяшчаюцца са сметніцы…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> элемент са сметніцы?</item>
- <item quantity="few">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> элементы са сметніцы?</item>
- <item quantity="many">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> элементаў са сметніцы?</item>
- <item quantity="other">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> элемента са сметніцы?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> элемент перамяшчаецца са сметніцы…</item>
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> элементы перамяшчаюцца са сметніцы…</item>
- <item quantity="many"><xliff:g id="COUNT">^1</xliff:g> элементаў перамяшчаюцца са сметніцы…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> элемента перамяшчаюцца са сметніцы…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайл?</item>
- <item quantity="few">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайлы?</item>
- <item quantity="many">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайлаў?</item>
- <item quantity="other">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайла?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one">Выдаляецца <xliff:g id="COUNT">^1</xliff:g> аўдыяфайл…</item>
- <item quantity="few">Выдаляюцца <xliff:g id="COUNT">^1</xliff:g> аўдыяфайлы…</item>
- <item quantity="many">Выдаляюцца <xliff:g id="COUNT">^1</xliff:g> аўдыяфайлаў…</item>
- <item quantity="other">Выдаляюцца <xliff:g id="COUNT">^1</xliff:g> аўдыяфайла…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> відэа?</item>
- <item quantity="few">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> відэа?</item>
- <item quantity="many">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> відэа?</item>
- <item quantity="other">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> відэа?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one">Выдаляецца <xliff:g id="COUNT">^1</xliff:g> відэа…</item>
- <item quantity="few">Выдаляюцца <xliff:g id="COUNT">^1</xliff:g> відэа…</item>
- <item quantity="many">Выдаляюцца <xliff:g id="COUNT">^1</xliff:g> відэа…</item>
- <item quantity="other">Выдаляюцца <xliff:g id="COUNT">^1</xliff:g> відэа…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> фота?</item>
- <item quantity="few">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> фота?</item>
- <item quantity="many">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> фота?</item>
- <item quantity="other">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> фота?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one">Выдаляецца <xliff:g id="COUNT">^1</xliff:g> фота…</item>
- <item quantity="few">Выдаляюцца <xliff:g id="COUNT">^1</xliff:g> фота…</item>
- <item quantity="many">Выдаляюцца <xliff:g id="COUNT">^1</xliff:g> фота…</item>
- <item quantity="other">Выдаляюцца <xliff:g id="COUNT">^1</xliff:g> фота…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> элемент?</item>
- <item quantity="few">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> элементы?</item>
- <item quantity="many">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> элементаў?</item>
- <item quantity="other">Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> элемента?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one">Выдаляецца <xliff:g id="COUNT">^1</xliff:g> элемент…</item>
- <item quantity="few">Выдаляюцца <xliff:g id="COUNT">^1</xliff:g> элементы…</item>
- <item quantity="many">Выдаляюцца <xliff:g id="COUNT">^1</xliff:g> элементаў…</item>
- <item quantity="other">Выдаляюцца <xliff:g id="COUNT">^1</xliff:g> элемента…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Дадаць"</string>
+ <string name="deselect" msgid="4297825044827769490">"Адмяніць выбар"</string>
+ <string name="deselected" msgid="8488133193326208475">"Выбар скасаваны"</string>
+ <string name="select" msgid="2704765470563027689">"Выбраць"</string>
+ <string name="selected" msgid="9151797369975828124">"Выбрана"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Выберыце да <xliff:g id="COUNT_0">^1</xliff:g> элемента}one{Выберыце да <xliff:g id="COUNT_1">^1</xliff:g> элемента}few{Выберыце да <xliff:g id="COUNT_1">^1</xliff:g> элементаў}many{Выберыце да <xliff:g id="COUNT_1">^1</xliff:g> элементаў}other{Выберыце да <xliff:g id="COUNT_1">^1</xliff:g> элемента}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Нядаўнія"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Няма фота і відэа"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Няма альбомаў"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Праглядзець выбранае"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Фота"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Альбомы"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Перадпрагляд"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Пераключыцца на працоўны"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Пераключыцца на асабісты"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Заблакіравана вашым адміністратарам"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Доступ да працоўных даных з асабістай праграмы забаронены"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Доступ да асабістых даных з працоўнай праграмы забаронены"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Працоўныя праграмы прыпынены"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Каб адкрыць працоўныя фота, уключыце працоўныя праграмы і паўтарыце спробу"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Гэта праграма можа мець доступ толькі да выбраных вамі фота"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> элемент}one{<xliff:g id="COUNT_1">^1</xliff:g> элемент}few{<xliff:g id="COUNT_1">^1</xliff:g> элементы}many{<xliff:g id="COUNT_1">^1</xliff:g> элементаў}other{<xliff:g id="COUNT_1">^1</xliff:g> элемента}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Дадаць (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Камера"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Спампоўкі"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Абранае"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Здымкі экрана"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Фота з рухам"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> зроблена <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Відэа знята ў <xliff:g id="TIME">%1$s</xliff:g> і мае працягласць <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Фота"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"Файл GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Фота з рухам"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Адключыць гук відэа"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Уключыць гук відэа"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Прайграць відэа"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Прыпыніць прайграванне відэа"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"З\'явіўся доступ да воблачных мультымедыя з праграмы \"<xliff:g id="PKG_NAME">%1$s</xliff:g>\""</string>
+ <string name="not_selected" msgid="2244008151669896758">"не выбраны"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Дазволіць праграме \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" змяніць гэты аўдыяфайл?}one{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайл?}few{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайлы?}many{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайлаў?}other{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайла?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Змяняецца аўдыяфайл…}one{Змяняецца <xliff:g id="COUNT">^1</xliff:g> аўдыяфайл…}few{Змяняюцца <xliff:g id="COUNT">^1</xliff:g> аўдыяфайлы…}many{Змяняюцца <xliff:g id="COUNT">^1</xliff:g> аўдыяфайлаў…}other{Змяняюцца <xliff:g id="COUNT">^1</xliff:g> аўдыяфайла…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Дазволіць праграме \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" змяніць гэта відэа?}one{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> відэа?}few{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> відэа?}many{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> відэа?}other{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> відэа?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Змяняецца відэа…}one{Змяняецца <xliff:g id="COUNT">^1</xliff:g> відэа…}few{Змяняюцца <xliff:g id="COUNT">^1</xliff:g> відэа…}many{Змяняюцца <xliff:g id="COUNT">^1</xliff:g> відэа…}other{Змяняюцца <xliff:g id="COUNT">^1</xliff:g> відэа…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Дазволіць праграме \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" змяніць гэта фота?}one{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> фота?}few{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> фота?}many{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> фота?}other{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> фота?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Змяняецца фота…}one{Змяняецца <xliff:g id="COUNT">^1</xliff:g> фота…}few{Змяняюцца <xliff:g id="COUNT">^1</xliff:g> фота…}many{Змяняюцца <xliff:g id="COUNT">^1</xliff:g> фота…}other{Змяняюцца <xliff:g id="COUNT">^1</xliff:g> фота…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Дазволіць праграме \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" змяніць гэты элемент?}one{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> элемент?}few{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> элементы?}many{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> элементаў?}other{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" змяніць <xliff:g id="COUNT">^2</xliff:g> элемента?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Змяняецца элемент…}one{Змяняецца <xliff:g id="COUNT">^1</xliff:g> элемент…}few{Змяняюцца <xliff:g id="COUNT">^1</xliff:g> элементы…}many{Змяняюцца <xliff:g id="COUNT">^1</xliff:g> элементаў…}other{Змяняюцца <xliff:g id="COUNT">^1</xliff:g> элемента…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Дазволіць праграме \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" перамясціць гэты аўдыяфайл у сметніцу?}one{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайл у сметніцу?}few{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайлы ў сметніцу?}many{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайлаў у сметніцу?}other{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайла ў сметніцу?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Аўдыяфайл перамяшчаецца ў сметніцу…}one{<xliff:g id="COUNT">^1</xliff:g> аўдыяфайл перамяшчаецца ў сметніцу…}few{<xliff:g id="COUNT">^1</xliff:g> аўдыяфайлы перамяшчаюцца ў сметніцу…}many{<xliff:g id="COUNT">^1</xliff:g> аўдыяфайлаў перамяшчаюцца ў сметніцу…}other{<xliff:g id="COUNT">^1</xliff:g> аўдыяфайла перамяшчаюцца ў сметніцу…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Дазволіць праграме \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" перамясціць гэта відэа ў сметніцу?}one{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> відэа ў сметніцу?}few{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> відэа ў сметніцу?}many{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> відэа ў сметніцу?}other{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> відэа ў сметніцу?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Відэа перамяшчаецца ў сметніцу…}one{<xliff:g id="COUNT">^1</xliff:g> відэа перамяшчаецца ў сметніцу…}few{<xliff:g id="COUNT">^1</xliff:g> відэа перамяшчаюцца ў сметніцу…}many{<xliff:g id="COUNT">^1</xliff:g> відэа перамяшчаюцца ў сметніцу…}other{<xliff:g id="COUNT">^1</xliff:g> відэа перамяшчаюцца ў сметніцу…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Дазволіць праграме \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" перамясціць гэта фота ў сметніцу?}one{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> фота ў сметніцу?}few{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> фота ў сметніцу?}many{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> фота ў сметніцу?}other{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> фота ў сметніцу?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Фота перамяшчаецца ў сметніцу…}one{<xliff:g id="COUNT">^1</xliff:g> фота перамяшчаецца ў сметніцу…}few{<xliff:g id="COUNT">^1</xliff:g> фота перамяшчаюцца ў сметніцу…}many{<xliff:g id="COUNT">^1</xliff:g> фота перамяшчаюцца ў сметніцу…}other{<xliff:g id="COUNT">^1</xliff:g> фота перамяшчаюцца ў сметніцу…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Дазволіць праграме \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" перамясціць гэты элемент у сметніцу?}one{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> элемент у сметніцу?}few{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> элементы ў сметніцу?}many{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> элементаў у сметніцу?}other{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> элемента ў сметніцу?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Элемент перамяшчаецца ў сметніцу…}one{<xliff:g id="COUNT">^1</xliff:g> элемент перамяшчаецца ў сметніцу…}few{<xliff:g id="COUNT">^1</xliff:g> элементы перамяшчаюцца ў сметніцу…}many{<xliff:g id="COUNT">^1</xliff:g> элементаў перамяшчаюцца ў сметніцу…}other{<xliff:g id="COUNT">^1</xliff:g> элемента перамяшчаюцца ў сметніцу…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Дазволіць праграме \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" перамясціць гэты аўдыяфайл са сметніцы?}one{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайл са сметніцы?}few{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайлы са сметніцы?}many{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайлаў са сметніцы?}other{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайла са сметніцы?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Аўдыяфайл перамяшчаецца са сметніцы…}one{<xliff:g id="COUNT">^1</xliff:g> аўдыяфайл перамяшчаецца са сметніцы…}few{<xliff:g id="COUNT">^1</xliff:g> аўдыяфайлы перамяшчаюцца са сметніцы…}many{<xliff:g id="COUNT">^1</xliff:g> аўдыяфайлаў перамяшчаюцца са сметніцы…}other{<xliff:g id="COUNT">^1</xliff:g> аўдыяфайла перамяшчаюцца са сметніцы…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Дазволіць праграме \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" перамясціць гэта відэа са сметніцы?}one{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> відэа са сметніцы?}few{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> відэа са сметніцы?}many{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> відэа са сметніцы?}other{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> відэа са сметніцы?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Відэа перамяшчаецца са сметніцы…}one{<xliff:g id="COUNT">^1</xliff:g> відэа перамяшчаецца са сметніцы…}few{<xliff:g id="COUNT">^1</xliff:g> відэа перамяшчаюцца са сметніцы…}many{<xliff:g id="COUNT">^1</xliff:g> відэа перамяшчаюцца са сметніцы…}other{<xliff:g id="COUNT">^1</xliff:g> відэа перамяшчаюцца са сметніцы…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Дазволіць праграме \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" перамясціць гэта фота са сметніцы?}one{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> фота са сметніцы?}few{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> фота са сметніцы?}many{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> фота са сметніцы?}other{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> фота са сметніцы?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Фота перамяшчаецца са сметніцы…}one{<xliff:g id="COUNT">^1</xliff:g> фота перамяшчаецца са сметніцы…}few{<xliff:g id="COUNT">^1</xliff:g> фота перамяшчаюцца са сметніцы…}many{<xliff:g id="COUNT">^1</xliff:g> фота перамяшчаюцца са сметніцы…}other{<xliff:g id="COUNT">^1</xliff:g> фота перамяшчаюцца са сметніцы…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Дазволіць праграме \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" перамясціць гэты элемент са сметніцы?}one{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> элемент са сметніцы?}few{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> элементы са сметніцы?}many{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> элементаў са сметніцы?}other{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" перамясціць <xliff:g id="COUNT">^2</xliff:g> элемента са сметніцы?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Элемент перамяшчаецца са сметніцы…}one{<xliff:g id="COUNT">^1</xliff:g> элемент перамяшчаецца са сметніцы…}few{<xliff:g id="COUNT">^1</xliff:g> элементы перамяшчаюцца са сметніцы…}many{<xliff:g id="COUNT">^1</xliff:g> элементаў перамяшчаюцца са сметніцы…}other{<xliff:g id="COUNT">^1</xliff:g> элемента перамяшчаюцца са сметніцы…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Дазволіць праграме \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" выдаліць гэты аўдыяфайл?}one{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайл?}few{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайлы?}many{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайлаў?}other{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> аўдыяфайла?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Выдаляецца аўдыяфайл…}one{Выдаляецца <xliff:g id="COUNT">^1</xliff:g> аўдыяфайл…}few{Выдаляюцца <xliff:g id="COUNT">^1</xliff:g> аўдыяфайлы…}many{Выдаляюцца <xliff:g id="COUNT">^1</xliff:g> аўдыяфайлаў…}other{Выдаляюцца <xliff:g id="COUNT">^1</xliff:g> аўдыяфайла…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Дазволіць праграме \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" выдаліць гэта відэа?}one{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> відэа?}few{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> відэа?}many{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> відэа?}other{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> відэа?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Выдаляецца відэа…}one{Выдаляецца <xliff:g id="COUNT">^1</xliff:g> відэа…}few{Выдаляюцца <xliff:g id="COUNT">^1</xliff:g> відэа…}many{Выдаляюцца <xliff:g id="COUNT">^1</xliff:g> відэа…}other{Выдаляюцца <xliff:g id="COUNT">^1</xliff:g> відэа…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Дазволіць праграме \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" выдаліць гэта фота?}one{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> фота?}few{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> фота?}many{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> фота?}other{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> фота?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Выдаляецца фота…}one{Выдаляецца <xliff:g id="COUNT">^1</xliff:g> фота…}few{Выдаляюцца <xliff:g id="COUNT">^1</xliff:g> фота…}many{Выдаляюцца <xliff:g id="COUNT">^1</xliff:g> фота…}other{Выдаляюцца <xliff:g id="COUNT">^1</xliff:g> фота…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Дазволіць праграме \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" выдаліць гэты элемент?}one{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> элемент?}few{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> элементы?}many{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> элементаў?}other{Дазволіць праграме \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" выдаліць <xliff:g id="COUNT">^2</xliff:g> элемента?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Выдаляецца элемент…}one{Выдаляецца <xliff:g id="COUNT">^1</xliff:g> элемент…}few{Выдаляюцца <xliff:g id="COUNT">^1</xliff:g> элементы…}many{Выдаляюцца <xliff:g id="COUNT">^1</xliff:g> элементаў…}other{Выдаляюцца <xliff:g id="COUNT">^1</xliff:g> элемента…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"Праграме \"<xliff:g id="APP_NAME">%s</xliff:g>\" не ўдалося апрацаваць файлы мультымедыя"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Апрацоўка мультымедыя скасавана"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Памылка апрацоўкі мультымедыя"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 878ad13..93dcfa1 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Мултимедия"</string>
<string name="storage_description" msgid="4081716890357580107">"Локално хранилище"</string>
<string name="app_label" msgid="9035307001052716210">"Мултимедийно хранилище"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"Инструмент за избор на снимки"</string>
<string name="artist_label" msgid="8105600993099120273">"Изпълнител"</string>
<string name="unknown" msgid="2059049215682829375">"Неизвестно"</string>
<string name="root_images" msgid="5861633549189045666">"Изображения"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Напред"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Разрешаване"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Отказ"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+ <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+ <xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">И още <xliff:g id="COUNT_1">^1</xliff:g> допълнителни елемента</item>
- <item quantity="one">И още <xliff:g id="COUNT_0">^1</xliff:g> допълнителен елемент</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+ <xliff:g id="COUNT_0">^1</xliff:g>}other{+ <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{И още <xliff:g id="COUNT_0">^1</xliff:g> допълнителен елемент}other{И още <xliff:g id="COUNT_1">^1</xliff:g> допълнителни елемента}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Изчистване на временните файлове на приложенията"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> иска да изчисти някои временни файлове. Това може да доведе до увеличено използване на батерията или мобилните данни."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Временните файлове на приложенията се изчистват…"</string>
<string name="clear" msgid="5524638938415865915">"Изчистване"</string>
<string name="allow" msgid="8885707816848569619">"Разрешаване"</string>
<string name="deny" msgid="6040983710442068936">"Отказ"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да промени <xliff:g id="COUNT">^2</xliff:g> аудиофайла?</item>
- <item quantity="one">Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да промени този аудиофайл?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> аудиофайла се променят…</item>
- <item quantity="one">Аудиофайлът се променя…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да промени <xliff:g id="COUNT">^2</xliff:g> видеоклипа?</item>
- <item quantity="one">Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да промени този видеоклип?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> видеоклипа се променят…</item>
- <item quantity="one">Видеоклипът се променя…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да промени <xliff:g id="COUNT">^2</xliff:g> снимки?</item>
- <item quantity="one">Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да промени тази снимка?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> снимки се променят…</item>
- <item quantity="one">Снимката се променя…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да промени <xliff:g id="COUNT">^2</xliff:g> елемента?</item>
- <item quantity="one">Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да промени този елемент?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> елемента се променят…</item>
- <item quantity="one">Елементът се променя…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> аудиофайла в кошчето?</item>
- <item quantity="one">Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да премести този аудиофайл в кошчето?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> аудиофайла се преместват в кошчето…</item>
- <item quantity="one">Аудиофайлът се премества в кошчето…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> видеоклипа в кошчето?</item>
- <item quantity="one">Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да премести този видеоклип в кошчето?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> видеоклипа се преместват в кошчето…</item>
- <item quantity="one">Видеоклипът се премества в кошчето…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> снимки в кошчето?</item>
- <item quantity="one">Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да премести тази снимка в кошчето?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> снимки се преместват в кошчето…</item>
- <item quantity="one">Снимката се премества в кошчето…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> елемента в кошчето?</item>
- <item quantity="one">Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да премести този елемент в кошчето?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> елемента се преместват в кошчето…</item>
- <item quantity="one">Елементът се премества в кошчето…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> аудиофайла извън кошчето?</item>
- <item quantity="one">Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да премести този аудиофайл извън кошчето?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> аудиофайла се преместват извън кошчето…</item>
- <item quantity="one">Аудиофайлът се премества извън кошчето…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> видеоклипа извън кошчето?</item>
- <item quantity="one">Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да премести този видеоклип извън кошчето?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> видеоклипа се преместват извън кошчето…</item>
- <item quantity="one">Видеоклипът се премества извън кошчето…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> снимки извън кошчето?</item>
- <item quantity="one">Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да премести тази снимка извън кошчето?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> снимки се преместват извън кошчето…</item>
- <item quantity="one">Снимката се премества извън кошчето…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> елемента извън кошчето?</item>
- <item quantity="one">Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да премести този елемент извън кошчето?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> елемента се преместват извън кошчето…</item>
- <item quantity="one">Елементът се премества извън кошчето…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да изтрие <xliff:g id="COUNT">^2</xliff:g> аудиофайла?</item>
- <item quantity="one">Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да изтрие този аудиофайл?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> аудиофайла се изтриват…</item>
- <item quantity="one">Аудиофайлът се изтрива…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да изтрие <xliff:g id="COUNT">^2</xliff:g> видеоклипа?</item>
- <item quantity="one">Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да изтрие този видеоклип?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> видеоклипа се изтриват…</item>
- <item quantity="one">Видеоклипът се изтрива…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да изтрие <xliff:g id="COUNT">^2</xliff:g> снимки?</item>
- <item quantity="one">Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да изтрие тази снимка?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> снимки се изтриват…</item>
- <item quantity="one">Снимката се изтрива…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да изтрие <xliff:g id="COUNT">^2</xliff:g> елемента?</item>
- <item quantity="one">Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да изтрие този елемент?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> елемента се изтриват…</item>
- <item quantity="one">Елементът се изтрива…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Добавяне"</string>
+ <string name="deselect" msgid="4297825044827769490">"Премахване на избора"</string>
+ <string name="deselected" msgid="8488133193326208475">"Неизбрано"</string>
+ <string name="select" msgid="2704765470563027689">"Избиране"</string>
+ <string name="selected" msgid="9151797369975828124">"Избрано"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Изберете най-много <xliff:g id="COUNT_0">^1</xliff:g> елемент}other{Изберете най-много <xliff:g id="COUNT_1">^1</xliff:g> елемента}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Скорошни"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Няма снимки или видеоклипове"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Няма албуми"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Преглед на избраното"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Снимки"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Албуми"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Визуализация"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Превкл. към служ. пoтр. профил"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Превключване към личния потребителски профил"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Блокирано от администратора ви"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Достъпът до служебни данни от лично приложение не е разрешен"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Достъпът до лични данни от служебно приложение не е разрешен"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Служебните приложения са поставени на пауза"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"За да отворите служебни снимки, включете служебните си приложения и опитайте отново"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Това приложение има достъп само до избраните от вас снимки"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> елемент}other{<xliff:g id="COUNT_1">^1</xliff:g> елемента}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Добавяне (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Камера"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Изтегляния"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Любими"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Екранни снимки"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Снимка с движение"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> от <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Видеоклип, заснет на <xliff:g id="TIME">%1$s</xliff:g>, с продължителност от <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Снимка"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF файл"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Снимка с движение"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Спиране на звука на видеоклипа"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Включване на звука на видеоклипа отново"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Пускане на видеоклипа"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Поставяне на видеоклипа на пауза"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Вече е налице мултимедия в облака от <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"не е избрано"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да промени този аудиофайл?}other{Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да промени <xliff:g id="COUNT">^2</xliff:g> аудиофайла?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Аудиофайлът се променя…}other{<xliff:g id="COUNT">^1</xliff:g> аудиофайла се променят…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да промени този видеоклип?}other{Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да промени <xliff:g id="COUNT">^2</xliff:g> видеоклипа?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Видеоклипът се променя…}other{<xliff:g id="COUNT">^1</xliff:g> видеоклипа се променят…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да промени тази снимка?}other{Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да промени <xliff:g id="COUNT">^2</xliff:g> снимки?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Снимката се променя…}other{<xliff:g id="COUNT">^1</xliff:g> снимки се променят…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да промени този елемент?}other{Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да промени <xliff:g id="COUNT">^2</xliff:g> елемента?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Елементът се променя…}other{<xliff:g id="COUNT">^1</xliff:g> елемента се променят…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да премести този аудиофайл в кошчето?}other{Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> аудиофайла в кошчето?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Аудиофайлът се премества в кошчето…}other{<xliff:g id="COUNT">^1</xliff:g> аудиофайла се преместват в кошчето…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да премести този видеоклип в кошчето?}other{Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> видеоклипа в кошчето?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Видеоклипът се премества в кошчето…}other{<xliff:g id="COUNT">^1</xliff:g> видеоклипа се преместват в кошчето…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да премести тази снимка в кошчето?}other{Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> снимки в кошчето?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Снимката се премества в кошчето…}other{<xliff:g id="COUNT">^1</xliff:g> снимки се преместват в кошчето…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да премести този елемент в кошчето?}other{Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> елемента в кошчето?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Елементът се премества в кошчето…}other{<xliff:g id="COUNT">^1</xliff:g> елемента се преместват в кошчето…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да премести този аудиофайл извън кошчето?}other{Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> аудиофайла извън кошчето?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Аудиофайлът се премества извън кошчето…}other{<xliff:g id="COUNT">^1</xliff:g> аудиофайла се преместват извън кошчето…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да премести този видеоклип извън кошчето?}other{Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> видеоклипа извън кошчето?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Видеоклипът се премества извън кошчето…}other{<xliff:g id="COUNT">^1</xliff:g> видеоклипа се преместват извън кошчето…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да премести тази снимка извън кошчето?}other{Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> снимки извън кошчето?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Снимката се премества извън кошчето…}other{<xliff:g id="COUNT">^1</xliff:g> снимки се преместват извън кошчето…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да премести този елемент извън кошчето?}other{Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> елемента извън кошчето?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Елементът се премества извън кошчето…}other{<xliff:g id="COUNT">^1</xliff:g> елемента се преместват извън кошчето…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да изтрие този аудиофайл?}other{Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да изтрие <xliff:g id="COUNT">^2</xliff:g> аудиофайла?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Аудиофайлът се изтрива…}other{<xliff:g id="COUNT">^1</xliff:g> аудиофайла се изтриват…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да изтрие този видеоклип?}other{Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да изтрие <xliff:g id="COUNT">^2</xliff:g> видеоклипа?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Видеоклипът се изтрива…}other{<xliff:g id="COUNT">^1</xliff:g> видеоклипа се изтриват…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да изтрие тази снимка?}other{Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да изтрие <xliff:g id="COUNT">^2</xliff:g> снимки?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Снимката се изтрива…}other{<xliff:g id="COUNT">^1</xliff:g> снимки се изтриват…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Да се разреши ли на <xliff:g id="APP_NAME_0">^1</xliff:g> да изтрие този елемент?}other{Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да изтрие <xliff:g id="COUNT">^2</xliff:g> елемента?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Елементът се изтрива…}other{<xliff:g id="COUNT">^1</xliff:g> елемента се изтриват…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> не може да обработва мултимедийни файлове"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Обработването на мултимедията е анулирано"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Грешка при обработването на мултимедията"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index e047ae1..a13b8eb 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"মিডিয়া"</string>
<string name="storage_description" msgid="4081716890357580107">"স্থানীয় স্টোরেজ"</string>
<string name="app_label" msgid="9035307001052716210">"মিডিয়া স্টোরেজ"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"ফটো চয়নকারি"</string>
<string name="artist_label" msgid="8105600993099120273">"শিল্পী"</string>
<string name="unknown" msgid="2059049215682829375">"অজানা"</string>
<string name="root_images" msgid="5861633549189045666">"ছবি"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"চালিয়ে যান"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"অনুমতি দিন"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"খারিজ করুন"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">+<xliff:g id="COUNT_1">^1</xliff:g>টি</item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g>টি</item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">এছাড়াও <xliff:g id="COUNT_1">^1</xliff:g>টি অতিরিক্ত আইটেম</item>
- <item quantity="other">এছাড়াও <xliff:g id="COUNT_1">^1</xliff:g>টি অতিরিক্ত আইটেম</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>টি}one{+<xliff:g id="COUNT_1">^1</xliff:g>টি}other{+<xliff:g id="COUNT_1">^1</xliff:g>টি}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{এছাড়াও <xliff:g id="COUNT_0">^1</xliff:g>টি অতিরিক্ত আইটেম}one{এছাড়াও <xliff:g id="COUNT_1">^1</xliff:g>টি অতিরিক্ত আইটেম}other{এছাড়াও <xliff:g id="COUNT_1">^1</xliff:g>টি অতিরিক্ত আইটেম}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"অস্থায়ী অ্যাপ ফাইলগুলি খালি করুন"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> কিছু অস্থায়ী ফাইলকে মুছে দিতে চায়। এর ফলে, ব্যাটারি বা মোবাইল ডেটা বেশি খরচ হতে পারে।"</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"অস্থায়ী অ্যাপ ফাইল মুছে ফেলা হচ্ছে…"</string>
<string name="clear" msgid="5524638938415865915">"সরান"</string>
<string name="allow" msgid="8885707816848569619">"অনুমতি দিন"</string>
<string name="deny" msgid="6040983710442068936">"বাতিল করুন"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি অডিও ফাইল পরিবর্তন করার অনুমতি দিতে চান?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি অডিও ফাইল পরিবর্তন করার অনুমতি দিতে চান?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g>টি অডিও ফাইল পরিবর্তন করা হচ্ছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>টি অডিও ফাইল পরিবর্তন করা হচ্ছে…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ভিডিও পরিবর্তন করার অনুমতি দিতে চান?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ভিডিও পরিবর্তন করার অনুমতি দিতে চান?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g>টি ভিডিও পরিবর্তন করা হচ্ছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>টি ভিডিও পরিবর্তন করা হচ্ছে…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ফটো পরিবর্তন করার অনুমতি দিতে চান?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ফটো পরিবর্তন করার অনুমতি দিতে চান?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g>টি ফটোতে পরিবর্তন করা হচ্ছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>টি ফটোতে পরিবর্তন করা হচ্ছে…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি আইটেম পরিবর্তন করার অনুমতি দিতে চান?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি আইটেম পরিবর্তন করার অনুমতি দিতে চান?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g>টি আইটেম পরিবর্তন করা হচ্ছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>টি আইটেম পরিবর্তন করা হচ্ছে…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি অডিও ফাইল ট্র্যাশ ফোল্ডারে সরিয়ে দেওয়ার অনুমতি দিতে চান?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি অডিও ফাইল ট্র্যাশ ফোল্ডারে সরিয়ে দেওয়ার অনুমতি দিতে চান?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g>টি অডিও ফাইল ট্র্যাশে সরানো হচ্ছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>টি অডিও ফাইল ট্র্যাশে সরানো হচ্ছে…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ভিডিও ট্র্যাশ ফোল্ডারে সরিয়ে দেওয়ার অনুমতি দিতে চান?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ভিডিও ট্র্যাশ ফোল্ডারে সরিয়ে দেওয়ার অনুমতি দিতে চান?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g>টি ভিডিও ট্র্যাশে সরানো হচ্ছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>টি ভিডিও ট্র্যাশে সরানো হচ্ছে…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ফটো ট্র্যাশ ফোল্ডারে সরিয়ে দেওয়ার অনুমতি দিতে চান?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ফটো ট্র্যাশ ফোল্ডারে সরিয়ে দেওয়ার অনুমতি দিতে চান?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g>টি ফটো ট্র্যাশে সরানো হচ্ছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>টি ফটো ট্র্যাশে সরানো হচ্ছে…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি আইটেম ট্র্যাশ ফোল্ডারে সরিয়ে দেওয়ার অনুমতি দিতে চান?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি আইটেম ট্র্যাশ ফোল্ডারে সরিয়ে দেওয়ার অনুমতি দিতে চান?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g>টি আইটেম ট্র্যাশে সরানো হচ্ছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>টি আইটেম ট্র্যাশে সরানো হচ্ছে…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি অডিও ফাইল ট্র্যাশ ফোল্ডার থেকে সরানোর অনুমতি দিতে চান?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি অডিও ফাইল ট্র্যাশ ফোল্ডার থেকে সরানোর অনুমতি দিতে চান?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g>টি অডিও ফাইলকে ট্র্যাশ থেকে বের করে আনা হচ্ছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>টি অডিও ফাইলকে ট্র্যাশ থেকে বের করে আনা হচ্ছে…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ভিডিও ট্র্যাশ ফোল্ডার থেকে সরানোর অনুমতি দিতে চান?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ভিডিও ট্র্যাশ ফোল্ডার থেকে সরানোর অনুমতি দিতে চান?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g>টি ভিডিওকে ট্র্যাশ থেকে বের করে আনা হচ্ছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>টি ভিডিওকে ট্র্যাশ থেকে বের করে আনা হচ্ছে…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ফটো ট্র্যাশ ফোল্ডার থেকে সরানোর অনুমতি দিতে চান?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ফটো ট্র্যাশ ফোল্ডার থেকে সরানোর অনুমতি দিতে চান?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g>টি ফটোকে ট্র্যাশ থেকে বের করে আনা হচ্ছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>টি ফটোকে ট্র্যাশ থেকে বের করে আনা হচ্ছে…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি আইটেম ট্র্যাশ ফোল্ডার থেকে সরানোর অনুমতি দিতে চান?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি আইটেম ট্র্যাশ ফোল্ডার থেকে সরানোর অনুমতি দিতে চান?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g>টি আইটেমকে ট্র্যাশ থেকে বের করে আনা হচ্ছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>টি আইটেমকে ট্র্যাশ থেকে বের করে আনা হচ্ছে…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি অডিও ফাইল মুছে দেওয়ার অনুমতি দিতে চান?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি অডিও ফাইল মুছে দেওয়ার অনুমতি দিতে চান?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g>টি অডিও ফাইল মুছে ফেলা হচ্ছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>টি অডিও ফাইল মুছে ফেলা হচ্ছে…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ভিডিও মুছে দেওয়ার অনুমতি দিতে চান?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ভিডিও মুছে দেওয়ার অনুমতি দিতে চান?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g>টি ভিডিও মুছে ফেলা হচ্ছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>টি ভিডিও মুছে ফেলা হচ্ছে…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ফটো মুছে দেওয়ার অনুমতি দিতে চান?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ফটো মুছে দেওয়ার অনুমতি দিতে চান?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g>টি ফটো মুছে ফেলা হচ্ছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>টি ফটো মুছে ফেলা হচ্ছে…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি আইটেম মুছে দেওয়ার অনুমতি দিতে চান?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি আইটেম মুছে দেওয়ার অনুমতি দিতে চান?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g>টি আইটেম মুছে ফেলা হচ্ছে…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>টি আইটেম মুছে ফেলা হচ্ছে…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"যোগ করুন"</string>
+ <string name="deselect" msgid="4297825044827769490">"টিক চিহ্নটি সরিয়ে দিন"</string>
+ <string name="deselected" msgid="8488133193326208475">"টিকচিহ্ন সরিয়ে দিন"</string>
+ <string name="select" msgid="2704765470563027689">"বেছে নিন"</string>
+ <string name="selected" msgid="9151797369975828124">"টিকচিহ্ন দিন"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{সর্বাধিক <xliff:g id="COUNT_0">^1</xliff:g>টি আইটেম বেছে নিন}one{সর্বাধিক <xliff:g id="COUNT_1">^1</xliff:g>টি আইটেম বেছে নিন}other{সর্বাধিক <xliff:g id="COUNT_1">^1</xliff:g>টি আইটেম বেছে নিন}}"</string>
+ <string name="recent" msgid="6694613584743207874">"সাম্প্রতিক"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"কোনও ফটো বা ভিডিও নেই"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"কোনও অ্যালবাম নেই"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"ভিউ বেছে নেওয়া হয়েছে"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"ফটো"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"অ্যালবাম"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"প্রিভিউ"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"অফিস প্রোফাইলে সুইচ করুন"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"ব্যক্তিগত প্রোফাইলে সুইচ করুন"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"আপনার অ্যাডমিন ব্লক করে দিয়েছে"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"ব্যক্তিগত অ্যাপ থেকে অফিসের ডেটা অ্যাক্সেস করার অনুমতি নেই"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"অফিস অ্যাপ থেকে ব্যক্তিগত ডেটা অ্যাক্সেস করার অনুমতি নেই"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"অফিসের অ্যাপ পজ করা আছে"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"অফিস প্রোফাইলের ফটো দেখতে, অফিসের অ্যাপ চালু করে আবার চেষ্টা করুন"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"এই অ্যাপ শুধুমাত্র আপনার বেছে নেওয়া ছবি অ্যাক্সেস করতে পারবে"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g>টি আইটেম}one{<xliff:g id="COUNT_1">^1</xliff:g>টি আইটেম}other{<xliff:g id="COUNT_1">^1</xliff:g>টি আইটেম}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"(<xliff:g id="COUNT">^1</xliff:g>)টি যোগ করুন"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"ক্যামেরা"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"ডাউনলোড করা আইটেম"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"পছন্দসই আইটেম"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"স্ক্রিনশট"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"মোশন ফটো"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> <xliff:g id="TIME">%2$s</xliff:g>-এ তোলা হয়েছে"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"<xliff:g id="TIME">%1$s</xliff:g>-এ যে ভিডিওটি নেওয়া হয়েছে তার সময়সীমা <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"ফটো"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"মোশন ফটো"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"ভিডিও মিউট করুন"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"ভিডিও আনমিউট করুন"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"ভিডিও চালান"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"ভিডিও পজ করুন"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"ক্লাউড মিডিয়া এখন <xliff:g id="PKG_NAME">%1$s</xliff:g> থেকে উপলভ্য"</string>
+ <string name="not_selected" msgid="2244008151669896758">"বেছে নেওয়া হয়নি"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-কে এই অডিও ফাইল পরিবর্তন করার অনুমতি দিতে চান?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি অডিও ফাইল পরিবর্তন করার অনুমতি দিতে চান?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি অডিও ফাইল পরিবর্তন করার অনুমতি দিতে চান?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{অডিও ফাইলে পরিবর্তন করা হচ্ছে…}one{<xliff:g id="COUNT">^1</xliff:g>টি অডিও ফাইলে পরিবর্তন করা হচ্ছে…}other{<xliff:g id="COUNT">^1</xliff:g>টি অডিও ফাইলে পরিবর্তন করা হচ্ছে…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-কে এই ভিডিও পরিবর্তন করার অনুমতি দিতে চান?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ভিডিও পরিবর্তন করার অনুমতি দিতে চান?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ভিডিও পরিবর্তন করার অনুমতি দিতে চান?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{ভিডিওতে পরিবর্তন করা হচ্ছে…}one{<xliff:g id="COUNT">^1</xliff:g>টি ভিডিওতে পরিবর্তন করা হচ্ছে…}other{<xliff:g id="COUNT">^1</xliff:g>টি ভিডিওতে পরিবর্তন করা হচ্ছে…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-কে এই ফটো পরিবর্তন করার অনুমতি দিতে চান?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ফটো পরিবর্তন করার অনুমতি দিতে চান?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ফটো পরিবর্তন করার অনুমতি দিতে চান?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{ফটোতে পরিবর্তন করা হচ্ছে…}one{<xliff:g id="COUNT">^1</xliff:g>টি ফটোতে পরিবর্তন করা হচ্ছে…}other{<xliff:g id="COUNT">^1</xliff:g>টি ফটোতে পরিবর্তন করা হচ্ছে…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-কে এই আইটেম পরিবর্তন করার অনুমতি দিতে চান?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি আইটেম পরিবর্তন করার অনুমতি দিতে চান?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি আইটেম পরিবর্তন করার অনুমতি দিতে চান?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{আইটেমে পরিবর্তন করা হচ্ছে…}one{<xliff:g id="COUNT">^1</xliff:g>টি আইটেমে পরিবর্তন করা হচ্ছে…}other{<xliff:g id="COUNT">^1</xliff:g>টি আইটেমে পরিবর্তন করা হচ্ছে…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-কে এই অডিও ফাইল ট্র্যাশ ফোল্ডারে সরিয়ে দেওয়ার অনুমতি দিতে চান?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি অডিও ফাইল ট্র্যাশ ফোল্ডারে সরিয়ে দেওয়ার অনুমতি দিতে চান?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি অডিও ফাইল ট্র্যাশ ফোল্ডারে সরিয়ে দেওয়ার অনুমতি দিতে চান?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{অডিও ফাইল ট্র্যাশে সরানো হচ্ছে…}one{<xliff:g id="COUNT">^1</xliff:g>টি অডিও ফাইল ট্র্যাশে সরানো হচ্ছে…}other{<xliff:g id="COUNT">^1</xliff:g>টি অডিও ফাইল ট্র্যাশে সরানো হচ্ছে…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-কে এই ভিডিও ট্র্যাশ ফোল্ডারে সরিয়ে দেওয়ার অনুমতি দিতে চান?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ভিডিও ট্র্যাশ ফোল্ডারে সরিয়ে দেওয়ার অনুমতি দিতে চান?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ভিডিও ট্র্যাশ ফোল্ডারে সরিয়ে দেওয়ার অনুমতি দিতে চান?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{ভিডিও ট্র্যাশে সরানো হচ্ছে…}one{<xliff:g id="COUNT">^1</xliff:g>টি ভিডিও ট্র্যাশে সরানো হচ্ছে…}other{<xliff:g id="COUNT">^1</xliff:g>টি ভিডিও ট্র্যাশে সরানো হচ্ছে…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-কে এই ফটো ট্র্যাশ ফোল্ডারে সরিয়ে দেওয়ার অনুমতি দিতে চান?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ফটো ট্র্যাশ ফোল্ডারে সরিয়ে দেওয়ার অনুমতি দিতে চান?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ফটো ট্র্যাশ ফোল্ডারে সরিয়ে দেওয়ার অনুমতি দিতে চান?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{ফটো ট্র্যাশে সরানো হচ্ছে…}one{<xliff:g id="COUNT">^1</xliff:g>টি ফটো ট্র্যাশে সরানো হচ্ছে…}other{<xliff:g id="COUNT">^1</xliff:g>টি ফটো ট্র্যাশে সরানো হচ্ছে…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-কে এই আইটেম ট্র্যাশ ফোল্ডারে সরিয়ে দেওয়ার অনুমতি দিতে চান?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি আইটেম ট্র্যাশ ফোল্ডারে সরিয়ে দেওয়ার অনুমতি দিতে চান?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি আইটেম ট্র্যাশ ফোল্ডারে সরিয়ে দেওয়ার অনুমতি দিতে চান?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{আইটেম ট্র্যাশে সরানো হচ্ছে…}one{<xliff:g id="COUNT">^1</xliff:g>টি আইটেম ট্র্যাশে সরানো হচ্ছে…}other{<xliff:g id="COUNT">^1</xliff:g>টি আইটেম ট্র্যাশে সরানো হচ্ছে…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-কে এই অডিও ফাইল ট্র্যাশ ফোল্ডার থেকে সরানোর অনুমতি দিতে চান?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি অডিও ফাইল ট্র্যাশ ফোল্ডার থেকে সরানোর অনুমতি দিতে চান?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি অডিও ফাইল ট্র্যাশ ফোল্ডার থেকে সরানোর অনুমতি দিতে চান?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{অডিও ফাইলকে ট্র্যাশ থেকে বের করে আনা হচ্ছে…}one{<xliff:g id="COUNT">^1</xliff:g>টি অডিও ফাইলকে ট্র্যাশ থেকে বের করে আনা হচ্ছে…}other{<xliff:g id="COUNT">^1</xliff:g>টি অডিও ফাইলকে ট্র্যাশ থেকে বের করে আনা হচ্ছে…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-কে এই ভিডিও ট্র্যাশ ফোল্ডার থেকে সরানোর অনুমতি দিতে চান?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ভিডিও ট্র্যাশ ফোল্ডার থেকে সরানোর অনুমতি দিতে চান?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ভিডিও ট্র্যাশ ফোল্ডার থেকে সরানোর অনুমতি দিতে চান?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{ভিডিওকে ট্র্যাশ থেকে বের করে আনা হচ্ছে…}one{<xliff:g id="COUNT">^1</xliff:g>টি ভিডিওকে ট্র্যাশ থেকে বের করে আনা হচ্ছে…}other{<xliff:g id="COUNT">^1</xliff:g>টি ভিডিওকে ট্র্যাশ থেকে বের করে আনা হচ্ছে…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-কে এই ফটো ট্র্যাশ ফোল্ডার থেকে সরানোর অনুমতি দিতে চান?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ফটো ট্র্যাশ ফোল্ডার থেকে সরানোর অনুমতি দিতে চান?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ফটো ট্র্যাশ ফোল্ডার থেকে সরানোর অনুমতি দিতে চান?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{ফটোকে ট্র্যাশ থেকে বের করে আনা হচ্ছে…}one{<xliff:g id="COUNT">^1</xliff:g>টি ফটোকে ট্র্যাশ থেকে বের করে আনা হচ্ছে…}other{<xliff:g id="COUNT">^1</xliff:g>টি ফটোকে ট্র্যাশ থেকে বের করে আনা হচ্ছে…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-কে এই আইটেম ট্র্যাশ ফোল্ডার থেকে সরানোর অনুমতি দিতে চান?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি আইটেম ট্র্যাশ ফোল্ডার থেকে সরানোর অনুমতি দিতে চান?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি আইটেম ট্র্যাশ ফোল্ডার থেকে সরানোর অনুমতি দিতে চান?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{আইটেমকে ট্র্যাশ থেকে বের করে আনা হচ্ছে…}one{<xliff:g id="COUNT">^1</xliff:g>টি আইটেমকে ট্র্যাশ থেকে বের করে আনা হচ্ছে…}other{<xliff:g id="COUNT">^1</xliff:g>টি আইটেমকে ট্র্যাশ থেকে বের করে আনা হচ্ছে…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-কে এই অডিও ফাইল মুছে দেওয়ার অনুমতি দিতে চান?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি অডিও ফাইল মুছে দেওয়ার অনুমতি দিতে চান?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি অডিও ফাইল মুছে দেওয়ার অনুমতি দিতে চান?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{অডিও ফাইল মুছে ফেলা হচ্ছে…}one{<xliff:g id="COUNT">^1</xliff:g>টি অডিও ফাইল মুছে ফেলা হচ্ছে…}other{<xliff:g id="COUNT">^1</xliff:g>টি অডিও ফাইল মুছে ফেলা হচ্ছে…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-কে এই ভিডিও মুছে দেওয়ার অনুমতি দিতে চান?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ভিডিও মুছে দেওয়ার অনুমতি দিতে চান?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ভিডিও মুছে দেওয়ার অনুমতি দিতে চান?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{ভিডিও মুছে ফেলা হচ্ছে…}one{<xliff:g id="COUNT">^1</xliff:g>টি ভিডিও মুছে ফেলা হচ্ছে…}other{<xliff:g id="COUNT">^1</xliff:g>টি ভিডিও মুছে ফেলা হচ্ছে…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-কে এই ফটো মুছে দেওয়ার অনুমতি দিতে চান?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ফটো মুছে দেওয়ার অনুমতি দিতে চান?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি ফটো মুছে দেওয়ার অনুমতি দিতে চান?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{ফটো মুছে ফেলা হচ্ছে…}one{<xliff:g id="COUNT">^1</xliff:g>টি ফটো মুছে ফেলা হচ্ছে…}other{<xliff:g id="COUNT">^1</xliff:g>টি ফটো মুছে ফেলা হচ্ছে…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-কে এই আইটেম মুছে দেওয়ার অনুমতি দিতে চান?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি আইটেম মুছে দেওয়ার অনুমতি দিতে চান?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি আইটেম মুছে দেওয়ার অনুমতি দিতে চান?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{আইটেম মুছে ফেলা হচ্ছে…}one{<xliff:g id="COUNT">^1</xliff:g>টি আইটেম মুছে ফেলা হচ্ছে…}other{<xliff:g id="COUNT">^1</xliff:g>টি আইটেম মুছে ফেলা হচ্ছে…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> মিডিয়া ফাইল প্রসেস করতে পারবে না"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"মিডিয়া ফাইল প্রসেস করা বাতিল হয়ে গেছে"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"মিডিয়া ফাইল প্রসেস করার সময়ে সমস্যা হচ্ছে"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 791b3a4..d2e5f61 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Izbornik fotografija"</string>
<string name="artist_label" msgid="8105600993099120273">"Umjetnik"</string>
<string name="unknown" msgid="2059049215682829375">"Nepoznato"</string>
<string name="root_images" msgid="5861633549189045666">"Slike"</string>
@@ -29,182 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Nastavi"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Dozvoli"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Odbij"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="few">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">Još <xliff:g id="COUNT_1">^1</xliff:g> dodatna stavka</item>
- <item quantity="few">Još <xliff:g id="COUNT_1">^1</xliff:g> dodatne stavke</item>
- <item quantity="other">Još <xliff:g id="COUNT_1">^1</xliff:g> dodatnih stavki</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{i još <xliff:g id="COUNT_0">^1</xliff:g>}one{i još <xliff:g id="COUNT_1">^1</xliff:g>}few{i još <xliff:g id="COUNT_1">^1</xliff:g>}other{i još <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Još <xliff:g id="COUNT_0">^1</xliff:g> dodatna stavka}one{Još <xliff:g id="COUNT_1">^1</xliff:g> dodatna stavka}few{Još <xliff:g id="COUNT_1">^1</xliff:g> dodatne stavke}other{Još <xliff:g id="COUNT_1">^1</xliff:g> dodatnih stavki}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Brisanje privremenih fajlova aplikacija"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"Aplikacija <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> želi obrisati neke privremene fajlove. Ovo može dovesti do povećanog korištenja baterije ili prijenosa podataka na mobilnoj mreži."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Brisanje privremenih fajlova aplikacije…"</string>
<string name="clear" msgid="5524638938415865915">"Obriši"</string>
<string name="allow" msgid="8885707816848569619">"Dozvoli"</string>
<string name="deny" msgid="6040983710442068936">"Odbij"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izmijeni <xliff:g id="COUNT">^2</xliff:g> audio fajl?</item>
- <item quantity="few">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izmijeni <xliff:g id="COUNT">^2</xliff:g> audio fajla?</item>
- <item quantity="other">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izmijeni <xliff:g id="COUNT">^2</xliff:g> audio fajlova?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one">Mijenjanje <xliff:g id="COUNT">^1</xliff:g> audio fajla…</item>
- <item quantity="few">Mijenjanje <xliff:g id="COUNT">^1</xliff:g> audio fajla…</item>
- <item quantity="other">Mijenjanje <xliff:g id="COUNT">^1</xliff:g> audio fajlova…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izmijeni <xliff:g id="COUNT">^2</xliff:g> videozapis?</item>
- <item quantity="few">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izmijeni <xliff:g id="COUNT">^2</xliff:g> videozapisa?</item>
- <item quantity="other">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izmijeni <xliff:g id="COUNT">^2</xliff:g> videozapisa?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one">Mijenjanje <xliff:g id="COUNT">^1</xliff:g> videozapisa…</item>
- <item quantity="few">Mijenjanje <xliff:g id="COUNT">^1</xliff:g> videozapisa…</item>
- <item quantity="other">Mijenjanje <xliff:g id="COUNT">^1</xliff:g> videozapisa…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izmijeni <xliff:g id="COUNT">^2</xliff:g> fotografiju?</item>
- <item quantity="few">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izmijeni <xliff:g id="COUNT">^2</xliff:g> fotografije?</item>
- <item quantity="other">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izmijeni <xliff:g id="COUNT">^2</xliff:g> fotografija?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one">Mijenjanje <xliff:g id="COUNT">^1</xliff:g> fotografije…</item>
- <item quantity="few">Mijenjanje <xliff:g id="COUNT">^1</xliff:g> fotografije…</item>
- <item quantity="other">Mijenjanje <xliff:g id="COUNT">^1</xliff:g> fotografija…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izmijeni <xliff:g id="COUNT">^2</xliff:g> stavku?</item>
- <item quantity="few">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izmijeni <xliff:g id="COUNT">^2</xliff:g> stavke?</item>
- <item quantity="other">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izmijeni <xliff:g id="COUNT">^2</xliff:g> stavki?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one">Mijenjanje <xliff:g id="COUNT">^1</xliff:g> stavke…</item>
- <item quantity="few">Mijenjanje <xliff:g id="COUNT">^1</xliff:g> stavke…</item>
- <item quantity="other">Mijenjanje <xliff:g id="COUNT">^1</xliff:g> stavki…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> audio fajl u otpad?</item>
- <item quantity="few">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> audio fajla u otpad?</item>
- <item quantity="other">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> audio fajlova u otpad?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one">Premještanje <xliff:g id="COUNT">^1</xliff:g> audio fajla u otpad…</item>
- <item quantity="few">Premještanje <xliff:g id="COUNT">^1</xliff:g> audio fajla u otpad…</item>
- <item quantity="other">Premještanje <xliff:g id="COUNT">^1</xliff:g> audio fajlova u otpad…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> videozapis u otpad?</item>
- <item quantity="few">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> videozapisa u otpad?</item>
- <item quantity="other">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> videozapisa u otpad?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one">Premještanje <xliff:g id="COUNT">^1</xliff:g> videozapisa u otpad…</item>
- <item quantity="few">Premještanje <xliff:g id="COUNT">^1</xliff:g> videozapisa u otpad…</item>
- <item quantity="other">Premještanje <xliff:g id="COUNT">^1</xliff:g> videozapisa u otpad…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> fotografiju u otpad?</item>
- <item quantity="few">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> fotografije u otpad?</item>
- <item quantity="other">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> fotografija u otpad?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one">Premještanje <xliff:g id="COUNT">^1</xliff:g> fotografije u otpad…</item>
- <item quantity="few">Premještanje <xliff:g id="COUNT">^1</xliff:g> fotografije u otpad…</item>
- <item quantity="other">Premještanje <xliff:g id="COUNT">^1</xliff:g> fotografija u otpad…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> stavku u otpad?</item>
- <item quantity="few">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> stavke u otpad?</item>
- <item quantity="other">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> stavki u otpad?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one">Premještanje <xliff:g id="COUNT">^1</xliff:g> stavke u otpad…</item>
- <item quantity="few">Premještanje <xliff:g id="COUNT">^1</xliff:g> stavke u otpad…</item>
- <item quantity="other">Premještanje <xliff:g id="COUNT">^1</xliff:g> stavki u otpad…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> audio fajl iz otpada?</item>
- <item quantity="few">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> audio fajla iz otpada?</item>
- <item quantity="other">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> audio fajlova iz otpada?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one">Premještanje <xliff:g id="COUNT">^1</xliff:g> audio fajla iz otpada…</item>
- <item quantity="few">Premještanje <xliff:g id="COUNT">^1</xliff:g> audio fajla iz otpada…</item>
- <item quantity="other">Premještanje <xliff:g id="COUNT">^1</xliff:g> audio fajlova iz otpada…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> videozapis iz otpada?</item>
- <item quantity="few">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> videozapisa iz otpada?</item>
- <item quantity="other">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> videozapisa iz otpada?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one">Premještanje <xliff:g id="COUNT">^1</xliff:g> videozapisa iz otpada…</item>
- <item quantity="few">Premještanje <xliff:g id="COUNT">^1</xliff:g> videozapisa iz otpada…</item>
- <item quantity="other">Premještanje <xliff:g id="COUNT">^1</xliff:g> videozapisa iz otpada…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> fotografiju iz otpada?</item>
- <item quantity="few">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> fotografije iz otpada?</item>
- <item quantity="other">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> fotografija iz otpada?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one">Premještanje <xliff:g id="COUNT">^1</xliff:g> fotografije iz otpada…</item>
- <item quantity="few">Premještanje <xliff:g id="COUNT">^1</xliff:g> fotografije iz otpada…</item>
- <item quantity="other">Premještanje <xliff:g id="COUNT">^1</xliff:g> fotografija iz otpada…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> stavku iz otpada?</item>
- <item quantity="few">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> stavke iz otpada?</item>
- <item quantity="other">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> stavki iz otpada?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one">Premještanje <xliff:g id="COUNT">^1</xliff:g> stavke iz otpada…</item>
- <item quantity="few">Premještanje <xliff:g id="COUNT">^1</xliff:g> stavke iz otpada…</item>
- <item quantity="other">Premještanje <xliff:g id="COUNT">^1</xliff:g> stavki iz otpada…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> audio fajl?</item>
- <item quantity="few">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> audio fajla?</item>
- <item quantity="other">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> audio fajlova?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one">Brisanje <xliff:g id="COUNT">^1</xliff:g> audio fajla…</item>
- <item quantity="few">Brisanje <xliff:g id="COUNT">^1</xliff:g> audio fajla…</item>
- <item quantity="other">Brisanje <xliff:g id="COUNT">^1</xliff:g> audio fajlova…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> videozapis?</item>
- <item quantity="few">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> videozapisa?</item>
- <item quantity="other">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> videozapisa?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one">Brisanje <xliff:g id="COUNT">^1</xliff:g> videozapisa…</item>
- <item quantity="few">Brisanje <xliff:g id="COUNT">^1</xliff:g> videozapisa…</item>
- <item quantity="other">Brisanje <xliff:g id="COUNT">^1</xliff:g> videozapisa…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> fotografiju?</item>
- <item quantity="few">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> fotografije?</item>
- <item quantity="other">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> fotografija?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one">Brisanje <xliff:g id="COUNT">^1</xliff:g> fotografije…</item>
- <item quantity="few">Brisanje <xliff:g id="COUNT">^1</xliff:g> fotografije…</item>
- <item quantity="other">Brisanje <xliff:g id="COUNT">^1</xliff:g> fotografija…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> stavku?</item>
- <item quantity="few">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> stavke?</item>
- <item quantity="other">Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> stavki?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one">Brisanje <xliff:g id="COUNT">^1</xliff:g> stavke…</item>
- <item quantity="few">Brisanje <xliff:g id="COUNT">^1</xliff:g> stavke…</item>
- <item quantity="other">Brisanje <xliff:g id="COUNT">^1</xliff:g> stavki…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Dodaj"</string>
+ <string name="deselect" msgid="4297825044827769490">"Poništi odabir"</string>
+ <string name="deselected" msgid="8488133193326208475">"Odabir poništen"</string>
+ <string name="select" msgid="2704765470563027689">"Odaberi"</string>
+ <string name="selected" msgid="9151797369975828124">"Odabrano"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Odaberite najviše <xliff:g id="COUNT_0">^1</xliff:g> stavku}one{Odaberite najviše <xliff:g id="COUNT_1">^1</xliff:g> stavku}few{Odaberite najviše <xliff:g id="COUNT_1">^1</xliff:g> stavke}other{Odaberite najviše <xliff:g id="COUNT_1">^1</xliff:g> stavki}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Nedavno"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Nema fotografija ni videozapisa"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Nema albuma"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Prikaži odabrano"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Fotografije"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albumi"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Pregled"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Prebacite se na radni"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Prebacite se na lični"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Blokirao je administrator"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Pristupanje poslovnim podacima iz lične aplikacije nije dozvoljeno"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Pristupanje ličnim podacima iz poslovne aplikacije nije dozvoljeno"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Poslovne aplikacije su pauzirane"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Da otvorite poslovne fotografije, uključite poslovne aplikacije i pokušajte ponovo"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Ova aplikacija može pristupiti samo fotografijama koje odaberete"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> stavka}one{<xliff:g id="COUNT_1">^1</xliff:g> stavka}few{<xliff:g id="COUNT_1">^1</xliff:g> stavke}other{<xliff:g id="COUNT_1">^1</xliff:g> stavki}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Dodaj (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Preuzimanja"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Omiljeno"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Snimci ekrana"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Fotografija s videom"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"Sadržaj <xliff:g id="ITEM_NAME">%1$s</xliff:g> je snimljen u <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Videozapis je snimljen u <xliff:g id="TIME">%1$s</xliff:g> i traje <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Fotografija"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Fotografija s videom"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Isključivanje zvuka videozapisa"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Uključivanje zvuka videozapisa"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Reproduciranje videozapisa"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Pauziranje videozapisa"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Medijski sadržaj u oblaku je sada dostupan od usluge <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"nije odabrano"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Dozvoliti da <xliff:g id="APP_NAME_0">^1</xliff:g> izmijeni ovaj audio fajl?}one{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izmijeni <xliff:g id="COUNT">^2</xliff:g> audio fajl?}few{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izmijeni <xliff:g id="COUNT">^2</xliff:g> audio fajla?}other{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izmijeni <xliff:g id="COUNT">^2</xliff:g> audio fajlova?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Mijenjanje audio fajla…}one{Mijenjanje <xliff:g id="COUNT">^1</xliff:g> audio fajla…}few{Mijenjanje <xliff:g id="COUNT">^1</xliff:g> audio fajla…}other{Mijenjanje <xliff:g id="COUNT">^1</xliff:g> audio fajlova…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Dozvoliti da <xliff:g id="APP_NAME_0">^1</xliff:g> izmijeni ovaj videozapis?}one{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izmijeni <xliff:g id="COUNT">^2</xliff:g> videozapis?}few{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izmijeni <xliff:g id="COUNT">^2</xliff:g> videozapisa?}other{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izmijeni <xliff:g id="COUNT">^2</xliff:g> videozapisa?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Mijenjanje videozapisa…}one{Mijenjanje <xliff:g id="COUNT">^1</xliff:g> videozapisa…}few{Mijenjanje <xliff:g id="COUNT">^1</xliff:g> videozapisa…}other{Mijenjanje <xliff:g id="COUNT">^1</xliff:g> videozapisa…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Dozvoliti da <xliff:g id="APP_NAME_0">^1</xliff:g> izmijeni ovu fotografiju?}one{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izmijeni <xliff:g id="COUNT">^2</xliff:g> fotografiju?}few{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izmijeni <xliff:g id="COUNT">^2</xliff:g> fotografije?}other{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izmijeni <xliff:g id="COUNT">^2</xliff:g> fotografija?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Mijenjanje fotografije…}one{Mijenjanje <xliff:g id="COUNT">^1</xliff:g> fotografije…}few{Mijenjanje <xliff:g id="COUNT">^1</xliff:g> fotografije…}other{Mijenjanje <xliff:g id="COUNT">^1</xliff:g> fotografija…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Dozvoliti da <xliff:g id="APP_NAME_0">^1</xliff:g> izmijeni ovu stavku?}one{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izmijeni <xliff:g id="COUNT">^2</xliff:g> stavku?}few{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izmijeni <xliff:g id="COUNT">^2</xliff:g> stavke?}other{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izmijeni <xliff:g id="COUNT">^2</xliff:g> stavki?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Mijenjanje stavke…}one{Mijenjanje <xliff:g id="COUNT">^1</xliff:g> stavke…}few{Mijenjanje <xliff:g id="COUNT">^1</xliff:g> stavke…}other{Mijenjanje <xliff:g id="COUNT">^1</xliff:g> stavki…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Dozvoliti da <xliff:g id="APP_NAME_0">^1</xliff:g> premjesti ovaj audio fajl u otpad?}one{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> audio fajl u otpad?}few{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> audio fajla u otpad?}other{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> audio fajlova u otpad?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Premještanje audio fajla u otpad…}one{Premještanje <xliff:g id="COUNT">^1</xliff:g> audio fajla u otpad…}few{Premještanje <xliff:g id="COUNT">^1</xliff:g> audio fajla u otpad…}other{Premještanje <xliff:g id="COUNT">^1</xliff:g> audio fajlova u otpad…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Dozvoliti da <xliff:g id="APP_NAME_0">^1</xliff:g> premjesti ovaj videozapis u otpad?}one{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> videozapis u otpad?}few{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> videozapisa u otpad?}other{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> videozapisa u otpad?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Premještanje videozapisa u otpad…}one{Premještanje <xliff:g id="COUNT">^1</xliff:g> videozapisa u otpad…}few{Premještanje <xliff:g id="COUNT">^1</xliff:g> videozapisa u otpad…}other{Premještanje <xliff:g id="COUNT">^1</xliff:g> videozapisa u otpad…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Dozvoliti da <xliff:g id="APP_NAME_0">^1</xliff:g> premjesti ovu fotografiju u otpad?}one{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> fotografiju u otpad?}few{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> fotografije u otpad?}other{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> fotografija u otpad?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Premještanje fotografije u otpad…}one{Premještanje <xliff:g id="COUNT">^1</xliff:g> fotografije u otpad…}few{Premještanje <xliff:g id="COUNT">^1</xliff:g> fotografije u otpad…}other{Premještanje <xliff:g id="COUNT">^1</xliff:g> fotografija u otpad…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Dozvoliti da <xliff:g id="APP_NAME_0">^1</xliff:g> premjesti ovu stavku u otpad?}one{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> stavku u otpad?}few{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> stavke u otpad?}other{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> stavki u otpad?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Premještanje stavke u otpad…}one{Premještanje <xliff:g id="COUNT">^1</xliff:g> stavke u otpad…}few{Premještanje <xliff:g id="COUNT">^1</xliff:g> stavke u otpad…}other{Premještanje <xliff:g id="COUNT">^1</xliff:g> stavki u otpad…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Dozvoliti da <xliff:g id="APP_NAME_0">^1</xliff:g> premjesti ovaj audio fajl iz otpada?}one{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> audio fajl iz otpada?}few{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> audio fajla iz otpada?}other{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> audio fajlova iz otpada?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Premještanje audio fajla iz otpada…}one{Premještanje <xliff:g id="COUNT">^1</xliff:g> audio fajla iz otpada…}few{Premještanje <xliff:g id="COUNT">^1</xliff:g> audio fajla iz otpada…}other{Premještanje <xliff:g id="COUNT">^1</xliff:g> audio fajlova iz otpada…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Dozvoliti da <xliff:g id="APP_NAME_0">^1</xliff:g> premjesti ovaj videozapis iz otpada?}one{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> videozapis iz otpada?}few{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> videozapisa iz otpada?}other{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> videozapisa iz otpada?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Premještanje videozapisa iz otpada…}one{Premještanje <xliff:g id="COUNT">^1</xliff:g> videozapisa iz otpada…}few{Premještanje <xliff:g id="COUNT">^1</xliff:g> videozapisa iz otpada…}other{Premještanje <xliff:g id="COUNT">^1</xliff:g> videozapisa iz otpada…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Dozvoliti da <xliff:g id="APP_NAME_0">^1</xliff:g> premjesti ovu fotografiju iz otpada?}one{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> fotografiju iz otpada?}few{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> fotografije iz otpada?}other{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> fotografija iz otpada?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Premještanje fotografije iz otpada…}one{Premještanje <xliff:g id="COUNT">^1</xliff:g> fotografije iz otpada…}few{Premještanje <xliff:g id="COUNT">^1</xliff:g> fotografije iz otpada…}other{Premještanje <xliff:g id="COUNT">^1</xliff:g> fotografija iz otpada…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Dozvoliti da <xliff:g id="APP_NAME_0">^1</xliff:g> premjesti ovu stavku iz otpada?}one{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> stavku iz otpada?}few{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> stavke iz otpada?}other{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> premjesti <xliff:g id="COUNT">^2</xliff:g> stavki iz otpada?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Premještanje stavke iz otpada…}one{Premještanje <xliff:g id="COUNT">^1</xliff:g> stavke iz otpada…}few{Premještanje <xliff:g id="COUNT">^1</xliff:g> stavke iz otpada…}other{Premještanje <xliff:g id="COUNT">^1</xliff:g> stavki iz otpada…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Dozvoliti da <xliff:g id="APP_NAME_0">^1</xliff:g> izbriše ovaj audio fajl?}one{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> audio fajl?}few{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> audio fajla?}other{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> audio fajlova?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Brisanje audio fajla…}one{Brisanje <xliff:g id="COUNT">^1</xliff:g> audio fajla…}few{Brisanje <xliff:g id="COUNT">^1</xliff:g> audio fajla…}other{Brisanje <xliff:g id="COUNT">^1</xliff:g> audio fajlova…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Dozvoliti da <xliff:g id="APP_NAME_0">^1</xliff:g> izbriše ovaj videozapis?}one{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> videozapis?}few{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> videozapisa?}other{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> videozapisa?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Brisanje videozapisa…}one{Brisanje <xliff:g id="COUNT">^1</xliff:g> videozapisa…}few{Brisanje <xliff:g id="COUNT">^1</xliff:g> videozapisa…}other{Brisanje <xliff:g id="COUNT">^1</xliff:g> videozapisa…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Dozvoliti da <xliff:g id="APP_NAME_0">^1</xliff:g> izbriše ovu fotografiju?}one{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> fotografiju?}few{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> fotografije?}other{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> fotografija?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Brisanje fotografije…}one{Brisanje <xliff:g id="COUNT">^1</xliff:g> fotografije…}few{Brisanje <xliff:g id="COUNT">^1</xliff:g> fotografije…}other{Brisanje <xliff:g id="COUNT">^1</xliff:g> fotografija…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Dozvoliti da <xliff:g id="APP_NAME_0">^1</xliff:g> izbriše ovu stavku?}one{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> stavku?}few{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> stavke?}other{Dozvoliti da <xliff:g id="APP_NAME_1">^1</xliff:g> izbriše <xliff:g id="COUNT">^2</xliff:g> stavki?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Brisanje stavke…}one{Brisanje <xliff:g id="COUNT">^1</xliff:g> stavke…}few{Brisanje <xliff:g id="COUNT">^1</xliff:g> stavke…}other{Brisanje <xliff:g id="COUNT">^1</xliff:g> stavki…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> ne može obrađivati medijske fajlove"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Obrada medijskih fajlova je otkazana"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Greška prilikom obrade medijskih fajlova"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 9341998..8ff3eb3 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Selector de fotos"</string>
<string name="artist_label" msgid="8105600993099120273">"Artista"</string>
<string name="unknown" msgid="2059049215682829375">"Desconegut"</string>
<string name="root_images" msgid="5861633549189045666">"Imatges"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Continua"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Permet"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Denega"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other"><xliff:g id="COUNT_1">^1</xliff:g> més</item>
- <item quantity="one"><xliff:g id="COUNT_0">^1</xliff:g> més</item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other"><xliff:g id="COUNT_1">^1</xliff:g> elements addicionals més</item>
- <item quantity="one"><xliff:g id="COUNT_0">^1</xliff:g> element addicional més</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> element addicional més}other{<xliff:g id="COUNT_1">^1</xliff:g> elements addicionals més}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Esborra fitxers temporals d\'aplicacions"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vol esborrar alguns fitxers temporals. Això pot suposar un augment de l\'ús de la bateria o de les dades mòbils."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"S\'estan esborrant els fitxers temporals d\'aplicacions…"</string>
<string name="clear" msgid="5524638938415865915">"Esborra"</string>
<string name="allow" msgid="8885707816848569619">"Permet"</string>
<string name="deny" msgid="6040983710442068936">"Denega"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> modifiqui <xliff:g id="COUNT">^2</xliff:g> fitxers d\'àudio?</item>
- <item quantity="one">Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> modifiqui aquest fitxer d\'àudio?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">S\'estan modificant <xliff:g id="COUNT">^1</xliff:g> fitxers d\'àudio…</item>
- <item quantity="one">S\'està modificant el fitxer d\'àudio…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> modifiqui <xliff:g id="COUNT">^2</xliff:g> vídeos?</item>
- <item quantity="one">Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> modifiqui aquest vídeo?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">S\'estan modificant <xliff:g id="COUNT">^1</xliff:g> vídeos…</item>
- <item quantity="one">S\'està modificant el vídeo…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> modifiqui <xliff:g id="COUNT">^2</xliff:g> fotos?</item>
- <item quantity="one">Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> modifiqui aquesta foto?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">S\'estan modificant <xliff:g id="COUNT">^1</xliff:g> fotos…</item>
- <item quantity="one">S\'està modificant la foto…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> modifiqui <xliff:g id="COUNT">^2</xliff:g> elements?</item>
- <item quantity="one">Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> modifiqui aquest element?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">S\'estan modificant <xliff:g id="COUNT">^1</xliff:g> elements…</item>
- <item quantity="one">S\'està modificant l\'element…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> mogui <xliff:g id="COUNT">^2</xliff:g> fitxers d\'àudio a la paperera?</item>
- <item quantity="one">Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> mogui aquest fitxer d\'àudio a la paperera?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">S\'estan movent <xliff:g id="COUNT">^1</xliff:g> fitxers d\'àudio a la paperera…</item>
- <item quantity="one">S\'està movent el fitxer d\'àudio a la paperera…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> mogui <xliff:g id="COUNT">^2</xliff:g> vídeos a la paperera?</item>
- <item quantity="one">Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> mogui aquest vídeo a la paperera?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">S\'estan movent <xliff:g id="COUNT">^1</xliff:g> vídeos a la paperera…</item>
- <item quantity="one">S\'està movent el vídeo a la paperera…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> mogui <xliff:g id="COUNT">^2</xliff:g> fotos a la paperera?</item>
- <item quantity="one">Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> mogui aquesta foto a la paperera?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">S\'estan movent <xliff:g id="COUNT">^1</xliff:g> fotos a la paperera…</item>
- <item quantity="one">S\'està movent la foto a la paperera…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> mogui <xliff:g id="COUNT">^2</xliff:g> elements a la paperera?</item>
- <item quantity="one">Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> mogui aquest element a la paperera?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">S\'estan movent <xliff:g id="COUNT">^1</xliff:g> elements a la paperera…</item>
- <item quantity="one">S\'està movent l\'element a la paperera…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> tregui <xliff:g id="COUNT">^2</xliff:g> fitxers d\'àudio de la paperera?</item>
- <item quantity="one">Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> tregui aquest fitxer d\'àudio de la paperera?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">S\'estan traient <xliff:g id="COUNT">^1</xliff:g> fitxers d\'àudio de la paperera…</item>
- <item quantity="one">S\'està traient el fitxer d\'àudio de la paperera…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> tregui <xliff:g id="COUNT">^2</xliff:g> vídeos de la paperera?</item>
- <item quantity="one">Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> tregui aquest vídeo de la paperera?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">S\'estan traient <xliff:g id="COUNT">^1</xliff:g> vídeos de la paperera…</item>
- <item quantity="one">S\'està traient el vídeo de la paperera…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> tregui <xliff:g id="COUNT">^2</xliff:g> fotos de la paperera?</item>
- <item quantity="one">Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> tregui aquesta foto de la paperera?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">S\'estan traient <xliff:g id="COUNT">^1</xliff:g> fotos de la paperera…</item>
- <item quantity="one">S\'està traient la foto de la paperera…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> tregui <xliff:g id="COUNT">^2</xliff:g> elements de la paperera?</item>
- <item quantity="one">Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> tregui aquest element de la paperera?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">S\'estan traient <xliff:g id="COUNT">^1</xliff:g> elements de la paperera…</item>
- <item quantity="one">S\'està traient l\'element de la paperera…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> suprimeixi <xliff:g id="COUNT">^2</xliff:g> fitxers d\'àudio?</item>
- <item quantity="one">Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> suprimeixi aquest fitxer d\'àudio?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">S\'estan suprimint <xliff:g id="COUNT">^1</xliff:g> fitxers d\'àudio…</item>
- <item quantity="one">S\'està suprimint el fitxer d\'àudio…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> suprimeixi <xliff:g id="COUNT">^2</xliff:g> vídeos?</item>
- <item quantity="one">Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> suprimeixi aquest vídeo?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">S\'estan suprimint <xliff:g id="COUNT">^1</xliff:g> vídeos…</item>
- <item quantity="one">S\'està suprimint el vídeo…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> suprimeixi <xliff:g id="COUNT">^2</xliff:g> fotos?</item>
- <item quantity="one">Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> suprimeixi aquesta foto?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">S\'estan suprimint <xliff:g id="COUNT">^1</xliff:g> fotos…</item>
- <item quantity="one">S\'està suprimint la foto…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> suprimeixi <xliff:g id="COUNT">^2</xliff:g> elements?</item>
- <item quantity="one">Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> suprimeixi aquest element?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">S\'estan suprimint <xliff:g id="COUNT">^1</xliff:g> elements…</item>
- <item quantity="one">S\'està suprimint l\'element…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Afegeix"</string>
+ <string name="deselect" msgid="4297825044827769490">"Desselecciona"</string>
+ <string name="deselected" msgid="8488133193326208475">"Desseleccionat"</string>
+ <string name="select" msgid="2704765470563027689">"Selecciona"</string>
+ <string name="selected" msgid="9151797369975828124">"Seleccionat"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Selecciona fins a <xliff:g id="COUNT_0">^1</xliff:g> element}other{Selecciona fins a <xliff:g id="COUNT_1">^1</xliff:g> elements}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Recent"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"No hi ha cap foto ni vídeo"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"No hi ha cap àlbum"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Mostra la selecció"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Fotos"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Àlbums"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Previsualitza"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Canvia al perfil de treball"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Canvia al perfil personal"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Bloquejat per l\'administrador"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"No està permès accedir a les dades de treball des d\'una aplicació personal"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"No està permès accedir a les dades personals des d\'una aplicació de treball"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Les aplicacions de treball estan en pausa"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Per obrir les fotos de treball, activa les aplicacions de treball i torna-ho a provar"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Aquesta aplicació només pot accedir a les fotos que seleccionis"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> element}other{<xliff:g id="COUNT_1">^1</xliff:g> elements}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Afegeix (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Càmera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Baixades"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Preferits"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Captures de pantalla"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Foto amb moviment"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"Data i hora de creació (<xliff:g id="ITEM_NAME">%1$s</xliff:g>): <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Vídeo gravat el dia <xliff:g id="TIME">%1$s</xliff:g> que dura <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"foto"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"foto amb moviment"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Silencia el vídeo"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Deixa de silenciar el vídeo"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Reprodueix el vídeo"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Posa en pausa el vídeo"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"El contingut multimèdia al núvol ara està disponible des de <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"no seleccionat"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> modifiqui aquest fitxer d\'àudio?}other{Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> modifiqui <xliff:g id="COUNT">^2</xliff:g> fitxers d\'àudio?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{S\'està modificant el fitxer d\'àudio…}other{S\'estan modificant <xliff:g id="COUNT">^1</xliff:g> fitxers d\'àudio…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> modifiqui aquest vídeo?}other{Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> modifiqui <xliff:g id="COUNT">^2</xliff:g> vídeos?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{S\'està modificant el vídeo…}other{S\'estan modificant <xliff:g id="COUNT">^1</xliff:g> vídeos…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> modifiqui aquesta foto?}other{Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> modifiqui <xliff:g id="COUNT">^2</xliff:g> fotos?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{S\'està modificant la foto…}other{S\'estan modificant <xliff:g id="COUNT">^1</xliff:g> fotos…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> modifiqui aquest element?}other{Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> modifiqui <xliff:g id="COUNT">^2</xliff:g> elements?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{S\'està modificant l\'element…}other{S\'estan modificant <xliff:g id="COUNT">^1</xliff:g> elements…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> mogui aquest fitxer d\'àudio a la paperera?}other{Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> mogui <xliff:g id="COUNT">^2</xliff:g> fitxers d\'àudio a la paperera?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{S\'està movent el fitxer d\'àudio a la paperera…}other{S\'estan movent <xliff:g id="COUNT">^1</xliff:g> fitxers d\'àudio a la paperera…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> mogui aquest vídeo a la paperera?}other{Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> mogui <xliff:g id="COUNT">^2</xliff:g> vídeos a la paperera?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{S\'està movent el vídeo a la paperera…}other{S\'estan movent <xliff:g id="COUNT">^1</xliff:g> vídeos a la paperera…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> mogui aquesta foto a la paperera?}other{Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> mogui <xliff:g id="COUNT">^2</xliff:g> fotos a la paperera?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{S\'està movent la foto a la paperera…}other{S\'estan movent <xliff:g id="COUNT">^1</xliff:g> fotos a la paperera…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> mogui aquest element a la paperera?}other{Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> mogui <xliff:g id="COUNT">^2</xliff:g> elements a la paperera?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{S\'està movent l\'element a la paperera…}other{S\'estan movent <xliff:g id="COUNT">^1</xliff:g> elements a la paperera…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> tregui aquest fitxer d\'àudio de la paperera?}other{Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> tregui <xliff:g id="COUNT">^2</xliff:g> fitxers d\'àudio de la paperera?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{S\'està traient el fitxer d\'àudio de la paperera…}other{S\'estan traient <xliff:g id="COUNT">^1</xliff:g> fitxers d\'àudio de la paperera…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> tregui aquest vídeo de la paperera?}other{Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> tregui <xliff:g id="COUNT">^2</xliff:g> vídeos de la paperera?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{S\'està traient el vídeo de la paperera…}other{S\'estan traient <xliff:g id="COUNT">^1</xliff:g> vídeos de la paperera…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> tregui aquesta foto de la paperera?}other{Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> tregui <xliff:g id="COUNT">^2</xliff:g> fotos de la paperera?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{S\'està traient la foto de la paperera…}other{S\'estan traient <xliff:g id="COUNT">^1</xliff:g> fotos de la paperera…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> tregui aquest element de la paperera?}other{Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> tregui <xliff:g id="COUNT">^2</xliff:g> elements de la paperera?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{S\'està traient l\'element de la paperera…}other{S\'estan traient <xliff:g id="COUNT">^1</xliff:g> elements de la paperera…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> suprimeixi aquest fitxer d\'àudio?}other{Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> suprimeixi <xliff:g id="COUNT">^2</xliff:g> fitxers d\'àudio?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{S\'està suprimint el fitxer d\'àudio…}other{S\'estan suprimint <xliff:g id="COUNT">^1</xliff:g> fitxers d\'àudio…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> suprimeixi aquest vídeo?}other{Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> suprimeixi <xliff:g id="COUNT">^2</xliff:g> vídeos?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{S\'està suprimint el vídeo…}other{S\'estan suprimint <xliff:g id="COUNT">^1</xliff:g> vídeos…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> suprimeixi aquesta foto?}other{Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> suprimeixi <xliff:g id="COUNT">^2</xliff:g> fotos?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{S\'està suprimint la foto…}other{S\'estan suprimint <xliff:g id="COUNT">^1</xliff:g> fotos…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Vols permetre que <xliff:g id="APP_NAME_0">^1</xliff:g> suprimeixi aquest element?}other{Vols permetre que <xliff:g id="APP_NAME_1">^1</xliff:g> suprimeixi <xliff:g id="COUNT">^2</xliff:g> elements?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{S\'està suprimint l\'element…}other{S\'estan suprimint <xliff:g id="COUNT">^1</xliff:g> elements…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> no pot processar els fitxers multimèdia"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"El processament del contingut multimèdia s\'ha cancel·lat"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"S\'ha produït un error en processar el contingut multimèdia"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index eab7293..3445ff1 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Výběr fotek"</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>
@@ -29,216 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Pokračovat"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Povolit"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Zamítnout"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="few">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="many">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="few">Plus <xliff:g id="COUNT_1">^1</xliff:g> další položky</item>
- <item quantity="many">Plus <xliff:g id="COUNT_1">^1</xliff:g> další položky</item>
- <item quantity="other">Plus <xliff:g id="COUNT_1">^1</xliff:g> dalších položek</item>
- <item quantity="one">Plus <xliff:g id="COUNT_0">^1</xliff:g> další položka</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}few{+<xliff:g id="COUNT_1">^1</xliff:g>}many{+<xliff:g id="COUNT_1">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Plus <xliff:g id="COUNT_0">^1</xliff:g> další položka}few{Plus <xliff:g id="COUNT_1">^1</xliff:g> další položky}many{Plus <xliff:g id="COUNT_1">^1</xliff:g> další položky}other{Plus <xliff:g id="COUNT_1">^1</xliff:g> dalších položek}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Vymazání dočasných souborů aplikací"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"Aplikace <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> chce vymazat nějaké dočasné soubory. Tato akce může vést ke zvýšenému využití baterie nebo mobilních dat."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Mazání dočasných souborů aplikace…"</string>
<string name="clear" msgid="5524638938415865915">"Vymazat"</string>
<string name="allow" msgid="8885707816848569619">"Povolit"</string>
<string name="deny" msgid="6040983710442068936">"Zakázat"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="few">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> upravit <xliff:g id="COUNT">^2</xliff:g> zvukové soubory?</item>
- <item quantity="many">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> upravit <xliff:g id="COUNT">^2</xliff:g> zvukového souboru?</item>
- <item quantity="other">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> upravit <xliff:g id="COUNT">^2</xliff:g> zvukových souborů?</item>
- <item quantity="one">Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> upravit tento zvukový soubor?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="few">Úprava <xliff:g id="COUNT">^1</xliff:g> zvukových souborů…</item>
- <item quantity="many">Úprava <xliff:g id="COUNT">^1</xliff:g> zvukového souboru…</item>
- <item quantity="other">Úprava <xliff:g id="COUNT">^1</xliff:g> zvukových souborů…</item>
- <item quantity="one">Úprava zvukového souboru…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="few">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> upravit <xliff:g id="COUNT">^2</xliff:g> videa?</item>
- <item quantity="many">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> upravit <xliff:g id="COUNT">^2</xliff:g> videa?</item>
- <item quantity="other">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> upravit <xliff:g id="COUNT">^2</xliff:g> videí?</item>
- <item quantity="one">Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> upravit toto video?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="few">Úprava <xliff:g id="COUNT">^1</xliff:g> videí…</item>
- <item quantity="many">Úprava <xliff:g id="COUNT">^1</xliff:g> videa…</item>
- <item quantity="other">Úprava <xliff:g id="COUNT">^1</xliff:g> videí…</item>
- <item quantity="one">Úprava videa…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="few">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> upravit <xliff:g id="COUNT">^2</xliff:g> fotky?</item>
- <item quantity="many">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> upravit <xliff:g id="COUNT">^2</xliff:g> fotky?</item>
- <item quantity="other">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> upravit <xliff:g id="COUNT">^2</xliff:g> fotek?</item>
- <item quantity="one">Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> upravit tuto fotku?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="few">Úprava <xliff:g id="COUNT">^1</xliff:g> fotek…</item>
- <item quantity="many">Úprava <xliff:g id="COUNT">^1</xliff:g> fotky…</item>
- <item quantity="other">Úprava <xliff:g id="COUNT">^1</xliff:g> fotek…</item>
- <item quantity="one">Úprava fotky…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="few">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> upravit <xliff:g id="COUNT">^2</xliff:g> položky?</item>
- <item quantity="many">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> upravit <xliff:g id="COUNT">^2</xliff:g> položky?</item>
- <item quantity="other">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> upravit <xliff:g id="COUNT">^2</xliff:g> položek?</item>
- <item quantity="one">Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> upravit tuto položku?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="few">Úprava <xliff:g id="COUNT">^1</xliff:g> položek…</item>
- <item quantity="many">Úprava <xliff:g id="COUNT">^1</xliff:g> položky…</item>
- <item quantity="other">Úprava <xliff:g id="COUNT">^1</xliff:g> položek…</item>
- <item quantity="one">Úprava položky…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="few">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> přesunout <xliff:g id="COUNT">^2</xliff:g> zvukové soubory do koše?</item>
- <item quantity="many">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> přesunout <xliff:g id="COUNT">^2</xliff:g> zvukového souboru do koše?</item>
- <item quantity="other">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> přesunout <xliff:g id="COUNT">^2</xliff:g> zvukových souborů do koše?</item>
- <item quantity="one">Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> přesunout tento zvukový soubor do koše?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="few">Přesouvání <xliff:g id="COUNT">^1</xliff:g> zvukových souborů do koše…</item>
- <item quantity="many">Přesouvání <xliff:g id="COUNT">^1</xliff:g> zvukového souboru do koše…</item>
- <item quantity="other">Přesouvání <xliff:g id="COUNT">^1</xliff:g> zvukových souborů do koše…</item>
- <item quantity="one">Přesouvání zvukového souboru do koše…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="few">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> přesunout <xliff:g id="COUNT">^2</xliff:g> videa do koše?</item>
- <item quantity="many">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> přesunout <xliff:g id="COUNT">^2</xliff:g> videa do koše?</item>
- <item quantity="other">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> přesunout <xliff:g id="COUNT">^2</xliff:g> videí do koše?</item>
- <item quantity="one">Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> přesunout toto video do koše?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="few">Přesouvání <xliff:g id="COUNT">^1</xliff:g> videí do koše…</item>
- <item quantity="many">Přesouvání <xliff:g id="COUNT">^1</xliff:g> videa do koše…</item>
- <item quantity="other">Přesouvání <xliff:g id="COUNT">^1</xliff:g> videí do koše…</item>
- <item quantity="one">Přesouvání videa do koše…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="few">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> přesunout <xliff:g id="COUNT">^2</xliff:g> fotky do koše?</item>
- <item quantity="many">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> přesunout <xliff:g id="COUNT">^2</xliff:g> fotky do koše?</item>
- <item quantity="other">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> přesunout <xliff:g id="COUNT">^2</xliff:g> fotek do koše?</item>
- <item quantity="one">Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> přesunout tuto fotku do koše?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="few">Přesouvání <xliff:g id="COUNT">^1</xliff:g> fotek do koše…</item>
- <item quantity="many">Přesouvání <xliff:g id="COUNT">^1</xliff:g> fotky do koše…</item>
- <item quantity="other">Přesouvání <xliff:g id="COUNT">^1</xliff:g> fotek do koše…</item>
- <item quantity="one">Přesouvání fotky do koše…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="few">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> přesunout <xliff:g id="COUNT">^2</xliff:g> položky do koše?</item>
- <item quantity="many">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> přesunout <xliff:g id="COUNT">^2</xliff:g> položky do koše?</item>
- <item quantity="other">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> přesunout <xliff:g id="COUNT">^2</xliff:g> položek do koše?</item>
- <item quantity="one">Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> přesunout tuto položku do koše?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="few">Přesouvání <xliff:g id="COUNT">^1</xliff:g> položek do koše…</item>
- <item quantity="many">Přesouvání <xliff:g id="COUNT">^1</xliff:g> položky do koše…</item>
- <item quantity="other">Přesouvání <xliff:g id="COUNT">^1</xliff:g> položek do koše…</item>
- <item quantity="one">Přesouvání položky do koše…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="few">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> vyjmout <xliff:g id="COUNT">^2</xliff:g> zvukové soubory z koše?</item>
- <item quantity="many">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> vyjmout <xliff:g id="COUNT">^2</xliff:g> zvukového souboru z koše?</item>
- <item quantity="other">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> vyjmout <xliff:g id="COUNT">^2</xliff:g> zvukových souborů z koše?</item>
- <item quantity="one">Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> vyjmout tento zvukový soubor z koše?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="few">Přesouvání <xliff:g id="COUNT">^1</xliff:g> zvukových souborů z koše…</item>
- <item quantity="many">Přesouvání <xliff:g id="COUNT">^1</xliff:g> zvukového souboru z koše…</item>
- <item quantity="other">Přesouvání <xliff:g id="COUNT">^1</xliff:g> zvukových souborů z koše…</item>
- <item quantity="one">Přesouvání zvukového souboru z koše…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="few">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> vyjmout <xliff:g id="COUNT">^2</xliff:g> videa z koše?</item>
- <item quantity="many">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> vyjmout <xliff:g id="COUNT">^2</xliff:g> videa z koše?</item>
- <item quantity="other">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> vyjmout <xliff:g id="COUNT">^2</xliff:g> videí z koše?</item>
- <item quantity="one">Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> vyjmout toto video z koše?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="few">Přesouvání <xliff:g id="COUNT">^1</xliff:g> videí z koše…</item>
- <item quantity="many">Přesouvání <xliff:g id="COUNT">^1</xliff:g> videa z koše…</item>
- <item quantity="other">Přesouvání <xliff:g id="COUNT">^1</xliff:g> videí z koše…</item>
- <item quantity="one">Přesouvání videa z koše…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="few">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> vyjmout <xliff:g id="COUNT">^2</xliff:g> fotky z koše?</item>
- <item quantity="many">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> vyjmout <xliff:g id="COUNT">^2</xliff:g> fotky z koše?</item>
- <item quantity="other">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> vyjmout <xliff:g id="COUNT">^2</xliff:g> fotek z koše?</item>
- <item quantity="one">Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> vyjmout tuto fotku z koše?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="few">Přesouvání <xliff:g id="COUNT">^1</xliff:g> fotek z koše…</item>
- <item quantity="many">Přesouvání <xliff:g id="COUNT">^1</xliff:g> fotky z koše…</item>
- <item quantity="other">Přesouvání <xliff:g id="COUNT">^1</xliff:g> fotek z koše…</item>
- <item quantity="one">Přesouvání fotky z koše…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="few">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> vyjmout <xliff:g id="COUNT">^2</xliff:g> položky z koše?</item>
- <item quantity="many">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> vyjmout <xliff:g id="COUNT">^2</xliff:g> položky z koše?</item>
- <item quantity="other">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> vyjmout <xliff:g id="COUNT">^2</xliff:g> položek z koše?</item>
- <item quantity="one">Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> vyjmout tuto položku z koše?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="few">Přesouvání <xliff:g id="COUNT">^1</xliff:g> položek z koše…</item>
- <item quantity="many">Přesouvání <xliff:g id="COUNT">^1</xliff:g> položky z koše…</item>
- <item quantity="other">Přesouvání <xliff:g id="COUNT">^1</xliff:g> položek z koše…</item>
- <item quantity="one">Přesouvání položky z koše…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="few">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> smazat <xliff:g id="COUNT">^2</xliff:g> zvukové soubory?</item>
- <item quantity="many">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> smazat <xliff:g id="COUNT">^2</xliff:g> zvukového souboru?</item>
- <item quantity="other">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> smazat <xliff:g id="COUNT">^2</xliff:g> zvukových souborů?</item>
- <item quantity="one">Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> smazat tento zvukový soubor?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="few">Mazání <xliff:g id="COUNT">^1</xliff:g> zvukových souborů…</item>
- <item quantity="many">Mazání <xliff:g id="COUNT">^1</xliff:g> zvukového souboru…</item>
- <item quantity="other">Mazání <xliff:g id="COUNT">^1</xliff:g> zvukových souborů…</item>
- <item quantity="one">Mazání zvukového souboru…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="few">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> smazat <xliff:g id="COUNT">^2</xliff:g> videa?</item>
- <item quantity="many">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> smazat <xliff:g id="COUNT">^2</xliff:g> videa?</item>
- <item quantity="other">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> smazat <xliff:g id="COUNT">^2</xliff:g> videí?</item>
- <item quantity="one">Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> smazat toto video?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="few">Mazání <xliff:g id="COUNT">^1</xliff:g> videí…</item>
- <item quantity="many">Mazání <xliff:g id="COUNT">^1</xliff:g> videa…</item>
- <item quantity="other">Mazání <xliff:g id="COUNT">^1</xliff:g> videí…</item>
- <item quantity="one">Mazání videa…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="few">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> smazat <xliff:g id="COUNT">^2</xliff:g> fotky?</item>
- <item quantity="many">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> smazat <xliff:g id="COUNT">^2</xliff:g> fotky?</item>
- <item quantity="other">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> smazat <xliff:g id="COUNT">^2</xliff:g> fotek?</item>
- <item quantity="one">Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> smazat tuto fotku?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="few">Mazání <xliff:g id="COUNT">^1</xliff:g> fotek…</item>
- <item quantity="many">Mazání <xliff:g id="COUNT">^1</xliff:g> fotky…</item>
- <item quantity="other">Mazání <xliff:g id="COUNT">^1</xliff:g> fotek…</item>
- <item quantity="one">Mazání fotky…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="few">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> smazat <xliff:g id="COUNT">^2</xliff:g> položky?</item>
- <item quantity="many">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> smazat <xliff:g id="COUNT">^2</xliff:g> položky?</item>
- <item quantity="other">Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> smazat <xliff:g id="COUNT">^2</xliff:g> položek?</item>
- <item quantity="one">Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> smazat tuto položku?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="few">Mazání <xliff:g id="COUNT">^1</xliff:g> položek…</item>
- <item quantity="many">Mazání <xliff:g id="COUNT">^1</xliff:g> položky…</item>
- <item quantity="other">Mazání <xliff:g id="COUNT">^1</xliff:g> položek…</item>
- <item quantity="one">Mazání položky…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Přidat"</string>
+ <string name="deselect" msgid="4297825044827769490">"Zrušit výběr"</string>
+ <string name="deselected" msgid="8488133193326208475">"Výběr zrušen"</string>
+ <string name="select" msgid="2704765470563027689">"Vybrat"</string>
+ <string name="selected" msgid="9151797369975828124">"Vybráno"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Vyberte maximálně <xliff:g id="COUNT_0">^1</xliff:g> položku}few{Vyberte maximálně <xliff:g id="COUNT_1">^1</xliff:g> položky}many{Vyberte maximálně <xliff:g id="COUNT_1">^1</xliff:g> položky}other{Vyberte maximálně <xliff:g id="COUNT_1">^1</xliff:g> položek}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Nedávné"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Žádné fotky ani videa"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Žádná alba"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Zobrazit vybrané"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Fotky"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Alba"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Náhled"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Přepnout na pracovní profil"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Přepnout na osobní profil"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Blokováno administrátorem"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Z osobní aplikace není přístup k pracovním datům povolen"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Z pracovní aplikace není přístup k osobním datům povolen"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Pracovní aplikace jsou pozastaveny"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Pokud chcete otevřít pracovní fotky, zapněte pracovní aplikace a zkuste to znovu"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Tato aplikace má přístup pouze k fotkám, které vyberete"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> položka}few{<xliff:g id="COUNT_1">^1</xliff:g> položky}many{<xliff:g id="COUNT_1">^1</xliff:g> položky}other{<xliff:g id="COUNT_1">^1</xliff:g> položek}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Přidat (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Fotoaparát"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Stažené soubory"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Oblíbené"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Snímky obrazovek"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Pohyblivá fotka"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> – pořízeno <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Video pořízené <xliff:g id="TIME">%1$s</xliff:g> o délce <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Fotka"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Pohyblivá fotka"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Vypnout video"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Zapnout video"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Přehrát video"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Pozastavit video"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Cloudová média jsou teď k dispozici ze zdroje <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"nevybráno"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> upravit tento zvukový soubor?}few{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> upravit <xliff:g id="COUNT">^2</xliff:g> zvukové soubory?}many{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> upravit <xliff:g id="COUNT">^2</xliff:g> zvukového souboru?}other{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> upravit <xliff:g id="COUNT">^2</xliff:g> zvukových souborů?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Úprava zvukového souboru…}few{Úprava <xliff:g id="COUNT">^1</xliff:g> zvukových souborů…}many{Úprava <xliff:g id="COUNT">^1</xliff:g> zvukového souboru…}other{Úprava <xliff:g id="COUNT">^1</xliff:g> zvukových souborů…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> upravit toto video?}few{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> upravit <xliff:g id="COUNT">^2</xliff:g> videa?}many{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> upravit <xliff:g id="COUNT">^2</xliff:g> videa?}other{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> upravit <xliff:g id="COUNT">^2</xliff:g> videí?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Úprava videa…}few{Úprava <xliff:g id="COUNT">^1</xliff:g> videí…}many{Úprava <xliff:g id="COUNT">^1</xliff:g> videa…}other{Úprava <xliff:g id="COUNT">^1</xliff:g> videí…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> upravit tuto fotku?}few{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> upravit <xliff:g id="COUNT">^2</xliff:g> fotky?}many{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> upravit <xliff:g id="COUNT">^2</xliff:g> fotky?}other{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> upravit <xliff:g id="COUNT">^2</xliff:g> fotek?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Úprava fotky…}few{Úprava <xliff:g id="COUNT">^1</xliff:g> fotek…}many{Úprava <xliff:g id="COUNT">^1</xliff:g> fotky…}other{Úprava <xliff:g id="COUNT">^1</xliff:g> fotek…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> upravit tuto položku?}few{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> upravit <xliff:g id="COUNT">^2</xliff:g> položky?}many{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> upravit <xliff:g id="COUNT">^2</xliff:g> položky?}other{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> upravit <xliff:g id="COUNT">^2</xliff:g> položek?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Úprava položky…}few{Úprava <xliff:g id="COUNT">^1</xliff:g> položek…}many{Úprava <xliff:g id="COUNT">^1</xliff:g> položky…}other{Úprava <xliff:g id="COUNT">^1</xliff:g> položek…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> přesunout tento zvukový soubor do koše?}few{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> přesunout <xliff:g id="COUNT">^2</xliff:g> zvukové soubory do koše?}many{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> přesunout <xliff:g id="COUNT">^2</xliff:g> zvukového souboru do koše?}other{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> přesunout <xliff:g id="COUNT">^2</xliff:g> zvukových souborů do koše?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Přesouvání zvukového souboru do koše…}few{Přesouvání <xliff:g id="COUNT">^1</xliff:g> zvukových souborů do koše…}many{Přesouvání <xliff:g id="COUNT">^1</xliff:g> zvukového souboru do koše…}other{Přesouvání <xliff:g id="COUNT">^1</xliff:g> zvukových souborů do koše…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> přesunout toto video do koše?}few{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> přesunout <xliff:g id="COUNT">^2</xliff:g> videa do koše?}many{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> přesunout <xliff:g id="COUNT">^2</xliff:g> videa do koše?}other{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> přesunout <xliff:g id="COUNT">^2</xliff:g> videí do koše?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Přesouvání videa do koše…}few{Přesouvání <xliff:g id="COUNT">^1</xliff:g> videí do koše…}many{Přesouvání <xliff:g id="COUNT">^1</xliff:g> videa do koše…}other{Přesouvání <xliff:g id="COUNT">^1</xliff:g> videí do koše…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> přesunout tuto fotku do koše?}few{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> přesunout <xliff:g id="COUNT">^2</xliff:g> fotky do koše?}many{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> přesunout <xliff:g id="COUNT">^2</xliff:g> fotky do koše?}other{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> přesunout <xliff:g id="COUNT">^2</xliff:g> fotek do koše?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Přesouvání fotky do koše…}few{Přesouvání <xliff:g id="COUNT">^1</xliff:g> fotek do koše…}many{Přesouvání <xliff:g id="COUNT">^1</xliff:g> fotky do koše…}other{Přesouvání <xliff:g id="COUNT">^1</xliff:g> fotek do koše…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> přesunout tuto položku do koše?}few{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> přesunout <xliff:g id="COUNT">^2</xliff:g> položky do koše?}many{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> přesunout <xliff:g id="COUNT">^2</xliff:g> položky do koše?}other{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> přesunout <xliff:g id="COUNT">^2</xliff:g> položek do koše?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Přesouvání položky do koše…}few{Přesouvání <xliff:g id="COUNT">^1</xliff:g> položek do koše…}many{Přesouvání <xliff:g id="COUNT">^1</xliff:g> položky do koše…}other{Přesouvání <xliff:g id="COUNT">^1</xliff:g> položek do koše…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> vyjmout tento zvukový soubor z koše?}few{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> vyjmout <xliff:g id="COUNT">^2</xliff:g> zvukové soubory z koše?}many{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> vyjmout <xliff:g id="COUNT">^2</xliff:g> zvukového souboru z koše?}other{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> vyjmout <xliff:g id="COUNT">^2</xliff:g> zvukových souborů z koše?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Přesouvání zvukového souboru z koše…}few{Přesouvání <xliff:g id="COUNT">^1</xliff:g> zvukových souborů z koše…}many{Přesouvání <xliff:g id="COUNT">^1</xliff:g> zvukového souboru z koše…}other{Přesouvání <xliff:g id="COUNT">^1</xliff:g> zvukových souborů z koše…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> vyjmout toto video z koše?}few{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> vyjmout <xliff:g id="COUNT">^2</xliff:g> videa z koše?}many{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> vyjmout <xliff:g id="COUNT">^2</xliff:g> videa z koše?}other{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> vyjmout <xliff:g id="COUNT">^2</xliff:g> videí z koše?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Přesouvání videa z koše…}few{Přesouvání <xliff:g id="COUNT">^1</xliff:g> videí z koše…}many{Přesouvání <xliff:g id="COUNT">^1</xliff:g> videa z koše…}other{Přesouvání <xliff:g id="COUNT">^1</xliff:g> videí z koše…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> vyjmout tuto fotku z koše?}few{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> vyjmout <xliff:g id="COUNT">^2</xliff:g> fotky z koše?}many{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> vyjmout <xliff:g id="COUNT">^2</xliff:g> fotky z koše?}other{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> vyjmout <xliff:g id="COUNT">^2</xliff:g> fotek z koše?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Přesouvání fotky z koše…}few{Přesouvání <xliff:g id="COUNT">^1</xliff:g> fotek z koše…}many{Přesouvání <xliff:g id="COUNT">^1</xliff:g> fotky z koše…}other{Přesouvání <xliff:g id="COUNT">^1</xliff:g> fotek z koše…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> vyjmout tuto položku z koše?}few{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> vyjmout <xliff:g id="COUNT">^2</xliff:g> položky z koše?}many{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> vyjmout <xliff:g id="COUNT">^2</xliff:g> položky z koše?}other{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> vyjmout <xliff:g id="COUNT">^2</xliff:g> položek z koše?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Přesouvání položky z koše…}few{Přesouvání <xliff:g id="COUNT">^1</xliff:g> položek z koše…}many{Přesouvání <xliff:g id="COUNT">^1</xliff:g> položky z koše…}other{Přesouvání <xliff:g id="COUNT">^1</xliff:g> položek z koše…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> smazat tento zvukový soubor?}few{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> smazat <xliff:g id="COUNT">^2</xliff:g> zvukové soubory?}many{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> smazat <xliff:g id="COUNT">^2</xliff:g> zvukového souboru?}other{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> smazat <xliff:g id="COUNT">^2</xliff:g> zvukových souborů?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Mazání zvukového souboru…}few{Mazání <xliff:g id="COUNT">^1</xliff:g> zvukových souborů…}many{Mazání <xliff:g id="COUNT">^1</xliff:g> zvukového souboru…}other{Mazání <xliff:g id="COUNT">^1</xliff:g> zvukových souborů…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> smazat toto video?}few{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> smazat <xliff:g id="COUNT">^2</xliff:g> videa?}many{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> smazat <xliff:g id="COUNT">^2</xliff:g> videa?}other{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> smazat <xliff:g id="COUNT">^2</xliff:g> videí?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Mazání videa…}few{Mazání <xliff:g id="COUNT">^1</xliff:g> videí…}many{Mazání <xliff:g id="COUNT">^1</xliff:g> videa…}other{Mazání <xliff:g id="COUNT">^1</xliff:g> videí…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> smazat tuto fotku?}few{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> smazat <xliff:g id="COUNT">^2</xliff:g> fotky?}many{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> smazat <xliff:g id="COUNT">^2</xliff:g> fotky?}other{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> smazat <xliff:g id="COUNT">^2</xliff:g> fotek?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Mazání fotky…}few{Mazání <xliff:g id="COUNT">^1</xliff:g> fotek…}many{Mazání <xliff:g id="COUNT">^1</xliff:g> fotky…}other{Mazání <xliff:g id="COUNT">^1</xliff:g> fotek…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Povolit aplikaci <xliff:g id="APP_NAME_0">^1</xliff:g> smazat tuto položku?}few{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> smazat <xliff:g id="COUNT">^2</xliff:g> položky?}many{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> smazat <xliff:g id="COUNT">^2</xliff:g> položky?}other{Povolit aplikaci <xliff:g id="APP_NAME_1">^1</xliff:g> smazat <xliff:g id="COUNT">^2</xliff:g> položek?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Mazání položky…}few{Mazání <xliff:g id="COUNT">^1</xliff:g> položek…}many{Mazání <xliff:g id="COUNT">^1</xliff:g> položky…}other{Mazání <xliff:g id="COUNT">^1</xliff:g> položek…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"Aplikace <xliff:g id="APP_NAME">%s</xliff:g> nedokáže zpracovat mediální soubory"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Zpracování mediálního obsahu bylo zrušeno"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Při zpracování mediálního obsahu došlo k chybě"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index e5bba25..6f15ca2 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Billedvælger"</string>
<string name="artist_label" msgid="8105600993099120273">"Kunstner"</string>
<string name="unknown" msgid="2059049215682829375">"Ukendt"</string>
<string name="root_images" msgid="5861633549189045666">"Billeder"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Fortsæt"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Tillad"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Afvis"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">Plus <xliff:g id="COUNT_1">^1</xliff:g> andet element</item>
- <item quantity="other">Plus <xliff:g id="COUNT_1">^1</xliff:g> andre elementer</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}one{+<xliff:g id="COUNT_1">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Plus <xliff:g id="COUNT_0">^1</xliff:g> andet element}one{Plus <xliff:g id="COUNT_1">^1</xliff:g> andet element}other{Plus <xliff:g id="COUNT_1">^1</xliff:g> andre elementer}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Ryd midlertidige appfiler"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vil gerne rydde nogle midlertidige filer. Dette kan resultere i øget forbrug af batteri eller mobildata."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Rydder midlertidige appfiler…"</string>
<string name="clear" msgid="5524638938415865915">"Ryd"</string>
<string name="allow" msgid="8885707816848569619">"Tillad"</string>
<string name="deny" msgid="6040983710442068936">"Afvis"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at ændre <xliff:g id="COUNT">^2</xliff:g> lydfil?</item>
- <item quantity="other">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at ændre <xliff:g id="COUNT">^2</xliff:g> lydfiler?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one">Ændrer <xliff:g id="COUNT">^1</xliff:g> lydfil…</item>
- <item quantity="other">Ændrer <xliff:g id="COUNT">^1</xliff:g> lydfiler…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at ændre <xliff:g id="COUNT">^2</xliff:g> video?</item>
- <item quantity="other">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at ændre <xliff:g id="COUNT">^2</xliff:g> videoer?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one">Ændrer <xliff:g id="COUNT">^1</xliff:g> video…</item>
- <item quantity="other">Ændrer <xliff:g id="COUNT">^1</xliff:g> videoer…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at ændre <xliff:g id="COUNT">^2</xliff:g> billede?</item>
- <item quantity="other">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at ændre <xliff:g id="COUNT">^2</xliff:g> billeder?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one">Ændrer <xliff:g id="COUNT">^1</xliff:g> billede…</item>
- <item quantity="other">Ændrer <xliff:g id="COUNT">^1</xliff:g> billeder…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at ændre <xliff:g id="COUNT">^2</xliff:g> element?</item>
- <item quantity="other">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at ændre <xliff:g id="COUNT">^2</xliff:g> elementer?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one">Ændrer <xliff:g id="COUNT">^1</xliff:g> element…</item>
- <item quantity="other">Ændrer <xliff:g id="COUNT">^1</xliff:g> elementer…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> lydfil til papirkurven?</item>
- <item quantity="other">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> lydfiler til papirkurven?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one">Flytter <xliff:g id="COUNT">^1</xliff:g> lydfil til papirkurven…</item>
- <item quantity="other">Flytter <xliff:g id="COUNT">^1</xliff:g> lydfiler til papirkurven…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> video til papirkurven?</item>
- <item quantity="other">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> videoer til papirkurven?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one">Flytter <xliff:g id="COUNT">^1</xliff:g> video til papirkurven…</item>
- <item quantity="other">Flytter <xliff:g id="COUNT">^1</xliff:g> videoer til papirkurven…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> billede til papirkurven?</item>
- <item quantity="other">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> billeder til papirkurven?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one">Flytter <xliff:g id="COUNT">^1</xliff:g> billede til papirkurven…</item>
- <item quantity="other">Flytter <xliff:g id="COUNT">^1</xliff:g> billeder til papirkurven…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> element til papirkurven?</item>
- <item quantity="other">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> elementer til papirkurven?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one">Flytter <xliff:g id="COUNT">^1</xliff:g> element til papirkurven…</item>
- <item quantity="other">Flytter <xliff:g id="COUNT">^1</xliff:g> elementer til papirkurven…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> lydfil ud af papirkurven?</item>
- <item quantity="other">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> lydfiler ud af papirkurven?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one">Flytter <xliff:g id="COUNT">^1</xliff:g> lydfil ud af papirkurven…</item>
- <item quantity="other">Flytter <xliff:g id="COUNT">^1</xliff:g> lydfiler ud af papirkurven…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> video ud af papirkurven?</item>
- <item quantity="other">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> videoer ud af papirkurven?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one">Flytter <xliff:g id="COUNT">^1</xliff:g> video ud af papirkurven…</item>
- <item quantity="other">Flytter <xliff:g id="COUNT">^1</xliff:g> videoer ud af papirkurven…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> billede ud af papirkurven?</item>
- <item quantity="other">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> billeder ud af papirkurven?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one">Flytter <xliff:g id="COUNT">^1</xliff:g> billede ud af papirkurven…</item>
- <item quantity="other">Flytter <xliff:g id="COUNT">^1</xliff:g> billeder ud af papirkurven…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> element ud af papirkurven?</item>
- <item quantity="other">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> elementer ud af papirkurven?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one">Flytter <xliff:g id="COUNT">^1</xliff:g> element ud af papirkurven…</item>
- <item quantity="other">Flytter <xliff:g id="COUNT">^1</xliff:g> elementer ud af papirkurven…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at slette <xliff:g id="COUNT">^2</xliff:g> lydfil?</item>
- <item quantity="other">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at slette <xliff:g id="COUNT">^2</xliff:g> lydfiler?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one">Sletter <xliff:g id="COUNT">^1</xliff:g> lydfil…</item>
- <item quantity="other">Sletter <xliff:g id="COUNT">^1</xliff:g> lydfiler…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at slette <xliff:g id="COUNT">^2</xliff:g> video?</item>
- <item quantity="other">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at slette <xliff:g id="COUNT">^2</xliff:g> videoer?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one">Sletter <xliff:g id="COUNT">^1</xliff:g> video…</item>
- <item quantity="other">Sletter <xliff:g id="COUNT">^1</xliff:g> videoer…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at slette <xliff:g id="COUNT">^2</xliff:g> billede?</item>
- <item quantity="other">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at slette <xliff:g id="COUNT">^2</xliff:g> billeder?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one">Sletter <xliff:g id="COUNT">^1</xliff:g> billede…</item>
- <item quantity="other">Sletter <xliff:g id="COUNT">^1</xliff:g> billeder…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at slette <xliff:g id="COUNT">^2</xliff:g> element?</item>
- <item quantity="other">Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at slette <xliff:g id="COUNT">^2</xliff:g> elementer?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one">Sletter <xliff:g id="COUNT">^1</xliff:g> element…</item>
- <item quantity="other">Sletter <xliff:g id="COUNT">^1</xliff:g> elementer…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Tilføj"</string>
+ <string name="deselect" msgid="4297825044827769490">"Fravælg"</string>
+ <string name="deselected" msgid="8488133193326208475">"Fravalgt"</string>
+ <string name="select" msgid="2704765470563027689">"Vælg"</string>
+ <string name="selected" msgid="9151797369975828124">"Valgt"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Vælg op til <xliff:g id="COUNT_0">^1</xliff:g> element}one{Vælg op til <xliff:g id="COUNT_1">^1</xliff:g> element}other{Vælg op til <xliff:g id="COUNT_1">^1</xliff:g> elementer}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Seneste"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Ingen billeder eller videoer"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Ingen album"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Se valgte"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Billeder"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Album"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Forhåndsvisning"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Skift til arbejdsprofil"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Skift til personlig profil"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Blokeret af din administrator"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Det er ikke tilladt at tilgå arbejdsrelaterede data fra en personlig app"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Det er ikke tilladt at tilgå personoplysninger fra en arbejdsapp"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Dine arbejdsapps er sat på pause"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Hvis du vil åbne billeder fra arbejdsprofilen, skal du aktivere dine arbejdsapps og derefter prøve igen"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Denne app kan kun tilgå de billeder, du vælger"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> element}one{<xliff:g id="COUNT_1">^1</xliff:g> element}other{<xliff:g id="COUNT_1">^1</xliff:g> elementer}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Tilføj (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Downloads"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Favoritter"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Screenshots"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Levende billede"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> blev taget <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Videoen blev optaget <xliff:g id="TIME">%1$s</xliff:g> og varede <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Billede"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"Gif"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Levende billede"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Slå videolyden fra"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Slå videolyden til"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Afspil video"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Sæt video på pause"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Medier i skyen er nu tilgængelige fra <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"ikke valgt"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Vil du give <xliff:g id="APP_NAME_0">^1</xliff:g> tilladelse til at ændre denne lydfil?}one{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at ændre <xliff:g id="COUNT">^2</xliff:g> lydfil?}other{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at ændre <xliff:g id="COUNT">^2</xliff:g> lydfiler?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Ændrer lydfilen…}one{Ændrer <xliff:g id="COUNT">^1</xliff:g> lydfil…}other{Ændrer <xliff:g id="COUNT">^1</xliff:g> lydfiler…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Vil du give <xliff:g id="APP_NAME_0">^1</xliff:g> tilladelse til at ændre denne video?}one{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at ændre <xliff:g id="COUNT">^2</xliff:g> video?}other{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at ændre <xliff:g id="COUNT">^2</xliff:g> videoer?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Ændrer videoen…}one{Ændrer <xliff:g id="COUNT">^1</xliff:g> video…}other{Ændrer <xliff:g id="COUNT">^1</xliff:g> videoer…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Vil du give <xliff:g id="APP_NAME_0">^1</xliff:g> tilladelse til at ændre dette billede?}one{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at ændre <xliff:g id="COUNT">^2</xliff:g> billede?}other{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at ændre <xliff:g id="COUNT">^2</xliff:g> billeder?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Ændrer billedet…}one{Ændrer <xliff:g id="COUNT">^1</xliff:g> billede…}other{Ændrer <xliff:g id="COUNT">^1</xliff:g> billeder…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Vil du give <xliff:g id="APP_NAME_0">^1</xliff:g> tilladelse til at ændre dette element?}one{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at ændre <xliff:g id="COUNT">^2</xliff:g> element?}other{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at ændre <xliff:g id="COUNT">^2</xliff:g> elementer?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Ændrer elementet…}one{Ændrer <xliff:g id="COUNT">^1</xliff:g> element…}other{Ændrer <xliff:g id="COUNT">^1</xliff:g> elementer…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Vil du give <xliff:g id="APP_NAME_0">^1</xliff:g> tilladelse til at flytte denne lydfil til papirkurven?}one{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> lydfil til papirkurven?}other{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> lydfiler til papirkurven?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Flytter lydfilen til papirkurven…}one{Flytter <xliff:g id="COUNT">^1</xliff:g> lydfil til papirkurven…}other{Flytter <xliff:g id="COUNT">^1</xliff:g> lydfiler til papirkurven…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Vil du give <xliff:g id="APP_NAME_0">^1</xliff:g> tilladelse til at flytte denne video til papirkurven?}one{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> video til papirkurven?}other{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> videoer til papirkurven?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Flytter videoen til papirkurven…}one{Flytter <xliff:g id="COUNT">^1</xliff:g> video til papirkurven…}other{Flytter <xliff:g id="COUNT">^1</xliff:g> videoer til papirkurven…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Vil du give <xliff:g id="APP_NAME_0">^1</xliff:g> tilladelse til at flytte dette billede til papirkurven?}one{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> billede til papirkurven?}other{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> billeder til papirkurven?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Flytter billedet til papirkurven…}one{Flytter <xliff:g id="COUNT">^1</xliff:g> billede til papirkurven…}other{Flytter <xliff:g id="COUNT">^1</xliff:g> billeder til papirkurven…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Vil du give <xliff:g id="APP_NAME_0">^1</xliff:g> tilladelse til at flytte dette element til papirkurven?}one{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> element til papirkurven?}other{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> elementer til papirkurven?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Flytter elementet til papirkurven…}one{Flytter <xliff:g id="COUNT">^1</xliff:g> element til papirkurven…}other{Flytter <xliff:g id="COUNT">^1</xliff:g> elementer til papirkurven…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Vil du give <xliff:g id="APP_NAME_0">^1</xliff:g> tilladelse til at flytte denne lydfil ud af papirkurven?}one{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> lydfil ud af papirkurven?}other{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> lydfiler ud af papirkurven?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Flytter lydfilen ud af papirkurven…}one{Flytter <xliff:g id="COUNT">^1</xliff:g> lydfil ud af papirkurven…}other{Flytter <xliff:g id="COUNT">^1</xliff:g> lydfiler ud af papirkurven…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Vil du give <xliff:g id="APP_NAME_0">^1</xliff:g> tilladelse til at flytte denne video ud af papirkurven?}one{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> video ud af papirkurven?}other{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> videoer ud af papirkurven?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Flytter videoen ud af papirkurven…}one{Flytter <xliff:g id="COUNT">^1</xliff:g> video ud af papirkurven…}other{Flytter <xliff:g id="COUNT">^1</xliff:g> videoer ud af papirkurven…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Vil du give <xliff:g id="APP_NAME_0">^1</xliff:g> tilladelse til at flytte dette billede ud af papirkurven?}one{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> billede ud af papirkurven?}other{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> billeder ud af papirkurven?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Flytter billedet ud af papirkurven…}one{Flytter <xliff:g id="COUNT">^1</xliff:g> billede ud af papirkurven…}other{Flytter <xliff:g id="COUNT">^1</xliff:g> billeder ud af papirkurven…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Vil du give <xliff:g id="APP_NAME_0">^1</xliff:g> tilladelse til at flytte dette element ud af papirkurven?}one{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> element ud af papirkurven?}other{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at flytte <xliff:g id="COUNT">^2</xliff:g> elementer ud af papirkurven?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Flytter elementet ud af papirkurven…}one{Flytter <xliff:g id="COUNT">^1</xliff:g> element ud af papirkurven…}other{Flytter <xliff:g id="COUNT">^1</xliff:g> elementer ud af papirkurven…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Vil du give <xliff:g id="APP_NAME_0">^1</xliff:g> tilladelse til at slette denne lydfil?}one{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at slette <xliff:g id="COUNT">^2</xliff:g> lydfil?}other{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at slette <xliff:g id="COUNT">^2</xliff:g> lydfiler?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Sletter lydfilen…}one{Sletter <xliff:g id="COUNT">^1</xliff:g> lydfil…}other{Sletter <xliff:g id="COUNT">^1</xliff:g> lydfiler…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Vil du give <xliff:g id="APP_NAME_0">^1</xliff:g> tilladelse til at slette denne video?}one{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at slette <xliff:g id="COUNT">^2</xliff:g> video?}other{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at slette <xliff:g id="COUNT">^2</xliff:g> videoer?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Sletter videoen…}one{Sletter <xliff:g id="COUNT">^1</xliff:g> video…}other{Sletter <xliff:g id="COUNT">^1</xliff:g> videoer…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Vil du give <xliff:g id="APP_NAME_0">^1</xliff:g> tilladelse til at slette dette billede?}one{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at slette <xliff:g id="COUNT">^2</xliff:g> billede?}other{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at slette <xliff:g id="COUNT">^2</xliff:g> billeder?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Sletter billedet…}one{Sletter <xliff:g id="COUNT">^1</xliff:g> billede…}other{Sletter <xliff:g id="COUNT">^1</xliff:g> billeder…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Vil du give <xliff:g id="APP_NAME_0">^1</xliff:g> tilladelse til at slette dette element?}one{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at slette <xliff:g id="COUNT">^2</xliff:g> element?}other{Vil du give <xliff:g id="APP_NAME_1">^1</xliff:g> tilladelse til at slette <xliff:g id="COUNT">^2</xliff:g> elementer?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Sletter elementet…}one{Sletter <xliff:g id="COUNT">^1</xliff:g> element…}other{Sletter <xliff:g id="COUNT">^1</xliff:g> elementer…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> kan ikke behandle mediefiler"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Mediebehandlingen er annulleret"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Mediebehandlingsfejl"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 6a0001a..d5b49c0 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Foto-Picker"</string>
<string name="artist_label" msgid="8105600993099120273">"Interpret"</string>
<string name="unknown" msgid="2059049215682829375">"Unbekannt"</string>
<string name="root_images" msgid="5861633549189045666">"Bilder"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Weiter"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Zulassen"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Ablehnen"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+ <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+ <xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other"><xliff:g id="COUNT_1">^1</xliff:g> weitere Elemente</item>
- <item quantity="one"><xliff:g id="COUNT_0">^1</xliff:g> weiteres Element</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+ <xliff:g id="COUNT_0">^1</xliff:g>}other{+ <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> weiteres Element}other{<xliff:g id="COUNT_1">^1</xliff:g> weitere Elemente}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Temporäre App-Dateien löschen"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> möchte einige temporäre Dateien löschen. Dadurch können Akkuverbrauch und mobile Datennutzung steigen."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Temporäre App-Dateien werden gelöscht…"</string>
<string name="clear" msgid="5524638938415865915">"Löschen"</string>
<string name="allow" msgid="8885707816848569619">"Zulassen"</string>
<string name="deny" msgid="6040983710442068936">"Ablehnen"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Audiodateien ändern?</item>
- <item quantity="one">Darf <xliff:g id="APP_NAME_0">^1</xliff:g> diese Audiodatei ändern?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> Audiodateien werden geändert…</item>
- <item quantity="one">Audiodatei wird geändert…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Videos ändern?</item>
- <item quantity="one">Darf <xliff:g id="APP_NAME_0">^1</xliff:g> dieses Video ändern?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> Videos werden geändert…</item>
- <item quantity="one">Video wird geändert…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Fotos ändern?</item>
- <item quantity="one">Darf <xliff:g id="APP_NAME_0">^1</xliff:g> dieses Foto ändern?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> Fotos werden geändert…</item>
- <item quantity="one">Foto wird geändert…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Elemente ändern?</item>
- <item quantity="one">Darf <xliff:g id="APP_NAME_0">^1</xliff:g> dieses Element ändern?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> Elemente werden geändert…</item>
- <item quantity="one">Element wird geändert…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Audiodateien in den Papierkorb verschieben?</item>
- <item quantity="one">Darf <xliff:g id="APP_NAME_0">^1</xliff:g> diese Audiodatei in den Papierkorb verschieben?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> Audiodateien werden in den Papierkorb verschoben…</item>
- <item quantity="one">Audiodatei wird in den Papierkorb verschoben…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Videos in den Papierkorb verschieben?</item>
- <item quantity="one">Darf <xliff:g id="APP_NAME_0">^1</xliff:g> dieses Video in den Papierkorb verschieben?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> Videos werden in den Papierkorb verschoben…</item>
- <item quantity="one">Video wird in den Papierkorb verschoben…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Fotos in den Papierkorb verschieben?</item>
- <item quantity="one">Darf <xliff:g id="APP_NAME_0">^1</xliff:g> dieses Foto in den Papierkorb verschieben?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> Fotos werden in den Papierkorb verschoben…</item>
- <item quantity="one">Foto wird in den Papierkorb verschoben…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Elemente in den Papierkorb verschieben?</item>
- <item quantity="one">Darf <xliff:g id="APP_NAME_0">^1</xliff:g> dieses Element in den Papierkorb verschieben?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> Elemente werden in den Papierkorb verschoben…</item>
- <item quantity="one">Element wird in den Papierkorb verschoben…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Audiodateien aus dem Papierkorb verschieben?</item>
- <item quantity="one">Darf <xliff:g id="APP_NAME_0">^1</xliff:g> diese Audiodatei aus dem Papierkorb verschieben?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> Audiodateien werden aus dem Papierkorb verschoben…</item>
- <item quantity="one">Audiodatei wird aus dem Papierkorb verschoben…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Videos aus dem Papierkorb verschieben?</item>
- <item quantity="one">Darf <xliff:g id="APP_NAME_0">^1</xliff:g> dieses Video aus dem Papierkorb verschieben?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> Videos werden aus dem Papierkorb verschoben…</item>
- <item quantity="one">Video wird aus dem Papierkorb verschoben…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Fotos aus dem Papierkorb verschieben?</item>
- <item quantity="one">Darf <xliff:g id="APP_NAME_0">^1</xliff:g> dieses Foto aus dem Papierkorb verschieben?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> Fotos werden aus dem Papierkorb verschoben…</item>
- <item quantity="one">Foto wird aus dem Papierkorb verschoben…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Elemente aus dem Papierkorb wiederherstellen?</item>
- <item quantity="one">Darf <xliff:g id="APP_NAME_0">^1</xliff:g> dieses Element aus dem Papierkorb verschieben?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> Elemente werden aus dem Papierkorb verschoben…</item>
- <item quantity="one">Element wird aus dem Papierkorb verschoben…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Audiodateien löschen?</item>
- <item quantity="one">Darf <xliff:g id="APP_NAME_0">^1</xliff:g> diese Audiodatei löschen?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> Audiodateien werden gelöscht…</item>
- <item quantity="one">Audiodatei wird gelöscht…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Videos löschen?</item>
- <item quantity="one">Darf <xliff:g id="APP_NAME_0">^1</xliff:g> dieses Video löschen?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> Videos werden gelöscht…</item>
- <item quantity="one">Video wird gelöscht…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Fotos löschen?</item>
- <item quantity="one">Darf <xliff:g id="APP_NAME_0">^1</xliff:g> dieses Foto löschen?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> Fotos werden gelöscht…</item>
- <item quantity="one">Foto wird gelöscht…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Elemente löschen?</item>
- <item quantity="one">Darf <xliff:g id="APP_NAME_0">^1</xliff:g> dieses Element löschen?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> Elemente werden gelöscht…</item>
- <item quantity="one">Element wird gelöscht…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Hinzufügen"</string>
+ <string name="deselect" msgid="4297825044827769490">"Auswahl aufheben"</string>
+ <string name="deselected" msgid="8488133193326208475">"Auswahl aufgehoben"</string>
+ <string name="select" msgid="2704765470563027689">"Auswählen"</string>
+ <string name="selected" msgid="9151797369975828124">"Ausgewählt"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Du kannst maximal <xliff:g id="COUNT_0">^1</xliff:g> Element auswählen}other{Du kannst maximal <xliff:g id="COUNT_1">^1</xliff:g> Elemente auswählen}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Neueste"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Keine Fotos oder Videos"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Keine Alben"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Auswahl ansehen"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Fotos"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Alben"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Vorschau"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Zum Arbeitsprofil wechseln"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Zum privaten Profil wechseln"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Vom Administrator blockiert"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Der Zugriff auf berufliche Daten von einer privaten App ist nicht zulässig"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Der Zugriff auf private Daten von einer geschäftlichen App ist nicht zulässig"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Geschäftliche Apps sind pausiert"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Wenn du geschäftliche Fotos aufrufen möchtest, aktiviere zuerst deine geschäftlichen Apps und versuche es dann noch einmal"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Diese App kann nur auf die Fotos zugreifen, die du auswählst"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> Element}other{<xliff:g id="COUNT_1">^1</xliff:g> Elemente}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Hinzufügen (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Downloads"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Favoriten"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Screenshots"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Foto mit Bewegtbild"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> wurde aufgenommen am <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Video vom <xliff:g id="TIME">%1$s</xliff:g>, Dauer <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Foto"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Foto mit Bewegtbild"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Video stummschalten"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Stummschaltung des Videos aufheben"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Video ansehen"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Video anhalten"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Cloud-Medien sind jetzt verfügbar über <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"nicht ausgewählt"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Darf <xliff:g id="APP_NAME_0">^1</xliff:g> diese Audiodatei ändern?}other{Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Audiodateien ändern?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Audiodatei wird geändert…}other{<xliff:g id="COUNT">^1</xliff:g> Audiodateien werden geändert…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Darf <xliff:g id="APP_NAME_0">^1</xliff:g> dieses Video ändern?}other{Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Videos ändern?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Video wird geändert…}other{<xliff:g id="COUNT">^1</xliff:g> Videos werden geändert…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Darf <xliff:g id="APP_NAME_0">^1</xliff:g> dieses Foto ändern?}other{Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Fotos ändern?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Foto wird geändert…}other{<xliff:g id="COUNT">^1</xliff:g> Fotos werden geändert…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Darf <xliff:g id="APP_NAME_0">^1</xliff:g> dieses Element ändern?}other{Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Elemente ändern?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Element wird geändert…}other{<xliff:g id="COUNT">^1</xliff:g> Elemente werden geändert…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Darf <xliff:g id="APP_NAME_0">^1</xliff:g> diese Audiodatei in den Papierkorb verschieben?}other{Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Audiodateien in den Papierkorb verschieben?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Audiodatei wird in den Papierkorb verschoben…}other{<xliff:g id="COUNT">^1</xliff:g> Audiodateien werden in den Papierkorb verschoben…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Darf <xliff:g id="APP_NAME_0">^1</xliff:g> dieses Video in den Papierkorb verschieben?}other{Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Videos in den Papierkorb verschieben?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Video wird in den Papierkorb verschoben…}other{<xliff:g id="COUNT">^1</xliff:g> Videos werden in den Papierkorb verschoben…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Darf <xliff:g id="APP_NAME_0">^1</xliff:g> dieses Foto in den Papierkorb verschieben?}other{Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Fotos in den Papierkorb verschieben?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Foto wird in den Papierkorb verschoben…}other{<xliff:g id="COUNT">^1</xliff:g> Fotos werden in den Papierkorb verschoben…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Darf <xliff:g id="APP_NAME_0">^1</xliff:g> dieses Element in den Papierkorb verschieben?}other{Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Elemente in den Papierkorb verschieben?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Element wird in den Papierkorb verschoben…}other{<xliff:g id="COUNT">^1</xliff:g> Elemente werden in den Papierkorb verschoben…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Darf <xliff:g id="APP_NAME_0">^1</xliff:g> diese Audiodatei aus dem Papierkorb verschieben?}other{Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Audiodateien aus dem Papierkorb verschieben?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Audiodatei wird aus dem Papierkorb verschoben…}other{<xliff:g id="COUNT">^1</xliff:g> Audiodateien werden aus dem Papierkorb verschoben…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Darf <xliff:g id="APP_NAME_0">^1</xliff:g> dieses Video aus dem Papierkorb verschieben?}other{Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Videos aus dem Papierkorb verschieben?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Video wird aus dem Papierkorb verschoben…}other{<xliff:g id="COUNT">^1</xliff:g> Videos werden aus dem Papierkorb verschoben…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Darf <xliff:g id="APP_NAME_0">^1</xliff:g> dieses Foto aus dem Papierkorb verschieben?}other{Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Fotos aus dem Papierkorb verschieben?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Foto wird aus dem Papierkorb verschoben…}other{<xliff:g id="COUNT">^1</xliff:g> Fotos werden aus dem Papierkorb verschoben…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Darf <xliff:g id="APP_NAME_0">^1</xliff:g> dieses Element aus dem Papierkorb verschieben?}other{Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Elemente aus dem Papierkorb verschieben?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Element wird aus dem Papierkorb verschoben…}other{<xliff:g id="COUNT">^1</xliff:g> Elemente werden aus dem Papierkorb verschoben…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Darf <xliff:g id="APP_NAME_0">^1</xliff:g> diese Audiodatei löschen?}other{Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Audiodateien löschen?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Audiodatei wird gelöscht…}other{<xliff:g id="COUNT">^1</xliff:g> Audiodateien werden gelöscht…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Darf <xliff:g id="APP_NAME_0">^1</xliff:g> dieses Video löschen?}other{Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Videos löschen?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Video wird gelöscht…}other{<xliff:g id="COUNT">^1</xliff:g> Videos werden gelöscht…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Darf <xliff:g id="APP_NAME_0">^1</xliff:g> dieses Foto löschen?}other{Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Fotos löschen?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Foto wird gelöscht…}other{<xliff:g id="COUNT">^1</xliff:g> Fotos werden gelöscht…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Darf <xliff:g id="APP_NAME_0">^1</xliff:g> dieses Element löschen?}other{Darf <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> Elemente löschen?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Element wird gelöscht…}other{<xliff:g id="COUNT">^1</xliff:g> Elemente werden gelöscht…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"Die App „<xliff:g id="APP_NAME">%s</xliff:g>“ kann Mediendateien nicht verarbeiten"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Medienverarbeitung abgebrochen"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Fehler bei Medienverarbeitung"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 634a9ee..2a77cd0 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Μέσα"</string>
<string name="storage_description" msgid="4081716890357580107">"Τοπικός χώρος αποθήκευσης"</string>
<string name="app_label" msgid="9035307001052716210">"Αποθηκευτικός χώρος μέσων"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"Εργαλείο επιλογής φωτογραφιών"</string>
<string name="artist_label" msgid="8105600993099120273">"Καλλιτέχνης"</string>
<string name="unknown" msgid="2059049215682829375">"Άγνωστο"</string>
<string name="root_images" msgid="5861633549189045666">"Εικόνες"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Συνέχεια"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Να επιτρέπεται"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Να μην επιτρέπεται"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Συν <xliff:g id="COUNT_1">^1</xliff:g> επιπλέον στοιχεία</item>
- <item quantity="one">Συν <xliff:g id="COUNT_0">^1</xliff:g> επιπλέον στοιχείο</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Συν <xliff:g id="COUNT_0">^1</xliff:g> επιπλέον στοιχείο}other{Συν <xliff:g id="COUNT_1">^1</xliff:g> επιπλέον στοιχεία}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Διαγραφή προσωρινών αρχείων εφαρμογών"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"Η εφαρμογή <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> θέλει να διαγράψει ορισμένα προσωρινά αρχεία. Αυτό μπορεί να οδηγήσει σε αυξημένη χρήση μπαταριών ή δεδομένων κινητής τηλεφωνίας."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Διαγραφή προσωρινών αρχείων εφαρμογών…"</string>
<string name="clear" msgid="5524638938415865915">"Διαγραφή"</string>
<string name="allow" msgid="8885707816848569619">"Να επιτρέπεται"</string>
<string name="deny" msgid="6040983710442068936">"Απόρριψη"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η τροποποίηση <xliff:g id="COUNT">^2</xliff:g> αρχείων ήχου;</item>
- <item quantity="one">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η τροποποίηση αυτού του αρχείου ήχου;</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">Τροποποίηση <xliff:g id="COUNT">^1</xliff:g> αρχείων ήχου…</item>
- <item quantity="one">Τροποποίηση αρχείου ήχου…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η τροποποίηση <xliff:g id="COUNT">^2</xliff:g> βίντεο;</item>
- <item quantity="one">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η τροποποίηση αυτού του βίντεο;</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">Τροποποίηση <xliff:g id="COUNT">^1</xliff:g> βίντεο…</item>
- <item quantity="one">Τροποποίηση βίντεο…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η τροποποίηση <xliff:g id="COUNT">^2</xliff:g> φωτογραφιών;</item>
- <item quantity="one">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η τροποποίηση αυτής της φωτογραφίας;</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">Τροποποίηση <xliff:g id="COUNT">^1</xliff:g> φωτογραφιών…</item>
- <item quantity="one">Τροποποίηση φωτογραφίας…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η τροποποίηση <xliff:g id="COUNT">^2</xliff:g> στοιχείων;</item>
- <item quantity="one">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η τροποποίηση αυτού του στοιχείου;</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">Τροποποίηση <xliff:g id="COUNT">^1</xliff:g> στοιχείων…</item>
- <item quantity="one">Τροποποίηση στοιχείου…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η μετακίνηση <xliff:g id="COUNT">^2</xliff:g> αρχείων ήχου στον κάδο;</item>
- <item quantity="one">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η μετακίνηση αυτού του αρχείου ήχου στον κάδο;</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">Μετακίνηση <xliff:g id="COUNT">^1</xliff:g> αρχείων ήχου στον κάδο…</item>
- <item quantity="one">Μετακίνηση αρχείου ήχου στον κάδο…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η μετακίνηση <xliff:g id="COUNT">^2</xliff:g> βίντεο στον κάδο;</item>
- <item quantity="one">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η μετακίνηση αυτού του βίντεο στον κάδο;</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">Μετακίνηση <xliff:g id="COUNT">^1</xliff:g> βίντεο στον κάδο…</item>
- <item quantity="one">Μετακίνηση βίντεο στον κάδο…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η μετακίνηση <xliff:g id="COUNT">^2</xliff:g> φωτογραφιών στον κάδο;</item>
- <item quantity="one">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η μετακίνηση αυτής της φωτογραφίας στον κάδο;</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">Μετακίνηση <xliff:g id="COUNT">^1</xliff:g> φωτογραφιών στον κάδο…</item>
- <item quantity="one">Μετακίνηση φωτογραφίας στον κάδο…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η μετακίνηση <xliff:g id="COUNT">^2</xliff:g> των στοιχείων στον κάδο;</item>
- <item quantity="one">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η μετακίνηση αυτού του στοιχείου στον κάδο;</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">Μετακίνηση <xliff:g id="COUNT">^1</xliff:g> στοιχείων στον κάδο…</item>
- <item quantity="one">Μετακίνηση στοιχείου στον κάδο…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η μετακίνηση <xliff:g id="COUNT">^2</xliff:g> αρχείων ήχου από τον κάδο;</item>
- <item quantity="one">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η μετακίνηση αυτού του αρχείου ήχου από τον κάδο;</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">Μετακίνηση <xliff:g id="COUNT">^1</xliff:g> αρχείων ήχου από τον κάδο…</item>
- <item quantity="one">Μετακίνηση αρχείου ήχου από τον κάδο…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η μετακίνηση <xliff:g id="COUNT">^2</xliff:g> βίντεο από τον κάδο;</item>
- <item quantity="one">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η μετακίνηση αυτού του βίντεο από τον κάδο;</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">Μετακίνηση <xliff:g id="COUNT">^1</xliff:g> βίντεο από τον κάδο…</item>
- <item quantity="one">Μετακίνηση βίντεο από τον κάδο…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η μετακίνηση <xliff:g id="COUNT">^2</xliff:g> φωτογραφιών από τον κάδο;</item>
- <item quantity="one">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η μετακίνηση αυτής της φωτογραφίας από τον κάδο;</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">Μετακίνηση <xliff:g id="COUNT">^1</xliff:g> φωτογραφιών από τον κάδο…</item>
- <item quantity="one">Μετακίνηση φωτογραφίας από τον κάδο…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η μετακίνηση <xliff:g id="COUNT">^2</xliff:g> στοιχείων από τον κάδο;</item>
- <item quantity="one">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η μετακίνηση αυτού του στοιχείου από τον κάδο;</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">Μετακίνηση <xliff:g id="COUNT">^1</xliff:g> στοιχείων από τον κάδο…</item>
- <item quantity="one">Μετακίνηση στοιχείου από τον κάδο…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η διαγραφή <xliff:g id="COUNT">^2</xliff:g> αρχείων ήχου;</item>
- <item quantity="one">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η διαγραφή αυτού του αρχείου ήχου;</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">Διαγραφή <xliff:g id="COUNT">^1</xliff:g> αρχείων ήχου…</item>
- <item quantity="one">Διαγραφή αρχείου ήχου…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η διαγραφή <xliff:g id="COUNT">^2</xliff:g> βίντεο;</item>
- <item quantity="one">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η διαγραφή αυτού του βίντεο;</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">Διαγραφή <xliff:g id="COUNT">^1</xliff:g> βίντεο…</item>
- <item quantity="one">Διαγραφή βίντεο…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η διαγραφή <xliff:g id="COUNT">^2</xliff:g> φωτογραφιών;</item>
- <item quantity="one">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η διαγραφή αυτής της φωτογραφίας:</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">Διαγραφή <xliff:g id="COUNT">^1</xliff:g> φωτογραφιών…</item>
- <item quantity="one">Διαγραφή φωτογραφίας…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η διαγραφή <xliff:g id="COUNT">^2</xliff:g> στοιχείων;</item>
- <item quantity="one">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η διαγραφή αυτού του στοιχείου;</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">Διαγραφή <xliff:g id="COUNT">^1</xliff:g> στοιχείων…</item>
- <item quantity="one">Διαγραφή στοιχείου…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Προσθήκη"</string>
+ <string name="deselect" msgid="4297825044827769490">"Αποεπιλογή"</string>
+ <string name="deselected" msgid="8488133193326208475">"Αποεπιλέχθηκε"</string>
+ <string name="select" msgid="2704765470563027689">"Επιλογή"</string>
+ <string name="selected" msgid="9151797369975828124">"Επιλέχθηκε"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Επιλέξτε έως <xliff:g id="COUNT_0">^1</xliff:g> στοιχείο}other{Επιλέξτε έως <xliff:g id="COUNT_1">^1</xliff:g> στοιχεία}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Πρόσφατα"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Δεν υπάρχουν φωτογραφίες ή βίντεο"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Δεν υπάρχουν λευκώματα"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Προβολή επιλεγμένων"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Φωτογραφίες"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Άλμπουμ"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Προεπισκόπηση"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Μετάβαση σε προφίλ εργασίας"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Μετάβαση σε προσωπικό προφίλ"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Αποκλείστηκε από τον διαχειριστή σας"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Δεν επιτρέπεται η πρόσβαση στα δεδομένα εργασίας από μια προσωπική εφαρμογή."</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Δεν επιτρέπεται η πρόσβαση στα προσωπικά δεδομένα από μια εφαρμογή εργασιών."</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Οι εφαρμογές εργασίας τέθηκαν σε παύση"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Για να ανοίξετε φωτογραφίες εργασίας, ενεργοποιήστε τις εφαρμογές εργασίας και έπειτα δοκιμάστε ξανά."</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Αυτή η εφαρμογή μπορεί να αποκτήσει πρόσβαση μόνο στις φωτογρ. που επιλέγετε."</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> στοιχείο}other{<xliff:g id="COUNT_1">^1</xliff:g> στοιχεία}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Προσθήκη (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Κάμερα"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Λήψεις"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Αγαπημένα"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Στιγμιότυπα οθόνης"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Κινούμενη φωτογραφία"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> λήφθηκε στις <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Βίντεο που ελήφθη στις <xliff:g id="TIME">%1$s</xliff:g> με διάρκεια <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Φωτογραφία"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Κινούμενη φωτογραφία"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Σίγαση βίντεο"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Κατάργηση σίγασης βίντεο"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Αναπαραγωγή βίντεο"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Παύση βίντεο"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Τα μέσα cloud είναι πλέον διαθέσιμα από την εφαρμογή <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"μη επιλεγμένο"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η τροποποίηση αυτού του αρχείου ήχου;}other{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η τροποποίηση <xliff:g id="COUNT">^2</xliff:g> αρχείων ήχου;}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Τροποποίηση αρχείου ήχου…}other{Τροποποίηση <xliff:g id="COUNT">^1</xliff:g> αρχείων ήχου…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η τροποποίηση αυτού του βίντεο;}other{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η τροποποίηση <xliff:g id="COUNT">^2</xliff:g> βίντεο;}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Τροποποίηση βίντεο…}other{Τροποποίηση <xliff:g id="COUNT">^1</xliff:g> βίντεο…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η τροποποίηση αυτής της φωτογραφίας;}other{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η τροποποίηση <xliff:g id="COUNT">^2</xliff:g> φωτογραφιών;}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Τροποποίηση φωτογραφίας…}other{Τροποποίηση <xliff:g id="COUNT">^1</xliff:g> φωτογραφιών…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η τροποποίηση αυτού του στοιχείου;}other{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η τροποποίηση <xliff:g id="COUNT">^2</xliff:g> στοιχείων;}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Τροποποίηση στοιχείου…}other{Τροποποίηση <xliff:g id="COUNT">^1</xliff:g> στοιχείων…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η μετακίνηση αυτού του αρχείου ήχου στον κάδο;}other{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η μετακίνηση <xliff:g id="COUNT">^2</xliff:g> αρχείων ήχου στον κάδο;}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Μετακίνηση αρχείου ήχου στον κάδο…}other{Μετακίνηση <xliff:g id="COUNT">^1</xliff:g> αρχείων ήχου στον κάδο…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η μετακίνηση αυτού του βίντεο στον κάδο;}other{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η μετακίνηση <xliff:g id="COUNT">^2</xliff:g> βίντεο στον κάδο;}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Μετακίνηση βίντεο στον κάδο…}other{Μετακίνηση <xliff:g id="COUNT">^1</xliff:g> βίντεο στον κάδο…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η μετακίνηση αυτής της φωτογραφίας στον κάδο;}other{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η μετακίνηση <xliff:g id="COUNT">^2</xliff:g> φωτογραφιών στον κάδο;}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Μετακίνηση φωτογραφίας στον κάδο…}other{Μετακίνηση <xliff:g id="COUNT">^1</xliff:g> φωτογραφιών στον κάδο…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η μετακίνηση αυτού του στοιχείου στον κάδο;}other{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η μετακίνηση <xliff:g id="COUNT">^2</xliff:g> των στοιχείων στον κάδο;}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Μετακίνηση στοιχείου στον κάδο…}other{Μετακίνηση <xliff:g id="COUNT">^1</xliff:g> στοιχείων στον κάδο…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η μετακίνηση αυτού του αρχείου ήχου από τον κάδο;}other{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η μετακίνηση <xliff:g id="COUNT">^2</xliff:g> αρχείων ήχου από τον κάδο;}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Μετακίνηση αρχείου ήχου από τον κάδο…}other{Μετακίνηση <xliff:g id="COUNT">^1</xliff:g> αρχείων ήχου από τον κάδο…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η μετακίνηση αυτού του βίντεο από τον κάδο;}other{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η μετακίνηση <xliff:g id="COUNT">^2</xliff:g> βίντεο από τον κάδο;}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Μετακίνηση βίντεο από τον κάδο…}other{Μετακίνηση <xliff:g id="COUNT">^1</xliff:g> βίντεο από τον κάδο…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η μετακίνηση αυτής της φωτογραφίας από τον κάδο;}other{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η μετακίνηση <xliff:g id="COUNT">^2</xliff:g> φωτογραφιών από τον κάδο;}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Μετακίνηση φωτογραφίας από τον κάδο…}other{Μετακίνηση <xliff:g id="COUNT">^1</xliff:g> φωτογραφιών από τον κάδο…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η μετακίνηση αυτού του στοιχείου από τον κάδο;}other{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η μετακίνηση <xliff:g id="COUNT">^2</xliff:g> στοιχείων από τον κάδο;}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Μετακίνηση στοιχείου από τον κάδο…}other{Μετακίνηση <xliff:g id="COUNT">^1</xliff:g> στοιχείων από τον κάδο…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η διαγραφή αυτού του αρχείου ήχου;}other{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η διαγραφή <xliff:g id="COUNT">^2</xliff:g> αρχείων ήχου;}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Διαγραφή αρχείου ήχου…}other{Διαγραφή <xliff:g id="COUNT">^1</xliff:g> αρχείων ήχου…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η διαγραφή αυτού του βίντεο;}other{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η διαγραφή <xliff:g id="COUNT">^2</xliff:g> βίντεο;}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Διαγραφή βίντεο…}other{Διαγραφή <xliff:g id="COUNT">^1</xliff:g> βίντεο…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η διαγραφή αυτής της φωτογραφίας;}other{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η διαγραφή <xliff:g id="COUNT">^2</xliff:g> φωτογραφιών;}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Διαγραφή φωτογραφίας…}other{Διαγραφή <xliff:g id="COUNT">^1</xliff:g> φωτογραφιών…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_0">^1</xliff:g> η διαγραφή αυτού του στοιχείου;}other{Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η διαγραφή <xliff:g id="COUNT">^2</xliff:g> στοιχείων;}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Διαγραφή στοιχείου…}other{Διαγραφή <xliff:g id="COUNT">^1</xliff:g> στοιχείων…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"Η εφαρμογή <xliff:g id="APP_NAME">%s</xliff:g> δεν έχει δυνατότητα επεξεργασίας αρχείων μέσων"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Η επεξεργασία μέσων ακυρώθηκε"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Σφάλμα επεξεργασίας μέσων"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 2f1bda0..06d385c 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Media"</string>
<string name="storage_description" msgid="4081716890357580107">"Local storage"</string>
<string name="app_label" msgid="9035307001052716210">"Media Storage"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"Photo picker"</string>
<string name="artist_label" msgid="8105600993099120273">"Artist"</string>
<string name="unknown" msgid="2059049215682829375">"Unknown"</string>
<string name="root_images" msgid="5861633549189045666">"Images"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Continue"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Allow"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Deny"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Plus <xliff:g id="COUNT_1">^1</xliff:g> additional items</item>
- <item quantity="one">Plus <xliff:g id="COUNT_0">^1</xliff:g> additional item</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Plus <xliff:g id="COUNT_0">^1</xliff:g> additional item}other{Plus <xliff:g id="COUNT_1">^1</xliff:g> additional items}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Clear temporary app files"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> would like to clear some temporary files. This may result in an increased usage of battery or mobile data."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Clearing temporary app files…"</string>
<string name="clear" msgid="5524638938415865915">"Clear"</string>
<string name="allow" msgid="8885707816848569619">"Allow"</string>
<string name="deny" msgid="6040983710442068936">"Deny"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> audio files?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this audio file?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">Modifying <xliff:g id="COUNT">^1</xliff:g> audio files…</item>
- <item quantity="one">Modifying audio file…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> videos?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this video?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">Modifying <xliff:g id="COUNT">^1</xliff:g> videos…</item>
- <item quantity="one">Modifying video…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> photos?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this photo?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">Modifying <xliff:g id="COUNT">^1</xliff:g> photos…</item>
- <item quantity="one">Modifying photo…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> items?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this item?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">Modifying <xliff:g id="COUNT">^1</xliff:g> items…</item>
- <item quantity="one">Modifying item…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files to bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this audio file to bin?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> audio files to bin…</item>
- <item quantity="one">Moving audio file to bin…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos to bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this video to bin?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> videos to bin…</item>
- <item quantity="one">Moving video to bin…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos to bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this photo to bin?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> photos to bin…</item>
- <item quantity="one">Moving photo to bin…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items to bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this item to bin?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> items to bin…</item>
- <item quantity="one">Moving item to bin…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files out of bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this audio file out of bin?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> audio files out of bin…</item>
- <item quantity="one">Moving audio file out of bin…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos out of bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this video out of bin?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> videos out of bin…</item>
- <item quantity="one">Moving video out of bin…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos out of bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this photo out of bin?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> photos out of bin…</item>
- <item quantity="one">Moving photo out of bin…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items out of bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this item out of bin?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> items out of bin…</item>
- <item quantity="one">Moving item out of bin…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> audio files?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this audio file?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">Deleting <xliff:g id="COUNT">^1</xliff:g> audio files…</item>
- <item quantity="one">Deleting audio file…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> videos?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this video?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">Deleting <xliff:g id="COUNT">^1</xliff:g> videos…</item>
- <item quantity="one">Deleting video…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> photos?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this photo?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">Deleting <xliff:g id="COUNT">^1</xliff:g> photos…</item>
- <item quantity="one">Deleting photo…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> items?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this item?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">Deleting <xliff:g id="COUNT">^1</xliff:g> items…</item>
- <item quantity="one">Deleting item…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Add"</string>
+ <string name="deselect" msgid="4297825044827769490">"Deselect"</string>
+ <string name="deselected" msgid="8488133193326208475">"Deselected"</string>
+ <string name="select" msgid="2704765470563027689">"Select"</string>
+ <string name="selected" msgid="9151797369975828124">"Selected"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Select up to <xliff:g id="COUNT_0">^1</xliff:g> item}other{Select up to <xliff:g id="COUNT_1">^1</xliff:g> items}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Recent"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"No photos or videos"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"No albums"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"View selected"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Photos"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albums"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Preview"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Switch to work"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Switch to personal"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Blocked by your admin"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Accessing work data from a personal app is not permitted"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Accessing personal data from a work app is not permitted"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Work apps are paused"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"To open work photos, turn on your work apps and then try again"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"This app can only access the photos that you select"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}other{<xliff:g id="COUNT_1">^1</xliff:g> items}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Add (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Camera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Downloads"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Favourites"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Screenshots"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Motion photo"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> taken on <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Video taken on <xliff:g id="TIME">%1$s</xliff:g> with duration <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Photo"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Motion photo"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Mute video"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Unmute video"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Play video"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Pause video"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Cloud media now available from <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"not selected"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this audio file?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> audio files?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Modifying audio file…}other{Modifying <xliff:g id="COUNT">^1</xliff:g> audio files…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this video?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> videos?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Modifying video…}other{Modifying <xliff:g id="COUNT">^1</xliff:g> videos…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this photo?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> photos?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Modifying photo…}other{Modifying <xliff:g id="COUNT">^1</xliff:g> photos…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this item?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> items?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Modifying item…}other{Modifying <xliff:g id="COUNT">^1</xliff:g> items…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this audio file to bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files to bin?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Moving audio file to bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> audio files to bin…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this video to bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos to bin?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Moving video to bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> videos to bin…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this photo to bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos to bin?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Moving photo to bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> photos to bin…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this item to bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items to bin?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Moving item to bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> items to bin…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this audio file out of bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files out of bin?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Moving audio file out of bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> audio files out of bin…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this video out of bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos out of bin?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Moving video out of bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> videos out of bin…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this photo out of bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos out of bin?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Moving photo out of bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> photos out of bin…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this item out of bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items out of bin?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Moving item out of bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> items out of bin…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this audio file?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> audio files?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Deleting audio file…}other{Deleting <xliff:g id="COUNT">^1</xliff:g> audio files…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this video?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> videos?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Deleting video…}other{Deleting <xliff:g id="COUNT">^1</xliff:g> videos…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this photo?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> photos?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Deleting photo…}other{Deleting <xliff:g id="COUNT">^1</xliff:g> photos…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this item?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> items?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Deleting item…}other{Deleting <xliff:g id="COUNT">^1</xliff:g> items…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> can\'t process media files"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Media processing cancelled"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Media processing error"</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index 2f1bda0..06d385c 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Media"</string>
<string name="storage_description" msgid="4081716890357580107">"Local storage"</string>
<string name="app_label" msgid="9035307001052716210">"Media Storage"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"Photo picker"</string>
<string name="artist_label" msgid="8105600993099120273">"Artist"</string>
<string name="unknown" msgid="2059049215682829375">"Unknown"</string>
<string name="root_images" msgid="5861633549189045666">"Images"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Continue"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Allow"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Deny"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Plus <xliff:g id="COUNT_1">^1</xliff:g> additional items</item>
- <item quantity="one">Plus <xliff:g id="COUNT_0">^1</xliff:g> additional item</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Plus <xliff:g id="COUNT_0">^1</xliff:g> additional item}other{Plus <xliff:g id="COUNT_1">^1</xliff:g> additional items}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Clear temporary app files"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> would like to clear some temporary files. This may result in an increased usage of battery or mobile data."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Clearing temporary app files…"</string>
<string name="clear" msgid="5524638938415865915">"Clear"</string>
<string name="allow" msgid="8885707816848569619">"Allow"</string>
<string name="deny" msgid="6040983710442068936">"Deny"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> audio files?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this audio file?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">Modifying <xliff:g id="COUNT">^1</xliff:g> audio files…</item>
- <item quantity="one">Modifying audio file…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> videos?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this video?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">Modifying <xliff:g id="COUNT">^1</xliff:g> videos…</item>
- <item quantity="one">Modifying video…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> photos?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this photo?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">Modifying <xliff:g id="COUNT">^1</xliff:g> photos…</item>
- <item quantity="one">Modifying photo…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> items?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this item?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">Modifying <xliff:g id="COUNT">^1</xliff:g> items…</item>
- <item quantity="one">Modifying item…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files to bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this audio file to bin?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> audio files to bin…</item>
- <item quantity="one">Moving audio file to bin…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos to bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this video to bin?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> videos to bin…</item>
- <item quantity="one">Moving video to bin…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos to bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this photo to bin?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> photos to bin…</item>
- <item quantity="one">Moving photo to bin…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items to bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this item to bin?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> items to bin…</item>
- <item quantity="one">Moving item to bin…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files out of bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this audio file out of bin?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> audio files out of bin…</item>
- <item quantity="one">Moving audio file out of bin…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos out of bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this video out of bin?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> videos out of bin…</item>
- <item quantity="one">Moving video out of bin…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos out of bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this photo out of bin?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> photos out of bin…</item>
- <item quantity="one">Moving photo out of bin…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items out of bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this item out of bin?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> items out of bin…</item>
- <item quantity="one">Moving item out of bin…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> audio files?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this audio file?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">Deleting <xliff:g id="COUNT">^1</xliff:g> audio files…</item>
- <item quantity="one">Deleting audio file…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> videos?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this video?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">Deleting <xliff:g id="COUNT">^1</xliff:g> videos…</item>
- <item quantity="one">Deleting video…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> photos?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this photo?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">Deleting <xliff:g id="COUNT">^1</xliff:g> photos…</item>
- <item quantity="one">Deleting photo…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> items?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this item?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">Deleting <xliff:g id="COUNT">^1</xliff:g> items…</item>
- <item quantity="one">Deleting item…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Add"</string>
+ <string name="deselect" msgid="4297825044827769490">"Deselect"</string>
+ <string name="deselected" msgid="8488133193326208475">"Deselected"</string>
+ <string name="select" msgid="2704765470563027689">"Select"</string>
+ <string name="selected" msgid="9151797369975828124">"Selected"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Select up to <xliff:g id="COUNT_0">^1</xliff:g> item}other{Select up to <xliff:g id="COUNT_1">^1</xliff:g> items}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Recent"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"No photos or videos"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"No albums"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"View selected"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Photos"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albums"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Preview"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Switch to work"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Switch to personal"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Blocked by your admin"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Accessing work data from a personal app is not permitted"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Accessing personal data from a work app is not permitted"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Work apps are paused"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"To open work photos, turn on your work apps and then try again"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"This app can only access the photos that you select"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}other{<xliff:g id="COUNT_1">^1</xliff:g> items}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Add (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Camera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Downloads"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Favourites"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Screenshots"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Motion photo"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> taken on <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Video taken on <xliff:g id="TIME">%1$s</xliff:g> with duration <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Photo"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Motion photo"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Mute video"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Unmute video"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Play video"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Pause video"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Cloud media now available from <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"not selected"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this audio file?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> audio files?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Modifying audio file…}other{Modifying <xliff:g id="COUNT">^1</xliff:g> audio files…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this video?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> videos?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Modifying video…}other{Modifying <xliff:g id="COUNT">^1</xliff:g> videos…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this photo?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> photos?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Modifying photo…}other{Modifying <xliff:g id="COUNT">^1</xliff:g> photos…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this item?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> items?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Modifying item…}other{Modifying <xliff:g id="COUNT">^1</xliff:g> items…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this audio file to bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files to bin?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Moving audio file to bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> audio files to bin…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this video to bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos to bin?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Moving video to bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> videos to bin…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this photo to bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos to bin?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Moving photo to bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> photos to bin…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this item to bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items to bin?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Moving item to bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> items to bin…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this audio file out of bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files out of bin?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Moving audio file out of bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> audio files out of bin…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this video out of bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos out of bin?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Moving video out of bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> videos out of bin…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this photo out of bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos out of bin?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Moving photo out of bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> photos out of bin…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this item out of bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items out of bin?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Moving item out of bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> items out of bin…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this audio file?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> audio files?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Deleting audio file…}other{Deleting <xliff:g id="COUNT">^1</xliff:g> audio files…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this video?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> videos?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Deleting video…}other{Deleting <xliff:g id="COUNT">^1</xliff:g> videos…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this photo?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> photos?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Deleting photo…}other{Deleting <xliff:g id="COUNT">^1</xliff:g> photos…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this item?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> items?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Deleting item…}other{Deleting <xliff:g id="COUNT">^1</xliff:g> items…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> can\'t process media files"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Media processing cancelled"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Media processing error"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 2f1bda0..06d385c 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Media"</string>
<string name="storage_description" msgid="4081716890357580107">"Local storage"</string>
<string name="app_label" msgid="9035307001052716210">"Media Storage"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"Photo picker"</string>
<string name="artist_label" msgid="8105600993099120273">"Artist"</string>
<string name="unknown" msgid="2059049215682829375">"Unknown"</string>
<string name="root_images" msgid="5861633549189045666">"Images"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Continue"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Allow"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Deny"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Plus <xliff:g id="COUNT_1">^1</xliff:g> additional items</item>
- <item quantity="one">Plus <xliff:g id="COUNT_0">^1</xliff:g> additional item</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Plus <xliff:g id="COUNT_0">^1</xliff:g> additional item}other{Plus <xliff:g id="COUNT_1">^1</xliff:g> additional items}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Clear temporary app files"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> would like to clear some temporary files. This may result in an increased usage of battery or mobile data."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Clearing temporary app files…"</string>
<string name="clear" msgid="5524638938415865915">"Clear"</string>
<string name="allow" msgid="8885707816848569619">"Allow"</string>
<string name="deny" msgid="6040983710442068936">"Deny"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> audio files?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this audio file?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">Modifying <xliff:g id="COUNT">^1</xliff:g> audio files…</item>
- <item quantity="one">Modifying audio file…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> videos?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this video?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">Modifying <xliff:g id="COUNT">^1</xliff:g> videos…</item>
- <item quantity="one">Modifying video…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> photos?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this photo?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">Modifying <xliff:g id="COUNT">^1</xliff:g> photos…</item>
- <item quantity="one">Modifying photo…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> items?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this item?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">Modifying <xliff:g id="COUNT">^1</xliff:g> items…</item>
- <item quantity="one">Modifying item…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files to bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this audio file to bin?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> audio files to bin…</item>
- <item quantity="one">Moving audio file to bin…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos to bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this video to bin?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> videos to bin…</item>
- <item quantity="one">Moving video to bin…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos to bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this photo to bin?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> photos to bin…</item>
- <item quantity="one">Moving photo to bin…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items to bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this item to bin?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> items to bin…</item>
- <item quantity="one">Moving item to bin…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files out of bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this audio file out of bin?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> audio files out of bin…</item>
- <item quantity="one">Moving audio file out of bin…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos out of bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this video out of bin?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> videos out of bin…</item>
- <item quantity="one">Moving video out of bin…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos out of bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this photo out of bin?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> photos out of bin…</item>
- <item quantity="one">Moving photo out of bin…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items out of bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this item out of bin?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> items out of bin…</item>
- <item quantity="one">Moving item out of bin…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> audio files?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this audio file?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">Deleting <xliff:g id="COUNT">^1</xliff:g> audio files…</item>
- <item quantity="one">Deleting audio file…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> videos?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this video?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">Deleting <xliff:g id="COUNT">^1</xliff:g> videos…</item>
- <item quantity="one">Deleting video…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> photos?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this photo?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">Deleting <xliff:g id="COUNT">^1</xliff:g> photos…</item>
- <item quantity="one">Deleting photo…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> items?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this item?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">Deleting <xliff:g id="COUNT">^1</xliff:g> items…</item>
- <item quantity="one">Deleting item…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Add"</string>
+ <string name="deselect" msgid="4297825044827769490">"Deselect"</string>
+ <string name="deselected" msgid="8488133193326208475">"Deselected"</string>
+ <string name="select" msgid="2704765470563027689">"Select"</string>
+ <string name="selected" msgid="9151797369975828124">"Selected"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Select up to <xliff:g id="COUNT_0">^1</xliff:g> item}other{Select up to <xliff:g id="COUNT_1">^1</xliff:g> items}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Recent"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"No photos or videos"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"No albums"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"View selected"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Photos"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albums"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Preview"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Switch to work"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Switch to personal"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Blocked by your admin"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Accessing work data from a personal app is not permitted"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Accessing personal data from a work app is not permitted"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Work apps are paused"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"To open work photos, turn on your work apps and then try again"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"This app can only access the photos that you select"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}other{<xliff:g id="COUNT_1">^1</xliff:g> items}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Add (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Camera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Downloads"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Favourites"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Screenshots"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Motion photo"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> taken on <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Video taken on <xliff:g id="TIME">%1$s</xliff:g> with duration <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Photo"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Motion photo"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Mute video"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Unmute video"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Play video"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Pause video"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Cloud media now available from <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"not selected"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this audio file?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> audio files?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Modifying audio file…}other{Modifying <xliff:g id="COUNT">^1</xliff:g> audio files…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this video?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> videos?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Modifying video…}other{Modifying <xliff:g id="COUNT">^1</xliff:g> videos…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this photo?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> photos?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Modifying photo…}other{Modifying <xliff:g id="COUNT">^1</xliff:g> photos…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this item?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> items?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Modifying item…}other{Modifying <xliff:g id="COUNT">^1</xliff:g> items…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this audio file to bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files to bin?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Moving audio file to bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> audio files to bin…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this video to bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos to bin?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Moving video to bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> videos to bin…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this photo to bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos to bin?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Moving photo to bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> photos to bin…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this item to bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items to bin?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Moving item to bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> items to bin…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this audio file out of bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files out of bin?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Moving audio file out of bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> audio files out of bin…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this video out of bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos out of bin?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Moving video out of bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> videos out of bin…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this photo out of bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos out of bin?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Moving photo out of bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> photos out of bin…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this item out of bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items out of bin?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Moving item out of bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> items out of bin…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this audio file?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> audio files?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Deleting audio file…}other{Deleting <xliff:g id="COUNT">^1</xliff:g> audio files…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this video?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> videos?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Deleting video…}other{Deleting <xliff:g id="COUNT">^1</xliff:g> videos…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this photo?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> photos?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Deleting photo…}other{Deleting <xliff:g id="COUNT">^1</xliff:g> photos…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this item?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> items?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Deleting item…}other{Deleting <xliff:g id="COUNT">^1</xliff:g> items…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> can\'t process media files"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Media processing cancelled"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Media processing error"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 2f1bda0..06d385c 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Media"</string>
<string name="storage_description" msgid="4081716890357580107">"Local storage"</string>
<string name="app_label" msgid="9035307001052716210">"Media Storage"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"Photo picker"</string>
<string name="artist_label" msgid="8105600993099120273">"Artist"</string>
<string name="unknown" msgid="2059049215682829375">"Unknown"</string>
<string name="root_images" msgid="5861633549189045666">"Images"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Continue"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Allow"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Deny"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Plus <xliff:g id="COUNT_1">^1</xliff:g> additional items</item>
- <item quantity="one">Plus <xliff:g id="COUNT_0">^1</xliff:g> additional item</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Plus <xliff:g id="COUNT_0">^1</xliff:g> additional item}other{Plus <xliff:g id="COUNT_1">^1</xliff:g> additional items}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Clear temporary app files"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> would like to clear some temporary files. This may result in an increased usage of battery or mobile data."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Clearing temporary app files…"</string>
<string name="clear" msgid="5524638938415865915">"Clear"</string>
<string name="allow" msgid="8885707816848569619">"Allow"</string>
<string name="deny" msgid="6040983710442068936">"Deny"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> audio files?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this audio file?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">Modifying <xliff:g id="COUNT">^1</xliff:g> audio files…</item>
- <item quantity="one">Modifying audio file…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> videos?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this video?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">Modifying <xliff:g id="COUNT">^1</xliff:g> videos…</item>
- <item quantity="one">Modifying video…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> photos?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this photo?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">Modifying <xliff:g id="COUNT">^1</xliff:g> photos…</item>
- <item quantity="one">Modifying photo…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> items?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this item?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">Modifying <xliff:g id="COUNT">^1</xliff:g> items…</item>
- <item quantity="one">Modifying item…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files to bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this audio file to bin?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> audio files to bin…</item>
- <item quantity="one">Moving audio file to bin…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos to bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this video to bin?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> videos to bin…</item>
- <item quantity="one">Moving video to bin…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos to bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this photo to bin?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> photos to bin…</item>
- <item quantity="one">Moving photo to bin…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items to bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this item to bin?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> items to bin…</item>
- <item quantity="one">Moving item to bin…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files out of bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this audio file out of bin?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> audio files out of bin…</item>
- <item quantity="one">Moving audio file out of bin…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos out of bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this video out of bin?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> videos out of bin…</item>
- <item quantity="one">Moving video out of bin…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos out of bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this photo out of bin?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> photos out of bin…</item>
- <item quantity="one">Moving photo out of bin…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items out of bin?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this item out of bin?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> items out of bin…</item>
- <item quantity="one">Moving item out of bin…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> audio files?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this audio file?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">Deleting <xliff:g id="COUNT">^1</xliff:g> audio files…</item>
- <item quantity="one">Deleting audio file…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> videos?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this video?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">Deleting <xliff:g id="COUNT">^1</xliff:g> videos…</item>
- <item quantity="one">Deleting video…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> photos?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this photo?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">Deleting <xliff:g id="COUNT">^1</xliff:g> photos…</item>
- <item quantity="one">Deleting photo…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> items?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this item?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">Deleting <xliff:g id="COUNT">^1</xliff:g> items…</item>
- <item quantity="one">Deleting item…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Add"</string>
+ <string name="deselect" msgid="4297825044827769490">"Deselect"</string>
+ <string name="deselected" msgid="8488133193326208475">"Deselected"</string>
+ <string name="select" msgid="2704765470563027689">"Select"</string>
+ <string name="selected" msgid="9151797369975828124">"Selected"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Select up to <xliff:g id="COUNT_0">^1</xliff:g> item}other{Select up to <xliff:g id="COUNT_1">^1</xliff:g> items}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Recent"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"No photos or videos"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"No albums"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"View selected"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Photos"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albums"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Preview"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Switch to work"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Switch to personal"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Blocked by your admin"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Accessing work data from a personal app is not permitted"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Accessing personal data from a work app is not permitted"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Work apps are paused"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"To open work photos, turn on your work apps and then try again"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"This app can only access the photos that you select"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}other{<xliff:g id="COUNT_1">^1</xliff:g> items}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Add (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Camera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Downloads"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Favourites"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Screenshots"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Motion photo"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> taken on <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Video taken on <xliff:g id="TIME">%1$s</xliff:g> with duration <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Photo"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Motion photo"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Mute video"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Unmute video"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Play video"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Pause video"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Cloud media now available from <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"not selected"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this audio file?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> audio files?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Modifying audio file…}other{Modifying <xliff:g id="COUNT">^1</xliff:g> audio files…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this video?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> videos?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Modifying video…}other{Modifying <xliff:g id="COUNT">^1</xliff:g> videos…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this photo?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> photos?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Modifying photo…}other{Modifying <xliff:g id="COUNT">^1</xliff:g> photos…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this item?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> items?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Modifying item…}other{Modifying <xliff:g id="COUNT">^1</xliff:g> items…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this audio file to bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files to bin?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Moving audio file to bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> audio files to bin…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this video to bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos to bin?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Moving video to bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> videos to bin…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this photo to bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos to bin?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Moving photo to bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> photos to bin…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this item to bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items to bin?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Moving item to bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> items to bin…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this audio file out of bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files out of bin?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Moving audio file out of bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> audio files out of bin…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this video out of bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos out of bin?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Moving video out of bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> videos out of bin…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this photo out of bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos out of bin?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Moving photo out of bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> photos out of bin…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this item out of bin?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items out of bin?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Moving item out of bin…}other{Moving <xliff:g id="COUNT">^1</xliff:g> items out of bin…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this audio file?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> audio files?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Deleting audio file…}other{Deleting <xliff:g id="COUNT">^1</xliff:g> audio files…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this video?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> videos?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Deleting video…}other{Deleting <xliff:g id="COUNT">^1</xliff:g> videos…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this photo?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> photos?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Deleting photo…}other{Deleting <xliff:g id="COUNT">^1</xliff:g> photos…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this item?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> items?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Deleting item…}other{Deleting <xliff:g id="COUNT">^1</xliff:g> items…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> can\'t process media files"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Media processing cancelled"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Media processing error"</string>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index 707a0f5..22092f9 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Media"</string>
<string name="storage_description" msgid="4081716890357580107">"Local storage"</string>
<string name="app_label" msgid="9035307001052716210">"Media Storage"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"Photo picker"</string>
<string name="artist_label" msgid="8105600993099120273">"Artist"</string>
<string name="unknown" msgid="2059049215682829375">"Unknown"</string>
<string name="root_images" msgid="5861633549189045666">"Images"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Continue"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Allow"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Deny"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Plus <xliff:g id="COUNT_1">^1</xliff:g> additional items</item>
- <item quantity="one">Plus <xliff:g id="COUNT_0">^1</xliff:g> additional item</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Plus <xliff:g id="COUNT_0">^1</xliff:g> additional item}other{Plus <xliff:g id="COUNT_1">^1</xliff:g> additional items}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Clear temporary app files"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> would like to clear some temporary files. This may result in an increased usage of battery or cellular data."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Clearing temporary app files…"</string>
<string name="clear" msgid="5524638938415865915">"Clear"</string>
<string name="allow" msgid="8885707816848569619">"Allow"</string>
<string name="deny" msgid="6040983710442068936">"Deny"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> audio files?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this audio file?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">Modifying <xliff:g id="COUNT">^1</xliff:g> audio files…</item>
- <item quantity="one">Modifying audio file…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> videos?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this video?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">Modifying <xliff:g id="COUNT">^1</xliff:g> videos…</item>
- <item quantity="one">Modifying video…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> photos?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this photo?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">Modifying <xliff:g id="COUNT">^1</xliff:g> photos…</item>
- <item quantity="one">Modifying photo…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> items?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this item?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">Modifying <xliff:g id="COUNT">^1</xliff:g> items…</item>
- <item quantity="one">Modifying item…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files to trash?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this audio file to trash?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> audio files to trash…</item>
- <item quantity="one">Moving audio file to trash…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos to trash?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this video to trash?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> videos to trash…</item>
- <item quantity="one">Moving video to trash…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos to trash?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this photo to trash?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> photos to trash…</item>
- <item quantity="one">Moving photo to trash…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items to trash?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this item to trash?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> items to trash…</item>
- <item quantity="one">Moving item to trash…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files out of trash?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this audio file out of trash?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> audio files out of trash…</item>
- <item quantity="one">Moving audio file out of trash…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos out of trash?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this video out of trash?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> videos out of trash…</item>
- <item quantity="one">Moving video out of trash…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos out of trash?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this photo out of trash?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> photos out of trash…</item>
- <item quantity="one">Moving photo out of trash…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items out of trash?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this item out of trash?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">Moving <xliff:g id="COUNT">^1</xliff:g> items out of trash…</item>
- <item quantity="one">Moving item out of trash…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> audio files?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this audio file?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">Deleting <xliff:g id="COUNT">^1</xliff:g> audio files…</item>
- <item quantity="one">Deleting audio file…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> videos?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this video?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">Deleting <xliff:g id="COUNT">^1</xliff:g> videos…</item>
- <item quantity="one">Deleting video…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> photos?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this photo?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">Deleting <xliff:g id="COUNT">^1</xliff:g> photos…</item>
- <item quantity="one">Deleting photo…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> items?</item>
- <item quantity="one">Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this item?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">Deleting <xliff:g id="COUNT">^1</xliff:g> items…</item>
- <item quantity="one">Deleting item…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Add"</string>
+ <string name="deselect" msgid="4297825044827769490">"Deselect"</string>
+ <string name="deselected" msgid="8488133193326208475">"Deselected"</string>
+ <string name="select" msgid="2704765470563027689">"Select"</string>
+ <string name="selected" msgid="9151797369975828124">"Selected"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Select up to <xliff:g id="COUNT_0">^1</xliff:g> item}other{Select up to <xliff:g id="COUNT_1">^1</xliff:g> items}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Recent"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"No photos or videos"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"No albums"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"View selected"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Photos"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albums"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Preview"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Switch to work"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Switch to personal"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Blocked by your admin"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Accessing work data from a personal app is not permitted"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Accessing personal data from a work app is not permitted"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Work apps are paused"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"To open work photos, turn on your work apps then try again"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"This app can only access the photos you select"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}other{<xliff:g id="COUNT_1">^1</xliff:g> items}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Add (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Camera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Downloads"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Favorites"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Screenshots"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Motion Photo"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> taken on <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Video taken on <xliff:g id="TIME">%1$s</xliff:g> with duration <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Photo"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Motion Photo"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Mute video"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Unmute video"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Play video"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Pause video"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Cloud media now available from <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"not selected"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this audio file?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> audio files?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Modifying audio file…}other{Modifying <xliff:g id="COUNT">^1</xliff:g> audio files…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this video?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> videos?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Modifying video…}other{Modifying <xliff:g id="COUNT">^1</xliff:g> videos…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this photo?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> photos?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Modifying photo…}other{Modifying <xliff:g id="COUNT">^1</xliff:g> photos…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to modify this item?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> items?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Modifying item…}other{Modifying <xliff:g id="COUNT">^1</xliff:g> items…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this audio file to trash?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files to trash?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Moving audio file to trash…}other{Moving <xliff:g id="COUNT">^1</xliff:g> audio files to trash…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this video to trash?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos to trash?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Moving video to trash…}other{Moving <xliff:g id="COUNT">^1</xliff:g> videos to trash…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this photo to trash?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos to trash?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Moving photo to trash…}other{Moving <xliff:g id="COUNT">^1</xliff:g> photos to trash…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this item to trash?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items to trash?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Moving item to trash…}other{Moving <xliff:g id="COUNT">^1</xliff:g> items to trash…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this audio file out of trash?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files out of trash?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Moving audio file out of trash…}other{Moving <xliff:g id="COUNT">^1</xliff:g> audio files out of trash…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this video out of trash?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos out of trash?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Moving video out of trash…}other{Moving <xliff:g id="COUNT">^1</xliff:g> videos out of trash…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this photo out of trash?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos out of trash?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Moving photo out of trash…}other{Moving <xliff:g id="COUNT">^1</xliff:g> photos out of trash…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to move this item out of trash?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items out of trash?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Moving item out of trash…}other{Moving <xliff:g id="COUNT">^1</xliff:g> items out of trash…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this audio file?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> audio files?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Deleting audio file…}other{Deleting <xliff:g id="COUNT">^1</xliff:g> audio files…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this video?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> videos?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Deleting video…}other{Deleting <xliff:g id="COUNT">^1</xliff:g> videos…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this photo?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> photos?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Deleting photo…}other{Deleting <xliff:g id="COUNT">^1</xliff:g> photos…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Allow <xliff:g id="APP_NAME_0">^1</xliff:g> to delete this item?}other{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> items?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Deleting item…}other{Deleting <xliff:g id="COUNT">^1</xliff:g> items…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> can\'t process media files"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Media processing cancelled"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Media processing error"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 016c843..d007714 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Selector de fotos"</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>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Continuar"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Permitir"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Denegar"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other"><xliff:g id="COUNT_1">^1</xliff:g> más</item>
- <item quantity="one"><xliff:g id="COUNT_0">^1</xliff:g> más</item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other"><xliff:g id="COUNT_1">^1</xliff:g> elementos adicionales</item>
- <item quantity="one"><xliff:g id="COUNT_0">^1</xliff:g> elemento adicional</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> más}many{+<xliff:g id="COUNT_1">^1</xliff:g>}other{<xliff:g id="COUNT_1">^1</xliff:g> más}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> elemento adicional}many{Plus <xliff:g id="COUNT_1">^1</xliff:g> additional items}other{<xliff:g id="COUNT_1">^1</xliff:g> elementos adicionales}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Borra archivos temporales de apps"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> quiere borrar algunos archivos temporales, lo que podría ocasionar un mayor uso de la batería o los datos móviles."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Borrando archivos temporales de apps…"</string>
<string name="clear" msgid="5524638938415865915">"Borrar"</string>
<string name="allow" msgid="8885707816848569619">"Permitir"</string>
<string name="deny" msgid="6040983710442068936">"Rechazar"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> archivos de audio?</item>
- <item quantity="one">¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este archivo de audio?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">Modificando <xliff:g id="COUNT">^1</xliff:g> archivos de audio…</item>
- <item quantity="one">Modificando el archivo de audio…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> videos?</item>
- <item quantity="one">¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este video?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">Modificando <xliff:g id="COUNT">^1</xliff:g> videos…</item>
- <item quantity="one">Modificando el video…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> fotos?</item>
- <item quantity="one">¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> modifique esta foto?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">Modificando <xliff:g id="COUNT">^1</xliff:g> fotos…</item>
- <item quantity="one">Modificando la foto…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> elementos?</item>
- <item quantity="one">¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este elemento?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">Modificando <xliff:g id="COUNT">^1</xliff:g> elementos…</item>
- <item quantity="one">Modificando el elemento…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> mueva <xliff:g id="COUNT">^2</xliff:g> archivos de audio a la papelera?</item>
- <item quantity="one">¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> mueva este archivo de audio a la papelera?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">Moviendo <xliff:g id="COUNT">^1</xliff:g> archivos de audio a la papelera…</item>
- <item quantity="one">Moviendo el archivo de audio a la papelera…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> mueva <xliff:g id="COUNT">^2</xliff:g> videos a la papelera?</item>
- <item quantity="one">¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> mueva este video a la papelera?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">Moviendo <xliff:g id="COUNT">^1</xliff:g> videos a la papelera…</item>
- <item quantity="one">Moviendo el video a la papelera…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> mueva <xliff:g id="COUNT">^2</xliff:g> fotos a la papelera?</item>
- <item quantity="one">¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> mueva esta foto a la papelera?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">Moviendo <xliff:g id="COUNT">^1</xliff:g> fotos a la papelera…</item>
- <item quantity="one">Moviendo la foto a la papelera…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">¿Deseas permitir <xliff:g id="APP_NAME_1">^1</xliff:g> mueva <xliff:g id="COUNT">^2</xliff:g> elementos a la papelera?</item>
- <item quantity="one">¿Deseas permitir <xliff:g id="APP_NAME_0">^1</xliff:g> mueva este elemento a la papelera?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">Moviendo <xliff:g id="COUNT">^1</xliff:g> elementos a la papelera…</item>
- <item quantity="one">Moviendo el elemento a la papelera…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> quite <xliff:g id="COUNT">^2</xliff:g> archivos de audio de la papelera?</item>
- <item quantity="one">¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> quite este archivo de audio de la papelera?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">Quitando <xliff:g id="COUNT">^1</xliff:g> archivos de audio de la papelera…</item>
- <item quantity="one">Quitando el archivo de audio de la papelera…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> quite <xliff:g id="COUNT">^2</xliff:g> videos de la papelera?</item>
- <item quantity="one">¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> quite este video de la papelera?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">Quitando <xliff:g id="COUNT">^1</xliff:g> videos de la papelera…</item>
- <item quantity="one">Quitando el video de la papelera…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> quite <xliff:g id="COUNT">^2</xliff:g> fotos de la papelera?</item>
- <item quantity="one">¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> quite esta foto de la papelera?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">Quitando <xliff:g id="COUNT">^1</xliff:g> fotos de la papelera…</item>
- <item quantity="one">Quitando la foto de la papelera…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> quite <xliff:g id="COUNT">^2</xliff:g> elementos de la papelera?</item>
- <item quantity="one">¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> quite este elemento de la papelera?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">Quitando <xliff:g id="COUNT">^1</xliff:g> elementos de la papelera…</item>
- <item quantity="one">Quitando el elemento de la papelera…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> borre <xliff:g id="COUNT">^2</xliff:g> archivos de audio?</item>
- <item quantity="one">¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> borre este archivo de audio?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">Borrando <xliff:g id="COUNT">^1</xliff:g> archivos de audio…</item>
- <item quantity="one">Borrando el archivo de audio…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> borre <xliff:g id="COUNT">^2</xliff:g> videos?</item>
- <item quantity="one">¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> borre este video?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">Borrando <xliff:g id="COUNT">^1</xliff:g> videos…</item>
- <item quantity="one">Borrando el video…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> borre <xliff:g id="COUNT">^2</xliff:g> fotos?</item>
- <item quantity="one">¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> borre esta foto?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">Borrando <xliff:g id="COUNT">^1</xliff:g> fotos…</item>
- <item quantity="one">Borrando la foto…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> borre <xliff:g id="COUNT">^2</xliff:g> elementos?</item>
- <item quantity="one">¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> borre este elemento?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">Borrando <xliff:g id="COUNT">^1</xliff:g> elementos…</item>
- <item quantity="one">Borrando el elemento…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Agregar"</string>
+ <string name="deselect" msgid="4297825044827769490">"Anular la selección"</string>
+ <string name="deselected" msgid="8488133193326208475">"Sin seleccionar"</string>
+ <string name="select" msgid="2704765470563027689">"Seleccionar"</string>
+ <string name="selected" msgid="9151797369975828124">"Seleccionado"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Elige <xliff:g id="COUNT_0">^1</xliff:g> elemento como máximo}many{Select up to <xliff:g id="COUNT_1">^1</xliff:g> items}other{Elige <xliff:g id="COUNT_1">^1</xliff:g> elementos como máximo}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Recientes"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"No hay fotos ni videos"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"No hay álbumes"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Ver seleccionados"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Fotos"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Álbumes"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Vista previa"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Cambiar al perfil de trabajo"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Cambiar al perfil personal"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Bloqueado por tu administrador"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"No se permite el acceso a datos de trabajo desde una app personal"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"No se permite el acceso a datos personales desde una app de trabajo"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Se pausaron las apps de trabajo"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Para abrir fotos de tu perfil de trabajo, activa tus apps de trabajo y vuelve a intentarlo"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Esta app solo puede acceder a las fotos que selecciones"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> elemento}many{<xliff:g id="COUNT_1">^1</xliff:g> items}other{<xliff:g id="COUNT_1">^1</xliff:g> elementos}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Agregar (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Cámara"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Descargas"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Favoritos"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Capturas de pantalla"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Foto en movimiento"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"Se tomó <xliff:g id="ITEM_NAME">%1$s</xliff:g> a la(s) <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Video creado el <xliff:g id="TIME">%1$s</xliff:g> de <xliff:g id="DURATION">%2$s</xliff:g> de duración"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Foto"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Foto en movimiento"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Silenciar video"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Activar sonido del video"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Reproducir video"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Pausar video"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Contenido multimedia en la nube ahora disponible desde <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"sin seleccionar"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este archivo de audio?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> audio files?}other{¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> archivos de audio?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Modificando el archivo de audio…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> audio files…}other{Modificando <xliff:g id="COUNT">^1</xliff:g> archivos de audio…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este video?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> videos?}other{¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> videos?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Modificando el video…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> videos…}other{Modificando <xliff:g id="COUNT">^1</xliff:g> videos…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> modifique esta foto?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> photos?}other{¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> fotos?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Modificando la foto…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> photos…}other{Modificando <xliff:g id="COUNT">^1</xliff:g> fotos…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este elemento?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> items?}other{¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> elementos?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Modificando el elemento…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> items…}other{Modificando <xliff:g id="COUNT">^1</xliff:g> elementos…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> mueva este archivo de audio a la papelera?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files to trash?}other{¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> mueva <xliff:g id="COUNT">^2</xliff:g> archivos de audio a la papelera?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Moviendo el archivo de audio a la papelera…}many{Moving <xliff:g id="COUNT">^1</xliff:g> audio files to trash…}other{Moviendo <xliff:g id="COUNT">^1</xliff:g> archivos de audio a la papelera…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> mueva este video a la papelera?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos to trash?}other{¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> mueva <xliff:g id="COUNT">^2</xliff:g> videos a la papelera?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Moviendo el video a la papelera…}many{Moving <xliff:g id="COUNT">^1</xliff:g> videos to trash…}other{Moviendo <xliff:g id="COUNT">^1</xliff:g> videos a la papelera…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> mueva esta foto a la papelera?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos to trash?}other{¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> mueva <xliff:g id="COUNT">^2</xliff:g> fotos a la papelera?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Moviendo la foto a la papelera…}many{Moving <xliff:g id="COUNT">^1</xliff:g> photos to trash…}other{Moviendo <xliff:g id="COUNT">^1</xliff:g> fotos a la papelera…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> mueva este elemento a la papelera?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items to trash?}other{¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> mueva <xliff:g id="COUNT">^2</xliff:g> elementos a la papelera?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Moviendo el elemento a la papelera…}many{Moving <xliff:g id="COUNT">^1</xliff:g> items to trash…}other{Moviendo <xliff:g id="COUNT">^1</xliff:g> elementos a la papelera…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> quite este archivo de audio de la papelera?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files out of trash?}other{¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> quite <xliff:g id="COUNT">^2</xliff:g> archivos de audio de la papelera?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Quitando el archivo de audio de la papelera…}many{Moving <xliff:g id="COUNT">^1</xliff:g> audio files out of trash…}other{Quitando <xliff:g id="COUNT">^1</xliff:g> archivos de audio de la papelera…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> quite este video de la papelera?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos out of trash?}other{¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> quite <xliff:g id="COUNT">^2</xliff:g> videos de la papelera?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Quitando el video de la papelera…}many{Moving <xliff:g id="COUNT">^1</xliff:g> videos out of trash…}other{Quitando <xliff:g id="COUNT">^1</xliff:g> videos de la papelera…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> quite esta foto de la papelera?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos out of trash?}other{¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> quite <xliff:g id="COUNT">^2</xliff:g> fotos de la papelera?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Quitando la foto de la papelera…}many{Moving <xliff:g id="COUNT">^1</xliff:g> photos out of trash…}other{Quitando <xliff:g id="COUNT">^1</xliff:g> fotos de la papelera…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> quite este elemento de la papelera?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items out of trash?}other{¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> quite <xliff:g id="COUNT">^2</xliff:g> elementos de la papelera?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Quitando el elemento de la papelera…}many{Moving <xliff:g id="COUNT">^1</xliff:g> items out of trash…}other{Quitando <xliff:g id="COUNT">^1</xliff:g> elementos de la papelera…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> borre este archivo de audio?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> audio files?}other{¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> borre <xliff:g id="COUNT">^2</xliff:g> archivos de audio?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Borrando el archivo de audio…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> audio files…}other{Borrando <xliff:g id="COUNT">^1</xliff:g> archivos de audio…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> borre este video?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> videos?}other{¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> borre <xliff:g id="COUNT">^2</xliff:g> videos?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Borrando el video…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> videos…}other{Borrando <xliff:g id="COUNT">^1</xliff:g> videos…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> borre esta foto?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> photos?}other{¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> borre <xliff:g id="COUNT">^2</xliff:g> fotos?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Borrando la foto…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> photos…}other{Borrando <xliff:g id="COUNT">^1</xliff:g> fotos…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{¿Deseas permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> borre este elemento?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> items?}other{¿Deseas permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> borre <xliff:g id="COUNT">^2</xliff:g> elementos?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Borrando el elemento…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> items…}other{Borrando <xliff:g id="COUNT">^1</xliff:g> elementos…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> no puede procesar archivos multimedia"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Se canceló el procesamiento de contenido multimedia"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Error al procesar el contenido multimedia"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 89b404f..93b0af2 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Selector de fotos"</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>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Continuar"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Permitir"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Denegar"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Y <xliff:g id="COUNT_1">^1</xliff:g> elementos más</item>
- <item quantity="one">Y <xliff:g id="COUNT_0">^1</xliff:g> elemento más</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}many{+<xliff:g id="COUNT_1">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Y <xliff:g id="COUNT_0">^1</xliff:g> elemento más}many{Plus <xliff:g id="COUNT_1">^1</xliff:g> additional items}other{Y <xliff:g id="COUNT_1">^1</xliff:g> elementos más}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Borrar archivos de aplicaciones temporales"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> quiere borrar algunos archivos temporales. Esta acción puede aumentar el uso de la batería o los datos móviles."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Borrando archivos temporales de aplicaciones…"</string>
<string name="clear" msgid="5524638938415865915">"Borrar"</string>
<string name="allow" msgid="8885707816848569619">"Permitir"</string>
<string name="deny" msgid="6040983710442068936">"Denegar"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> archivos de audio?</item>
- <item quantity="one">¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este archivo de audio?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">Modificando <xliff:g id="COUNT">^1</xliff:g> archivos de audio…</item>
- <item quantity="one">Modificando archivo de audio…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> vídeos?</item>
- <item quantity="one">¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este vídeo?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">Modificando <xliff:g id="COUNT">^1</xliff:g> vídeos…</item>
- <item quantity="one">Modificando vídeo…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> fotos?</item>
- <item quantity="one">¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> modifique esta foto?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">Modificando <xliff:g id="COUNT">^1</xliff:g> fotos…</item>
- <item quantity="one">Modificando foto…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> elementos?</item>
- <item quantity="one">¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este elemento?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">Modificando <xliff:g id="COUNT">^1</xliff:g> elementos…</item>
- <item quantity="one">Modificando elemento…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> mueva <xliff:g id="COUNT">^2</xliff:g> archivos de audio a la papelera?</item>
- <item quantity="one">¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> mueva este archivo de audio a la papelera?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">Moviendo <xliff:g id="COUNT">^1</xliff:g> archivos de audio a la papelera…</item>
- <item quantity="one">Moviendo archivo de audio a la papelera…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> mueva <xliff:g id="COUNT">^2</xliff:g> vídeos a la papelera?</item>
- <item quantity="one">¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> mueva este vídeo a la papelera?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">Moviendo <xliff:g id="COUNT">^1</xliff:g> vídeos a la papelera…</item>
- <item quantity="one">Moviendo vídeo a la papelera…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> mueva <xliff:g id="COUNT">^2</xliff:g> fotos a la papelera?</item>
- <item quantity="one">¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> mueva esta foto a la papelera?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">Moviendo <xliff:g id="COUNT">^1</xliff:g> fotos a la papelera…</item>
- <item quantity="one">Moviendo foto a la papelera…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> mueva <xliff:g id="COUNT">^2</xliff:g> elementos a la papelera?</item>
- <item quantity="one">¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> mueva este elemento a la papelera?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">Moviendo <xliff:g id="COUNT">^1</xliff:g> elementos a la papelera…</item>
- <item quantity="one">Moviendo elemento a la papelera…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> quite <xliff:g id="COUNT">^2</xliff:g> archivos de audio de la papelera?</item>
- <item quantity="one">¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> quite este archivo de audio de la papelera?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">Quitando <xliff:g id="COUNT">^1</xliff:g> archivos de audio de la papelera…</item>
- <item quantity="one">Quitando archivo de audio de la papelera…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> quite <xliff:g id="COUNT">^2</xliff:g> vídeos de la papelera?</item>
- <item quantity="one">¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> quite este vídeo de la papelera?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">Quitando <xliff:g id="COUNT">^1</xliff:g> vídeos de la papelera…</item>
- <item quantity="one">Quitando vídeo de la papelera…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> quite <xliff:g id="COUNT">^2</xliff:g> fotos de la papelera?</item>
- <item quantity="one">¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> quite esta foto de la papelera?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">Quitando <xliff:g id="COUNT">^1</xliff:g> fotos de la papelera…</item>
- <item quantity="one">Quitando foto de la papelera…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> quite <xliff:g id="COUNT">^2</xliff:g> elementos de la papelera?</item>
- <item quantity="one">¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> quite este elemento de la papelera?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">Quitando <xliff:g id="COUNT">^1</xliff:g> elementos de la papelera…</item>
- <item quantity="one">Quitando elemento de la papelera…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> elimine <xliff:g id="COUNT">^2</xliff:g> archivos de audio?</item>
- <item quantity="one">¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> elimine este archivo de audio?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">Eliminando <xliff:g id="COUNT">^1</xliff:g> archivos de audio…</item>
- <item quantity="one">Eliminando archivo de audio…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> elimine <xliff:g id="COUNT">^2</xliff:g> vídeos?</item>
- <item quantity="one">¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> elimine este vídeo?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">Eliminando <xliff:g id="COUNT">^1</xliff:g> vídeos…</item>
- <item quantity="one">Eliminando vídeo…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> elimine <xliff:g id="COUNT">^2</xliff:g> fotos?</item>
- <item quantity="one">¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> elimine esta foto?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">Eliminando <xliff:g id="COUNT">^1</xliff:g> fotos…</item>
- <item quantity="one">Eliminando foto…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> elimine <xliff:g id="COUNT">^2</xliff:g> elementos?</item>
- <item quantity="one">¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> elimine este elemento?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">Eliminando <xliff:g id="COUNT">^1</xliff:g> elementos…</item>
- <item quantity="one">Eliminando elemento…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Añadir"</string>
+ <string name="deselect" msgid="4297825044827769490">"Desmarcar"</string>
+ <string name="deselected" msgid="8488133193326208475">"Desmarcado"</string>
+ <string name="select" msgid="2704765470563027689">"Seleccionar"</string>
+ <string name="selected" msgid="9151797369975828124">"Marcado"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Selecciona hasta <xliff:g id="COUNT_0">^1</xliff:g> elemento}many{Select up to <xliff:g id="COUNT_1">^1</xliff:g> items}other{Selecciona hasta <xliff:g id="COUNT_1">^1</xliff:g> elementos}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Reciente"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"No hay fotos ni vídeos"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"No hay ningún álbum"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Ver seleccionados"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Fotos"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Álbumes"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Vista previa"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Cambiar al de trabajo"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Cambiar al personal"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Bloqueado por tu administrador"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"No se puede acceder a datos de trabajo desde una aplicación personal"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"No se puede acceder a datos personales desde una aplicación de trabajo"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Las aplicaciones de trabajo están en pausa"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Para abrir fotos del trabajo, activa tus aplicaciones de trabajo e inténtalo de nuevo"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Esta aplicación solo puede acceder a las fotos que selecciones"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> elemento}many{<xliff:g id="COUNT_1">^1</xliff:g> items}other{<xliff:g id="COUNT_1">^1</xliff:g> elementos}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Añadir (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Cámara"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Descargas"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Favoritos"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Capturas de pantalla"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Foto con movimiento"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g>, del <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Vídeo grabado el <xliff:g id="TIME">%1$s</xliff:g> con una duración de <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Foto"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Foto con movimiento"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Silenciar vídeo"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Dejar de silenciar vídeo"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Reproducir vídeo"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Pausar vídeo"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Contenido multimedia en la nube ahora disponible desde <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"no seleccionado"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este archivo de audio?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> audio files?}other{¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> archivos de audio?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Modificando archivo de audio…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> audio files…}other{Modificando <xliff:g id="COUNT">^1</xliff:g> archivos de audio…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este vídeo?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> videos?}other{¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> vídeos?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Modificando vídeo…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> videos…}other{Modificando <xliff:g id="COUNT">^1</xliff:g> vídeos…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> modifique esta foto?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> photos?}other{¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> fotos?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Modificando foto…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> photos…}other{Modificando <xliff:g id="COUNT">^1</xliff:g> fotos…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este elemento?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> items?}other{¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> elementos?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Modificando elemento…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> items…}other{Modificando <xliff:g id="COUNT">^1</xliff:g> elementos…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> mueva este archivo de audio a la papelera?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files to trash?}other{¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> mueva <xliff:g id="COUNT">^2</xliff:g> archivos de audio a la papelera?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Moviendo archivo de audio a la papelera…}many{Moving <xliff:g id="COUNT">^1</xliff:g> audio files to trash…}other{Moviendo <xliff:g id="COUNT">^1</xliff:g> archivos de audio a la papelera…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> mueva este vídeo a la papelera?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos to trash?}other{¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> mueva <xliff:g id="COUNT">^2</xliff:g> vídeos a la papelera?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Moviendo vídeo a la papelera…}many{Moving <xliff:g id="COUNT">^1</xliff:g> videos to trash…}other{Moviendo <xliff:g id="COUNT">^1</xliff:g> vídeos a la papelera…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> mueva esta foto a la papelera?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos to trash?}other{¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> mueva <xliff:g id="COUNT">^2</xliff:g> fotos a la papelera?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Moviendo foto a la papelera…}many{Moving <xliff:g id="COUNT">^1</xliff:g> photos to trash…}other{Moviendo <xliff:g id="COUNT">^1</xliff:g> fotos a la papelera…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> mueva este elemento a la papelera?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items to trash?}other{¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> mueva <xliff:g id="COUNT">^2</xliff:g> elementos a la papelera?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Moviendo elemento a la papelera…}many{Moving <xliff:g id="COUNT">^1</xliff:g> items to trash…}other{Moviendo <xliff:g id="COUNT">^1</xliff:g> elementos a la papelera…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> quite este archivo de audio de la papelera?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files out of trash?}other{¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> quite <xliff:g id="COUNT">^2</xliff:g> archivos de audio de la papelera?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Quitando archivo de audio de la papelera…}many{Moving <xliff:g id="COUNT">^1</xliff:g> audio files out of trash…}other{Quitando <xliff:g id="COUNT">^1</xliff:g> archivos de audio de la papelera…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> quite este vídeo de la papelera?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos out of trash?}other{¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> quite <xliff:g id="COUNT">^2</xliff:g> vídeos de la papelera?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Quitando vídeo de la papelera…}many{Moving <xliff:g id="COUNT">^1</xliff:g> videos out of trash…}other{Quitando <xliff:g id="COUNT">^1</xliff:g> vídeos de la papelera…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> quite esta foto de la papelera?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos out of trash?}other{¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> quite <xliff:g id="COUNT">^2</xliff:g> fotos de la papelera?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Quitando foto de la papelera…}many{Moving <xliff:g id="COUNT">^1</xliff:g> photos out of trash…}other{Quitando <xliff:g id="COUNT">^1</xliff:g> fotos de la papelera…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> quite este elemento de la papelera?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items out of trash?}other{¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> quite <xliff:g id="COUNT">^2</xliff:g> elementos de la papelera?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Quitando elemento de la papelera…}many{Moving <xliff:g id="COUNT">^1</xliff:g> items out of trash…}other{Quitando <xliff:g id="COUNT">^1</xliff:g> elementos de la papelera…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> elimine este archivo de audio?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> audio files?}other{¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> elimine <xliff:g id="COUNT">^2</xliff:g> archivos de audio?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Eliminando archivo de audio…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> audio files…}other{Eliminando <xliff:g id="COUNT">^1</xliff:g> archivos de audio…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> elimine este vídeo?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> videos?}other{¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> elimine <xliff:g id="COUNT">^2</xliff:g> vídeos?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Eliminando vídeo…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> videos…}other{Eliminando <xliff:g id="COUNT">^1</xliff:g> vídeos…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> elimine esta foto?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> photos?}other{¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> elimine <xliff:g id="COUNT">^2</xliff:g> fotos?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Eliminando foto…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> photos…}other{Eliminando <xliff:g id="COUNT">^1</xliff:g> fotos…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{¿Permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> elimine este elemento?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> items?}other{¿Permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> elimine <xliff:g id="COUNT">^2</xliff:g> elementos?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Eliminando elemento…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> items…}other{Eliminando <xliff:g id="COUNT">^1</xliff:g> elementos…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> no puede procesar archivos multimedia"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Procesamiento de elementos multimedia cancelado"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"No se han podido procesar elementos multimedia"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 27a526a..fc4c00e 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Fotovalija"</string>
<string name="artist_label" msgid="8105600993099120273">"Esitaja"</string>
<string name="unknown" msgid="2059049215682829375">"Teadmata"</string>
<string name="root_images" msgid="5861633549189045666">"Pildid"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Jätka"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Luba"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Keela"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Veel <xliff:g id="COUNT_1">^1</xliff:g> lisaüksust</item>
- <item quantity="one">Veel <xliff:g id="COUNT_0">^1</xliff:g> lisaüksus</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Veel <xliff:g id="COUNT_0">^1</xliff:g> lisaüksus}other{Veel <xliff:g id="COUNT_1">^1</xliff:g> lisaüksust}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Rakenduse ajutiste failide kustutamine"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> soovib kustutada mõned ajutised failid. See võib aku või mobiilse andmeside kasutust suurendada."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Rakenduse ajutiste failide kustutamine …"</string>
<string name="clear" msgid="5524638938415865915">"Kustuta"</string>
<string name="allow" msgid="8885707816848569619">"Luba"</string>
<string name="deny" msgid="6040983710442068936">"Keela"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">Kas lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> helifaili muuta?</item>
- <item quantity="one">Kas lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> seda helifaili muuta?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> helifaili töötlemine …</item>
- <item quantity="one">Helifaili töötlemine …</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">Kas lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> videot muuta?</item>
- <item quantity="one">Kas lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> seda videot muuta?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> video töötlemine …</item>
- <item quantity="one">Video töötlemine …</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">Kas lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> fotot muuta?</item>
- <item quantity="one">Kas lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> seda fotot muuta?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> foto töötlemine …</item>
- <item quantity="one">Foto töötlemine …</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">Kas lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> üksust muuta?</item>
- <item quantity="one">Kas lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> seda üksust muuta?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> üksuse töötlemine …</item>
- <item quantity="one">Üksuse töötlemine …</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">Kas lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> helifaili prügikasti teisaldada?</item>
- <item quantity="one">Kas lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> see helifail prügikasti teisaldada?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> helifaili teisaldamine prügikasti …</item>
- <item quantity="one">Helifaili teisaldamine prügikasti …</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">Kas lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> videot prügikasti teisaldada?</item>
- <item quantity="one">Kas lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> see video prügikasti teisaldada?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> video teisaldamine prügikasti …</item>
- <item quantity="one">Video teisaldamine prügikasti …</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">Kas lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> fotot prügikasti teisaldada?</item>
- <item quantity="one">Kas lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> see foto prügikasti teisaldada?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> foto teisaldamine prügikasti …</item>
- <item quantity="one">Foto teisaldamine prügikasti …</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">Kas lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> üksust prügikasti teisaldada?</item>
- <item quantity="one">Kas lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> see üksus prügikasti teisaldada?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> üksuse teisaldamine prügikasti …</item>
- <item quantity="one">Üksuse teisaldamine prügikasti …</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">Kas lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> helifaili prügikastist taastada?</item>
- <item quantity="one">Kas lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> selle helifaili prügikastist taastada?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> helifaili taastamine prügikastist …</item>
- <item quantity="one">Helifaili taastamine prügikastist …</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">Kas lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> videot prügikastist taastada?</item>
- <item quantity="one">Kas lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> selle video prügikastist taastada?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> video taastamine prügikastist …</item>
- <item quantity="one">Video taastamine prügikastist …</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">Kas lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> fotot prügikastist taastada?</item>
- <item quantity="one">Kas lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> selle foto prügikastist taastada?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> foto taastamine prügikastist …</item>
- <item quantity="one">Foto taastamine prügikastist …</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">Kas lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> üksust prügikastist taastada?</item>
- <item quantity="one">Kas lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> selle üksuse prügikastist taastada?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> üksuse taastamine prügikastist …</item>
- <item quantity="one">Üksuse taastamine prügikastist …</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">Kas soovite lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> kustutada <xliff:g id="COUNT">^2</xliff:g> helifaili?</item>
- <item quantity="one">Kas soovite lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> selle helifaili kustutada?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> helifaili kustutamine …</item>
- <item quantity="one">Helifaili kustutamine …</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">Kas soovite lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> kustutada <xliff:g id="COUNT">^2</xliff:g> videot?</item>
- <item quantity="one">Kas soovite lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> selle video kustutada?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> video kustutamine …</item>
- <item quantity="one">Video kustutamine …</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">Kas soovite lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> kustutada <xliff:g id="COUNT">^2</xliff:g> fotot?</item>
- <item quantity="one">Kas soovite lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> selle foto kustutada?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> foto kustutamine …</item>
- <item quantity="one">Foto kustutamine …</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">Kas soovite lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> kustutada <xliff:g id="COUNT">^2</xliff:g> üksust?</item>
- <item quantity="one">Kas soovite lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> selle üksuse kustutada?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> üksuse kustutamine …</item>
- <item quantity="one">Üksuse kustutamine …</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Lisa"</string>
+ <string name="deselect" msgid="4297825044827769490">"Tühista valik"</string>
+ <string name="deselected" msgid="8488133193326208475">"Valik on tühistatud"</string>
+ <string name="select" msgid="2704765470563027689">"Vali"</string>
+ <string name="selected" msgid="9151797369975828124">"Valitud"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Valige kuni <xliff:g id="COUNT_0">^1</xliff:g> üksus}other{Valige kuni <xliff:g id="COUNT_1">^1</xliff:g> üksust}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Hiljutised"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Fotosid ega videoid pole"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Albumeid pole"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Kuva valitud"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Fotod"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albumid"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Eelvaade"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Lülituge tööprofiilile"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Lülituge isiklikule profiilile"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Blokeeris teie administraator"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Juurdepääs tööandmetele isikliku rakenduse kaudu pole lubatud"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Juurdepääs isiklikele andmetele töörakenduse kaudu pole lubatud"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Töörakendused on peatatud"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Tööfotode avamiseks lülitage töörakendused sisse ja proovige uuesti"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"See rakendus pääseb juurde vaid teie valitud fotodele"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> üksus}other{<xliff:g id="COUNT_1">^1</xliff:g> üksust}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Lisa (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Kaamera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Allalaadimised"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Lemmikud"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Ekraanipildid"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Liikuv foto"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> jäädvustati <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Video jäädvustamise aeg: <xliff:g id="TIME">%1$s</xliff:g>. Video kestus: <xliff:g id="DURATION">%2$s</xliff:g>."</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Foto"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Liikuv foto"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Video vaigistamine"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Video vaigistuse tühistamine"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Esita video"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Peata video"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Pilvemeedia on nüüd rakenduse <xliff:g id="PKG_NAME">%1$s</xliff:g> kaudu saadaval"</string>
+ <string name="not_selected" msgid="2244008151669896758">"pole valitud"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Kas lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> seda helifaili muuta?}other{Kas lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> helifaili muuta?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Helifaili muutmine …}other{<xliff:g id="COUNT">^1</xliff:g> helifaili muutmine …}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Kas lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> seda videot muuta?}other{Kas lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> videot muuta?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Video muutmine …}other{<xliff:g id="COUNT">^1</xliff:g> video muutmine …}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Kas lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> seda fotot muuta?}other{Kas lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> fotot muuta?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Foto muutmine …}other{<xliff:g id="COUNT">^1</xliff:g> foto muutmine …}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Kas lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> seda üksust muuta?}other{Kas lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> üksust muuta?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Üksuse muutmine …}other{<xliff:g id="COUNT">^1</xliff:g> üksuse muutmine …}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Kas lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> see helifail prügikasti teisaldada?}other{Kas lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> helifaili prügikasti teisaldada?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Helifaili teisaldamine prügikasti …}other{<xliff:g id="COUNT">^1</xliff:g> helifaili teisaldamine prügikasti …}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Kas lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> see video prügikasti teisaldada?}other{Kas lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> videot prügikasti teisaldada?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Video teisaldamine prügikasti …}other{<xliff:g id="COUNT">^1</xliff:g> video teisaldamine prügikasti …}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Kas lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> see foto prügikasti teisaldada?}other{Kas lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> fotot prügikasti teisaldada?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Foto teisaldamine prügikasti …}other{<xliff:g id="COUNT">^1</xliff:g> foto teisaldamine prügikasti …}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Kas lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> see üksus prügikasti teisaldada?}other{Kas lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> üksust prügikasti teisaldada?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Üksuse teisaldamine prügikasti …}other{<xliff:g id="COUNT">^1</xliff:g> üksuse teisaldamine prügikasti …}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Kas lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> selle helifaili prügikastist taastada?}other{Kas lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> helifaili prügikastist taastada?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Helifaili taastamine prügikastist …}other{<xliff:g id="COUNT">^1</xliff:g> helifaili taastamine prügikastist …}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Kas lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> selle video prügikastist taastada?}other{Kas lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> videot prügikastist taastada?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Video taastamine prügikastist …}other{<xliff:g id="COUNT">^1</xliff:g> video taastamine prügikastist …}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Kas lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> selle foto prügikastist taastada?}other{Kas lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> fotot prügikastist taastada?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Foto taastamine prügikastist …}other{<xliff:g id="COUNT">^1</xliff:g> foto taastamine prügikastist …}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Kas lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> selle üksuse prügikastist taastada?}other{Kas lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> üksust prügikastist taastada?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Üksuse taastamine prügikastist …}other{<xliff:g id="COUNT">^1</xliff:g> üksuse taastamine prügikastist …}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Kas soovite lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> selle helifaili kustutada?}other{Kas soovite lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> kustutada <xliff:g id="COUNT">^2</xliff:g> helifaili?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Helifaili kustutamine …}other{<xliff:g id="COUNT">^1</xliff:g> helifaili kustutamine …}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Kas soovite lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> selle video kustutada?}other{Kas soovite lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> kustutada <xliff:g id="COUNT">^2</xliff:g> videot?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Video kustutamine …}other{<xliff:g id="COUNT">^1</xliff:g> video kustutamine …}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Kas soovite lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> selle foto kustutada?}other{Kas soovite lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> kustutada <xliff:g id="COUNT">^2</xliff:g> fotot?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Foto kustutamine …}other{<xliff:g id="COUNT">^1</xliff:g> foto kustutamine …}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Kas soovite lubada rakendusel <xliff:g id="APP_NAME_0">^1</xliff:g> selle üksuse kustutada?}other{Kas soovite lubada rakendusel <xliff:g id="APP_NAME_1">^1</xliff:g> kustutada <xliff:g id="COUNT">^2</xliff:g> üksust?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Üksuse kustutamine …}other{<xliff:g id="COUNT">^1</xliff:g> üksuse kustutamine …}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> ei saa meediafaile töödelda"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Meedia töötlemine tühistati"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Viga meedia töötlemisel"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 707e083..e87efd7 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Multimedia-edukia"</string>
<string name="storage_description" msgid="4081716890357580107">"Biltegi lokala"</string>
<string name="app_label" msgid="9035307001052716210">"Multimediaren memoria-unitatea"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"Argazki-hautatzailea"</string>
<string name="artist_label" msgid="8105600993099120273">"Artista"</string>
<string name="unknown" msgid="2059049215682829375">"Ezezaguna"</string>
<string name="root_images" msgid="5861633549189045666">"Irudiak"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Egin aurrera"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Eman baimena"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Ukatu"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Eta beste <xliff:g id="COUNT_1">^1</xliff:g> elementu</item>
- <item quantity="one">Eta beste <xliff:g id="COUNT_0">^1</xliff:g> elementu</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{Beste <xliff:g id="COUNT_0">^1</xliff:g>}other{Beste <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Eta beste <xliff:g id="COUNT_0">^1</xliff:g> elementu}other{Eta beste <xliff:g id="COUNT_1">^1</xliff:g> elementu}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Garbitu aplikazioen aldi baterako fitxategiak"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aplikazioak aldi baterako fitxategi batzuk ezabatu nahi ditu. Ondorioz, baliteke bateria edo datu-konexioko datu gehiago erabiltzea."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Aplikazioaren aldi baterako fitxategiak garbitzen…"</string>
<string name="clear" msgid="5524638938415865915">"Garbitu"</string>
<string name="allow" msgid="8885707816848569619">"Eman baimena"</string>
<string name="deny" msgid="6040983710442068936">"Ukatu"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> audio-fitxategiri aldaketak egiteko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
- <item quantity="one">Audio-fitxategi honi aldaketak egiteko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> audio-fitxategi aldatzen…</item>
- <item quantity="one">Audio-fitxategia aldatzen…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> bideori aldaketak egiteko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
- <item quantity="one">Bideo honi aldaketak egiteko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> bideo aldatzen…</item>
- <item quantity="one">Bideoa aldatzen…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> argazkiri aldaketak egiteko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
- <item quantity="one">Argazki honi aldaketak egiteko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> argazki aldatzen…</item>
- <item quantity="one">Argazkia aldatzen…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> elementuri aldaketak egiteko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
- <item quantity="one">Elementu honi aldaketak egiteko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> elementu aldatzen…</item>
- <item quantity="one">Elementua aldatzen…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> audio-fitxategi zaborrontzira eramateko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
- <item quantity="one">Audio-fitxategi hau zaborrontzira eramateko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> audio-fitxategi zaborrontzira eramaten…</item>
- <item quantity="one">Audio-fitxategia zaborrontzira eramaten…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> bideo zaborrontzira eramateko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
- <item quantity="one">Bideo hau zaborrontzira eramateko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> bideo zaborrontzira eramaten…</item>
- <item quantity="one">Bideoa zaborrontzira eramaten…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> argazki zaborrontzira eramateko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
- <item quantity="one">Argazki hau zaborrontzira eramateko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> argazki zaborrontzira eramaten…</item>
- <item quantity="one">Argazkia zaborrontzira eramaten…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> elementu zaborrontzira eramateko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
- <item quantity="one">Elementu hau zaborrontzira eramateko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> elementu zaborrontzira eramaten…</item>
- <item quantity="one">Elementua zaborrontzira eramaten…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> audio-fitxategi zaborrontzitik ateratzeko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
- <item quantity="one">Audio-fitxategi hau zaborrontzitik ateratzeko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> audio-fitxategi zaborrontzitik ateratzen…</item>
- <item quantity="one">Audio-fitxategia zaborrontzitik ateratzen…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> bideo zaborrontzitik ateratzeko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
- <item quantity="one">Bideo hau zaborrontzitik ateratzeko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> bideo zaborrontzitik ateratzen…</item>
- <item quantity="one">Bideoa zaborrontzitik ateratzen…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> argazki zaborrontzitik ateratzeko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
- <item quantity="one">Argazki hau zaborrontzitik ateratzeko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> argazki zaborrontzitik ateratzen…</item>
- <item quantity="one">Argazkia zaborrontzitik ateratzen…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> elementu zaborrontzitik ateratzeko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
- <item quantity="one">Elementu hau zaborrontzitik ateratzeko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> elementu zaborrontzitik ateratzen…</item>
- <item quantity="one">Elementua zaborrontzitik ateratzen…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> audio-fitxategi ezabatzeko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
- <item quantity="one">Audio-fitxategi hau ezabatzeko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> audio-fitxategi ezabatzen…</item>
- <item quantity="one">Audio-fitxategia ezabatzen…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> bideo ezabatzeko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
- <item quantity="one">Bideo hau ezabatzeko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> bideo ezabatzen…</item>
- <item quantity="one">Bideoa ezabatzen…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> argazki ezabatzeko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
- <item quantity="one">Argazki hau ezabatzeko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> argazki ezabatzen…</item>
- <item quantity="one">Argazkia ezabatzen…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> elementu ezabatzeko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
- <item quantity="one">Elementu hau ezabatzeko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> elementu ezabatzen…</item>
- <item quantity="one">Elementua ezabatzen…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Gehitu"</string>
+ <string name="deselect" msgid="4297825044827769490">"Desautatu"</string>
+ <string name="deselected" msgid="8488133193326208475">"Desautatuta"</string>
+ <string name="select" msgid="2704765470563027689">"Hautatu"</string>
+ <string name="selected" msgid="9151797369975828124">"Hautatuta"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Hautatu <xliff:g id="COUNT_0">^1</xliff:g> elementu, gehienez}other{Hautatu <xliff:g id="COUNT_1">^1</xliff:g> elementu, gehienez}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Azkenak"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Ez dago argazki edo bideorik"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Ez dago albumik"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Ikusi hautatutakoak"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Argazkiak"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albumak"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Aurrebista"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Aldatu laneko profilera"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Aldatu profil pertsonalera"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Administratzaileak blokeatu du"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Laneko datuak ezin dira aplikazio pertsonalen bidez atzitu"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Datu pertsonalak ezin dira laneko aplikazioen bidez atzitu"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Pausatuta daude laneko aplikazioak"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Laneko argazkiak irekitzeko, aktibatu laneko aplikazioak eta saiatu berriro"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Zuk hautatutako argazkiak bakarrik atzi ditzake aplikazio honek"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> elementu}other{<xliff:g id="COUNT_1">^1</xliff:g> elementu}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Gehitu (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Deskargak"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Gogokoak"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Pantaila-argazkiak"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Argazki mugimenduduna"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g>)"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"<xliff:g id="TIME">%1$s</xliff:g> datan grabatutako bideoa; <xliff:g id="DURATION">%2$s</xliff:g> irauten du"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Argazkia"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIFa"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Argazki mugimenduduna"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Desaktibatu bideoaren audioa"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Aktibatu bideoaren audioa"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Erreproduzitu bideoa"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Pausatu bideoa"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Hodeiko multimedia edukia <xliff:g id="PKG_NAME">%1$s</xliff:g> bidez atzi daiteke orain"</string>
+ <string name="not_selected" msgid="2244008151669896758">"hautatu gabe"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Audio-fitxategiari aldaketak egiteko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?}other{<xliff:g id="COUNT">^2</xliff:g> audio-fitxategiri aldaketak egiteko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Audio-fitxategia aldatzen…}other{<xliff:g id="COUNT">^1</xliff:g> audio-fitxategi aldatzen…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Bideoari aldaketak egiteko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?}other{<xliff:g id="COUNT">^2</xliff:g> bideori aldaketak egiteko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Bideoa aldatzen…}other{<xliff:g id="COUNT">^1</xliff:g> bideo aldatzen…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Argazkiari aldaketak egiteko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?}other{<xliff:g id="COUNT">^2</xliff:g> argazkiri aldaketak egiteko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Argazkia aldatzen…}other{<xliff:g id="COUNT">^1</xliff:g> argazki aldatzen…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Elementuari aldaketak egiteko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?}other{<xliff:g id="COUNT">^2</xliff:g> elementuri aldaketak egiteko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Elementua aldatzen…}other{<xliff:g id="COUNT">^1</xliff:g> elementu aldatzen…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Audio-fitxategia zaborrontzira eramateko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?}other{<xliff:g id="COUNT">^2</xliff:g> audio-fitxategi zaborrontzira eramateko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Audio-fitxategia zaborrontzira eramaten…}other{<xliff:g id="COUNT">^1</xliff:g> audio-fitxategi zaborrontzira eramaten…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Bideoa zaborrontzira eramateko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?}other{<xliff:g id="COUNT">^2</xliff:g> bideo zaborrontzira eramateko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Bideoa zaborrontzira eramaten…}other{<xliff:g id="COUNT">^1</xliff:g> bideo zaborrontzira eramaten…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Argazkia zaborrontzira eramateko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?}other{<xliff:g id="COUNT">^2</xliff:g> argazki zaborrontzira eramateko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Argazkia zaborrontzira eramaten…}other{<xliff:g id="COUNT">^1</xliff:g> argazki zaborrontzira eramaten…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Elementua zaborrontzira eramateko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?}other{<xliff:g id="COUNT">^2</xliff:g> elementu zaborrontzira eramateko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Elementua zaborrontzira eramaten…}other{<xliff:g id="COUNT">^1</xliff:g> elementu zaborrontzira eramaten…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Audio-fitxategia zaborrontzitik ateratzeko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?}other{<xliff:g id="COUNT">^2</xliff:g> audio-fitxategi zaborrontzitik ateratzeko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Audio-fitxategia zaborrontzitik ateratzen…}other{<xliff:g id="COUNT">^1</xliff:g> audio-fitxategi zaborrontzitik ateratzen…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Bideoa zaborrontzitik ateratzeko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?}other{<xliff:g id="COUNT">^2</xliff:g> bideo zaborrontzitik ateratzeko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Bideoa zaborrontzitik ateratzen…}other{<xliff:g id="COUNT">^1</xliff:g> bideo zaborrontzitik ateratzen…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Argazkia zaborrontzitik ateratzeko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?}other{<xliff:g id="COUNT">^2</xliff:g> argazki zaborrontzitik ateratzeko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Argazkia zaborrontzitik ateratzen…}other{<xliff:g id="COUNT">^1</xliff:g> argazki zaborrontzitik ateratzen…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Elementua zaborrontzitik ateratzeko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?}other{<xliff:g id="COUNT">^2</xliff:g> elementu zaborrontzitik ateratzeko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Elementua zaborrontzitik ateratzen…}other{<xliff:g id="COUNT">^1</xliff:g> elementu zaborrontzitik ateratzen…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Audio-fitxategia ezabatzeko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?}other{<xliff:g id="COUNT">^2</xliff:g> audio-fitxategi ezabatzeko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Audio-fitxategia ezabatzen…}other{<xliff:g id="COUNT">^1</xliff:g> audio-fitxategi ezabatzen…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Bideoa ezabatzeko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?}other{<xliff:g id="COUNT">^2</xliff:g> bideo ezabatzeko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Bideoa ezabatzen…}other{<xliff:g id="COUNT">^1</xliff:g> bideo ezabatzen…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Argazkia ezabatzeko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?}other{<xliff:g id="COUNT">^2</xliff:g> argazki ezabatzeko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Argazkia ezabatzen…}other{<xliff:g id="COUNT">^1</xliff:g> argazki ezabatzen…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Elementua ezabatzeko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?}other{<xliff:g id="COUNT">^2</xliff:g> elementu ezabatzeko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Elementua ezabatzen…}other{<xliff:g id="COUNT">^1</xliff:g> elementu ezabatzen…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> aplikazioak ezin ditu prozesatu multimedia-fitxategiak"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Bertan behera utzi da multimedia-edukiaren prozesamendua"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Errore bat gertatu da multimedia-edukia prozesatzean"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 0006271..7c63184 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"رسانه"</string>
<string name="storage_description" msgid="4081716890357580107">"فضای ذخیرهسازی محلی"</string>
<string name="app_label" msgid="9035307001052716210">"فضای ذخیرهسازی رسانه"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"انتخابگر عکس"</string>
<string name="artist_label" msgid="8105600993099120273">"هنرمند"</string>
<string name="unknown" msgid="2059049215682829375">"نامشخص"</string>
<string name="root_images" msgid="5861633549189045666">"تصویر"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"ادامه"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"اجازه دادن"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"مجاز نبودن"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">بهعلاوه <xliff:g id="COUNT_1">^1</xliff:g> مورد دیگر</item>
- <item quantity="other">بهعلاوه <xliff:g id="COUNT_1">^1</xliff:g> مورد دیگر</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}one{+<xliff:g id="COUNT_1">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{بهعلاوه <xliff:g id="COUNT_0">^1</xliff:g> مورد دیگر}one{بهعلاوه <xliff:g id="COUNT_1">^1</xliff:g> مورد دیگر}other{بهعلاوه <xliff:g id="COUNT_1">^1</xliff:g> مورد دیگر}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"پاک کردن فایلهای موقت برنامه"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> میخواهد برخی از فایلهای موقت را پاک کند. این کار ممکن است استفاده از باتری یا داده شبکه تلفن همراه را افزایش دهد."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"درحال پاک کردن فایلهای موقتی برنامه…"</string>
<string name="clear" msgid="5524638938415865915">"پاک کردن"</string>
<string name="allow" msgid="8885707816848569619">"اجازه دادن"</string>
<string name="deny" msgid="6040983710442068936">"مجاز نبودن"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> فایل صوتی را تغییر دهد؟</item>
- <item quantity="other">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> فایل صوتی را تغییر دهد؟</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one">درحال اصلاح <xliff:g id="COUNT">^1</xliff:g> فایل صوتی…</item>
- <item quantity="other">درحال اصلاح <xliff:g id="COUNT">^1</xliff:g> فایل صوتی…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> ویدیو را تغییر دهد؟</item>
- <item quantity="other">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> ویدیو را تغییر دهد؟</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one">درحال اصلاح <xliff:g id="COUNT">^1</xliff:g> ویدیو…</item>
- <item quantity="other">درحال اصلاح <xliff:g id="COUNT">^1</xliff:g> ویدیو…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> عکس را تغییر دهد؟</item>
- <item quantity="other">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> عکس را تغییر دهد؟</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one">درحال اصلاح <xliff:g id="COUNT">^1</xliff:g> عکس…</item>
- <item quantity="other">درحال اصلاح <xliff:g id="COUNT">^1</xliff:g> عکس…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> مورد را تغییر دهد؟</item>
- <item quantity="other">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> مورد را تغییر دهد؟</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one">درحال اصلاح <xliff:g id="COUNT">^1</xliff:g> مورد…</item>
- <item quantity="other">درحال اصلاح <xliff:g id="COUNT">^1</xliff:g> مورد…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> فایل صوتی را به «حذفشدهها» منتقل کند؟</item>
- <item quantity="other">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> فایل صوتی را به «حذفشدهها» منتقل کند؟</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one">درحال انتقال <xliff:g id="COUNT">^1</xliff:g> فایل صوتی به حذفشدهها…</item>
- <item quantity="other">درحال انتقال <xliff:g id="COUNT">^1</xliff:g> فایل صوتی به حذفشدهها…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> ویدیو را به «حذفشدهها» منتقل کند؟</item>
- <item quantity="other">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> ویدیو را به «حذفشدهها» منتقل کند؟</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one">درحال انتقال <xliff:g id="COUNT">^1</xliff:g> ویدیو به حذفشدهها…</item>
- <item quantity="other">درحال انتقال <xliff:g id="COUNT">^1</xliff:g> ویدیو به حذفشدهها…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> عکس را به «حذفشدهها» منتقل کند؟</item>
- <item quantity="other">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> عکس را به «حذفشدهها» منتقل کند؟</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one">درحال انتقال <xliff:g id="COUNT">^1</xliff:g> عکس به حذفشدهها…</item>
- <item quantity="other">درحال انتقال <xliff:g id="COUNT">^1</xliff:g> عکس به حذفشدهها…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> مورد را به «حذفشدهها» منتقل کند؟</item>
- <item quantity="other">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> مورد را به «حذفشدهها» منتقل کند؟</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one">درحال انتقال <xliff:g id="COUNT">^1</xliff:g> مورد به حذفشدهها…</item>
- <item quantity="other">درحال انتقال <xliff:g id="COUNT">^1</xliff:g> مورد به حذفشدهها…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> فایل صوتی را از «حذفشدهها» خارج کند؟</item>
- <item quantity="other">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> فایل صوتی را از «حذفشدهها» خارج کند؟</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one">درحال خارج کردن <xliff:g id="COUNT">^1</xliff:g> فایل صوتی از حذفشدهها…</item>
- <item quantity="other">درحال خارج کردن <xliff:g id="COUNT">^1</xliff:g> فایل صوتی از حذفشدهها…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> ویدیو را از «حذفشدهها» خارج کند؟</item>
- <item quantity="other">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> ویدیو را از «حذفشدهها» خارج کند؟</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one">درحال خارج کردن <xliff:g id="COUNT">^1</xliff:g> ویدیو از حذفشدهها…</item>
- <item quantity="other">درحال خارج کردن <xliff:g id="COUNT">^1</xliff:g> ویدیو از حذفشدهها…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> عکس را از «حذفشدهها» خارج کند؟</item>
- <item quantity="other">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> عکس را از «حذفشدهها» خارج کند؟</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one">درحال خارج کردن <xliff:g id="COUNT">^1</xliff:g> عکس از حذفشدهها…</item>
- <item quantity="other">درحال خارج کردن <xliff:g id="COUNT">^1</xliff:g> عکس از حذفشدهها…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> مورد را از «حذفشدهها» خارج کند؟</item>
- <item quantity="other">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> مورد را از «حذفشدهها» خارج کند؟</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one">درحال خارج کردن <xliff:g id="COUNT">^1</xliff:g> مورد از حذفشدهها…</item>
- <item quantity="other">درحال خارج کردن <xliff:g id="COUNT">^1</xliff:g> مورد از حذفشدهها…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> فایل صوتی را حذف کند؟</item>
- <item quantity="other">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> فایل صوتی را حذف کند؟</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one">درحال حذف <xliff:g id="COUNT">^1</xliff:g> فایل صوتی…</item>
- <item quantity="other">درحال حذف <xliff:g id="COUNT">^1</xliff:g> فایل صوتی…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> ویدیو را حذف کند؟</item>
- <item quantity="other">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> ویدیو را حذف کند؟</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one">درحال حذف <xliff:g id="COUNT">^1</xliff:g> ویدیو…</item>
- <item quantity="other">درحال حذف <xliff:g id="COUNT">^1</xliff:g> ویدیو…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> عکس را حذف کند؟</item>
- <item quantity="other">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> عکس را حذف کند؟</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one">درحال حذف <xliff:g id="COUNT">^1</xliff:g> عکس…</item>
- <item quantity="other">درحال حذف <xliff:g id="COUNT">^1</xliff:g> عکس…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> مورد را حذف کند؟</item>
- <item quantity="other">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> مورد را حذف کند؟</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one">درحال حذف <xliff:g id="COUNT">^1</xliff:g> مورد…</item>
- <item quantity="other">درحال حذف <xliff:g id="COUNT">^1</xliff:g> مورد…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"افزودن"</string>
+ <string name="deselect" msgid="4297825044827769490">"لغو انتخاب"</string>
+ <string name="deselected" msgid="8488133193326208475">"لغو انتخابشده"</string>
+ <string name="select" msgid="2704765470563027689">"انتخاب"</string>
+ <string name="selected" msgid="9151797369975828124">"انتخابشده"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{حداکثر <xliff:g id="COUNT_0">^1</xliff:g> مورد را انتخاب کنید}one{حداکثر <xliff:g id="COUNT_1">^1</xliff:g> مورد را انتخاب کنید}other{حداکثر <xliff:g id="COUNT_1">^1</xliff:g> مورد را انتخاب کنید}}"</string>
+ <string name="recent" msgid="6694613584743207874">"اخیر"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"عکس یا ویدیویی موجود نیست"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"آلبومی موجود نیست"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"مشاهده موارد انتخابشده"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"عکسها"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"آلبومها"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"پیشنما"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"رفتن به نمایه کاری"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"رفتن به نمایه شخصی"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"سرپرست آن را مسدود کرده است"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"دسترسی به دادههای کاری ازطریق برنامه شخصی مجاز نیست"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"دسترسی به دادههای شخصی ازطریق برنامه کاری مجاز نیست"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"برنامههای کاری موقتاً متوقف شدهاند."</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"برای باز کردن عکسهای کاری، برنامههای کاری را روشن کنید و سپس دوباره امتحان کنید"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"این برنامه فقط میتواند به عکسهای انتخابی شما دسترسی پیدا کند"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> مورد}one{<xliff:g id="COUNT_1">^1</xliff:g> مورد}other{<xliff:g id="COUNT_1">^1</xliff:g> مورد}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"افزودن (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"دوربین"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"بارگیریها"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"موارد دلخواه"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"نماگرفتها"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"عکس حرکتی"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> در <xliff:g id="TIME">%2$s</xliff:g> گرفته شده است"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"ویدیو در <xliff:g id="TIME">%1$s</xliff:g> با مدتزمان <xliff:g id="DURATION">%2$s</xliff:g> گرفته شده است"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"عکس"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"عکس حرکتی"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"بیصدا کردن ویدیو"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"صدادار کردن ویدیو"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"پخش ویدیو"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"مکث ویدیو"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"رسانه ابری اکنون از <xliff:g id="PKG_NAME">%1$s</xliff:g> دردسترس است"</string>
+ <string name="not_selected" msgid="2244008151669896758">"انتخاب نشده است"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{به <xliff:g id="APP_NAME_0">^1</xliff:g> اجازه میدهید این فایل صوتی را تغییر دهد؟}one{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> فایل صوتی را تغییر دهد؟}other{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> فایل صوتی را تغییر دهد؟}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{درحال اصلاح فایل صوتی…}one{درحال اصلاح <xliff:g id="COUNT">^1</xliff:g> فایل صوتی…}other{درحال اصلاح <xliff:g id="COUNT">^1</xliff:g> فایل صوتی…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{به <xliff:g id="APP_NAME_0">^1</xliff:g> اجازه میدهید این ویدیو را تغییر دهد؟}one{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> ویدیو را تغییر دهد؟}other{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> ویدیو را تغییر دهد؟}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{درحال اصلاح ویدیو…}one{درحال اصلاح <xliff:g id="COUNT">^1</xliff:g> ویدیو…}other{درحال اصلاح <xliff:g id="COUNT">^1</xliff:g> ویدیو…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{به <xliff:g id="APP_NAME_0">^1</xliff:g> اجازه میدهید این عکس را تغییر دهد؟}one{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> عکس را تغییر دهد؟}other{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> عکس را تغییر دهد؟}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{درحال اصلاح عکس…}one{درحال اصلاح <xliff:g id="COUNT">^1</xliff:g> عکس…}other{درحال اصلاح <xliff:g id="COUNT">^1</xliff:g> عکس…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{به <xliff:g id="APP_NAME_0">^1</xliff:g> اجازه میدهید این مورد را تغییر دهد؟}one{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> مورد را تغییر دهد؟}other{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> مورد را تغییر دهد؟}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{درحال اصلاح مورد…}one{درحال اصلاح <xliff:g id="COUNT">^1</xliff:g> مورد…}other{درحال اصلاح <xliff:g id="COUNT">^1</xliff:g> مورد…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{به <xliff:g id="APP_NAME_0">^1</xliff:g> اجازه میدهید این فایل صوتی را به حذفشدهها منتقل کند؟}one{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> فایل صوتی را به حذفشدهها منتقل کند؟}other{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> فایل صوتی را به حذفشدهها منتقل کند؟}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{درحال انتقال فایل صوتی به حذفشدهها…}one{درحال انتقال <xliff:g id="COUNT">^1</xliff:g> فایل صوتی به حذفشدهها…}other{درحال انتقال <xliff:g id="COUNT">^1</xliff:g> فایل صوتی به حذفشدهها…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{به <xliff:g id="APP_NAME_0">^1</xliff:g> اجازه میدهید این ویدیو را به حذفشدهها منتقل کند؟}one{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> ویدیو را به حذفشدهها منتقل کند؟}other{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> ویدیو را به حذفشدهها منتقل کند؟}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{درحال انتقال ویدیو به حذفشدهها…}one{درحال انتقال <xliff:g id="COUNT">^1</xliff:g> ویدیو به حذفشدهها…}other{درحال انتقال <xliff:g id="COUNT">^1</xliff:g> ویدیو به حذفشدهها…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{به <xliff:g id="APP_NAME_0">^1</xliff:g> اجازه میدهید این عکس را به حذفشدهها منتقل کند؟}one{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> عکس را به حذفشدهها منتقل کند؟}other{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> عکس را به حذفشدهها منتقل کند؟}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{درحال انتقال عکس به حذفشدهها…}one{درحال انتقال <xliff:g id="COUNT">^1</xliff:g> عکس به حذفشدهها…}other{درحال انتقال <xliff:g id="COUNT">^1</xliff:g> عکس به حذفشدهها…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{به <xliff:g id="APP_NAME_0">^1</xliff:g> اجازه میدهید این مورد را به حذفشدهها منتقل کند؟}one{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> مورد را به حذفشدهها منتقل کند؟}other{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> مورد را به حذفشدهها منتقل کند؟}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{درحال انتقال مورد به حذفشدهها…}one{درحال انتقال <xliff:g id="COUNT">^1</xliff:g> مورد به حذفشدهها…}other{درحال انتقال <xliff:g id="COUNT">^1</xliff:g> مورد به حذفشدهها…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{به <xliff:g id="APP_NAME_0">^1</xliff:g> اجازه میدهید این فایل صوتی را از حذفشدهها خارج کند؟}one{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> فایل صوتی را از حذفشدهها خارج کند؟}other{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> فایل صوتی را از حذفشدهها خارج کند؟}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{درحال خارج کردن فایل صوتی از حذفشدهها…}one{درحال خارج کردن <xliff:g id="COUNT">^1</xliff:g> فایل صوتی از حذفشدهها…}other{درحال خارج کردن <xliff:g id="COUNT">^1</xliff:g> فایل صوتی از حذفشدهها…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{به <xliff:g id="APP_NAME_0">^1</xliff:g> اجازه میدهید این ویدیو را از حذفشدهها خارج کند؟}one{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> ویدیو را از حذفشدهها خارج کند؟}other{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> ویدیو را از حذفشدهها خارج کند؟}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{درحال خارج کردن ویدیو از حذفشدهها…}one{درحال خارج کردن <xliff:g id="COUNT">^1</xliff:g> ویدیو از حذفشدهها…}other{درحال خارج کردن <xliff:g id="COUNT">^1</xliff:g> ویدیو از حذفشدهها…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{به <xliff:g id="APP_NAME_0">^1</xliff:g> اجازه میدهید این عکس را از حذفشدهها خارج کند؟}one{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> عکس را از حذفشدهها خارج کند؟}other{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> عکس را از حذفشدهها خارج کند؟}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{درحال خارج کردن عکس از حذفشدهها…}one{درحال خارج کردن <xliff:g id="COUNT">^1</xliff:g> عکس از حذفشدهها…}other{درحال خارج کردن <xliff:g id="COUNT">^1</xliff:g> عکس از حذفشدهها…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{به <xliff:g id="APP_NAME_0">^1</xliff:g> اجازه میدهید این مورد را از حذفشدهها خارج کند؟}one{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> مورد را از حذفشدهها خارج کند؟}other{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> مورد را از حذفشدهها خارج کند؟}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{درحال خارج کردن مورد از حذفشدهها…}one{درحال خارج کردن <xliff:g id="COUNT">^1</xliff:g> مورد از حذفشدهها…}other{درحال خارج کردن <xliff:g id="COUNT">^1</xliff:g> مورد از حذفشدهها…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{به <xliff:g id="APP_NAME_0">^1</xliff:g> اجازه میدهید این فایل صوتی را حذف کند؟}one{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> فایل صوتی را حذف کند؟}other{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> فایل صوتی را حذف کند؟}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{درحال حذف فایل صوتی…}one{درحال حذف <xliff:g id="COUNT">^1</xliff:g> فایل صوتی…}other{درحال حذف <xliff:g id="COUNT">^1</xliff:g> فایل صوتی…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{به <xliff:g id="APP_NAME_0">^1</xliff:g> اجازه میدهید این ویدیو را حذف کند؟}one{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> ویدیو را حذف کند؟}other{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> ویدیو را حذف کند؟}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{درحال حذف ویدیو…}one{درحال حذف <xliff:g id="COUNT">^1</xliff:g> ویدیو…}other{درحال حذف <xliff:g id="COUNT">^1</xliff:g> ویدیو…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{به <xliff:g id="APP_NAME_0">^1</xliff:g> اجازه میدهید این عکس را حذف کند؟}one{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> عکس را حذف کند؟}other{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> عکس را حذف کند؟}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{درحال حذف عکس…}one{درحال حذف <xliff:g id="COUNT">^1</xliff:g> عکس…}other{درحال حذف <xliff:g id="COUNT">^1</xliff:g> عکس…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{به <xliff:g id="APP_NAME_0">^1</xliff:g> اجازه میدهید این مورد را حذف کند؟}one{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> مورد را حذف کند؟}other{به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> مورد را حذف کند؟}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{درحال حذف مورد…}one{درحال حذف <xliff:g id="COUNT">^1</xliff:g> مورد…}other{درحال حذف <xliff:g id="COUNT">^1</xliff:g> مورد…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> نمیتواند فایلهای رسانهای را پردازش کند"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"پردازش رسانه لغو شد"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"خطای پردازش رسانه"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 9e65ae7..734f960 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Kuvien valitsin"</string>
<string name="artist_label" msgid="8105600993099120273">"Artisti"</string>
<string name="unknown" msgid="2059049215682829375">"Tuntematon"</string>
<string name="root_images" msgid="5861633549189045666">"Kuvat"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Jatka"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Salli"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Estä"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Ja <xliff:g id="COUNT_1">^1</xliff:g> muuta asiaa</item>
- <item quantity="one">Ja <xliff:g id="COUNT_0">^1</xliff:g> muu asia</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> muu}other{<xliff:g id="COUNT_1">^1</xliff:g> muuta}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Vielä <xliff:g id="COUNT_0">^1</xliff:g> muu kohde}other{Vielä <xliff:g id="COUNT_1">^1</xliff:g> muuta kohdetta}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Poista väliaikaisia sovellustiedostoja"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> haluaa poistaa joitain väliaikaisia tiedostoja. Tämä voi lisätä akun tai mobiilidatan käyttöä."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Poistetaan väliaikaisia sovellustiedostoja…"</string>
<string name="clear" msgid="5524638938415865915">"Poista"</string>
<string name="allow" msgid="8885707816848569619">"Salli"</string>
<string name="deny" msgid="6040983710442068936">"Estä"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">Saako <xliff:g id="APP_NAME_1">^1</xliff:g> muokata <xliff:g id="COUNT">^2</xliff:g> audiotiedostoa?</item>
- <item quantity="one">Saako <xliff:g id="APP_NAME_0">^1</xliff:g> muokata tätä audiotiedostoa?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">Muokataan <xliff:g id="COUNT">^1</xliff:g> audiotiedostoa…</item>
- <item quantity="one">Muokataan audiotiedostoa…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">Saako <xliff:g id="APP_NAME_1">^1</xliff:g> muokata <xliff:g id="COUNT">^2</xliff:g> videota?</item>
- <item quantity="one">Saako <xliff:g id="APP_NAME_0">^1</xliff:g> muokata tätä videota?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">Muokataan <xliff:g id="COUNT">^1</xliff:g> videota…</item>
- <item quantity="one">Muokataan videota…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">Saako <xliff:g id="APP_NAME_1">^1</xliff:g> muokata <xliff:g id="COUNT">^2</xliff:g> kuvaa?</item>
- <item quantity="one">Saako <xliff:g id="APP_NAME_0">^1</xliff:g> muokata tätä kuvaa?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">Muokataan <xliff:g id="COUNT">^1</xliff:g> valokuvaa…</item>
- <item quantity="one">Muokataan valokuvaa…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">Saako <xliff:g id="APP_NAME_1">^1</xliff:g> muokata <xliff:g id="COUNT">^2</xliff:g> kohdetta?</item>
- <item quantity="one">Saako <xliff:g id="APP_NAME_0">^1</xliff:g> muokata tätä?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">Muokataan <xliff:g id="COUNT">^1</xliff:g> kohdetta…</item>
- <item quantity="one">Muokataan kohdetta…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">Saako <xliff:g id="APP_NAME_1">^1</xliff:g> siirtää <xliff:g id="COUNT">^2</xliff:g> audiotiedostoa roskakoriin?</item>
- <item quantity="one">Saako <xliff:g id="APP_NAME_0">^1</xliff:g> siirtää tämän audiotiedoston roskakoriin?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">Siirretään <xliff:g id="COUNT">^1</xliff:g> audiotiedostoa roskakoriin…</item>
- <item quantity="one">Siirretään audiotiedostoa roskakoriin…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">Saako <xliff:g id="APP_NAME_1">^1</xliff:g> siirtää <xliff:g id="COUNT">^2</xliff:g> videota roskakoriin?</item>
- <item quantity="one">Saako <xliff:g id="APP_NAME_0">^1</xliff:g> siirtää tämän videon roskakoriin?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">Siirretään <xliff:g id="COUNT">^1</xliff:g> videota roskakoriin…</item>
- <item quantity="one">Siirretään videota roskakoriin…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">Saako <xliff:g id="APP_NAME_1">^1</xliff:g> siirtää <xliff:g id="COUNT">^2</xliff:g> kuvaa roskakoriin?</item>
- <item quantity="one">Saako <xliff:g id="APP_NAME_0">^1</xliff:g> siirtää tämän kuvan roskakoriin?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">Siirretään <xliff:g id="COUNT">^1</xliff:g> valokuvaa roskakoriin…</item>
- <item quantity="one">Siirretään valokuvaa roskakoriin…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">Saako <xliff:g id="APP_NAME_1">^1</xliff:g> siirtää <xliff:g id="COUNT">^2</xliff:g> kohdetta roskakoriin?</item>
- <item quantity="one">Saako <xliff:g id="APP_NAME_0">^1</xliff:g> siirtää tämän roskakoriin?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">Siirretään <xliff:g id="COUNT">^1</xliff:g> kohdetta roskakoriin…</item>
- <item quantity="one">Siirretään kohdetta roskakoriin…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">Saako <xliff:g id="APP_NAME_1">^1</xliff:g> ottaa <xliff:g id="COUNT">^2</xliff:g> audiotiedostoa pois roskakorista?</item>
- <item quantity="one">Saako <xliff:g id="APP_NAME_0">^1</xliff:g> ottaa tämän audiotiedoston pois roskakorista?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">Siirretään <xliff:g id="COUNT">^1</xliff:g> audiotiedostoa pois roskakorista…</item>
- <item quantity="one">Siirretään audiotiedostoa pois roskakorista…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">Saako <xliff:g id="APP_NAME_1">^1</xliff:g> ottaa <xliff:g id="COUNT">^2</xliff:g> videota pois roskakorista?</item>
- <item quantity="one">Saako <xliff:g id="APP_NAME_0">^1</xliff:g> ottaa tämän videon pois roskakorista?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">Siirretään <xliff:g id="COUNT">^1</xliff:g> videota pois roskakorista…</item>
- <item quantity="one">Siirretään videota pois roskakorista…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">Saako <xliff:g id="APP_NAME_1">^1</xliff:g> ottaa <xliff:g id="COUNT">^2</xliff:g> kuvaa pois roskakorista?</item>
- <item quantity="one">Saako <xliff:g id="APP_NAME_0">^1</xliff:g> ottaa tämän kuvan pois roskakorista?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">Siirretään <xliff:g id="COUNT">^1</xliff:g> valokuvaa pois roskakorista…</item>
- <item quantity="one">Siirretään valokuvaa pois roskakorista…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">Saako <xliff:g id="APP_NAME_1">^1</xliff:g> ottaa <xliff:g id="COUNT">^2</xliff:g> kohdetta pois roskakorista?</item>
- <item quantity="one">Saako <xliff:g id="APP_NAME_0">^1</xliff:g> ottaa tämän pois roskakorista?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">Siirretään <xliff:g id="COUNT">^1</xliff:g> kohdetta pois roskakorista…</item>
- <item quantity="one">Siirretään kohdetta pois roskakorista…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">Saako <xliff:g id="APP_NAME_1">^1</xliff:g> poistaa <xliff:g id="COUNT">^2</xliff:g> audiotiedostoa?</item>
- <item quantity="one">Saako <xliff:g id="APP_NAME_0">^1</xliff:g> poistaa tämän audiotiedoston?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">Poistetaan <xliff:g id="COUNT">^1</xliff:g> audiotiedostoa…</item>
- <item quantity="one">Poistetaan audiotiedostoa…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">Saako <xliff:g id="APP_NAME_1">^1</xliff:g> poistaa <xliff:g id="COUNT">^2</xliff:g> videota?</item>
- <item quantity="one">Saako <xliff:g id="APP_NAME_0">^1</xliff:g> poistaa tämän videon?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">Poistetaan <xliff:g id="COUNT">^1</xliff:g> videota…</item>
- <item quantity="one">Poistetaan videota…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">Saako <xliff:g id="APP_NAME_1">^1</xliff:g> poistaa <xliff:g id="COUNT">^2</xliff:g> kuvaa?</item>
- <item quantity="one">Saako <xliff:g id="APP_NAME_0">^1</xliff:g> poistaa tämän kuvan?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">Poistetaan <xliff:g id="COUNT">^1</xliff:g> valokuvaa…</item>
- <item quantity="one">Poistetaan valokuvaa…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">Saako <xliff:g id="APP_NAME_1">^1</xliff:g> poistaa <xliff:g id="COUNT">^2</xliff:g> kohdetta?</item>
- <item quantity="one">Saako <xliff:g id="APP_NAME_0">^1</xliff:g> poistaa tämän?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">Poistetaan <xliff:g id="COUNT">^1</xliff:g> kohdetta…</item>
- <item quantity="one">Poistetaan kohdetta…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Lisää"</string>
+ <string name="deselect" msgid="4297825044827769490">"Poista valinta"</string>
+ <string name="deselected" msgid="8488133193326208475">"Valinta poistettu"</string>
+ <string name="select" msgid="2704765470563027689">"Valitse"</string>
+ <string name="selected" msgid="9151797369975828124">"Valittu"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Valitse enintään <xliff:g id="COUNT_0">^1</xliff:g> kohde}other{Valitse enintään <xliff:g id="COUNT_1">^1</xliff:g> kohdetta}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Viimeisimmät"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Ei kuvia tai videoita"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Ei albumeita"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Katso valitut"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Kuvat"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albumit"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Esikatselu"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Siirry työprofiiliin"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Siirry henkilökohtaiseen profiiliin"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Järjestelmänvalvojasi estämä"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Työdataan pääsy ei ole sallittu henkilökohtaisen sovelluksen kautta"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Henkilökohtaiseen dataan pääsy ei ole sallittu työsovelluksen kautta"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Työsovellukset on keskeytetty"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Jos haluat avata työkuvasi, laita työsovellukset päälle ja yritä uudelleen"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Sovelluksella on pääsy vain valitsemiisi kuviin"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> kohde}other{<xliff:g id="COUNT_1">^1</xliff:g> kohdetta}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Lisää (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Lataukset"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Suosikit"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Kuvakaappaukset"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Liikkuva kuva"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> on otettu <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Video kuvattu <xliff:g id="TIME">%1$s</xliff:g>, kesto <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Kuva"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Liikkuva kuva"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Mykistä video"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Poista videon mykistys"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Toista video"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Keskeytä video"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Pilvimediaa nyt saatavilla täältä: <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"ei valittu"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Saako <xliff:g id="APP_NAME_0">^1</xliff:g> muokata tätä audiotiedostoa?}other{Saako <xliff:g id="APP_NAME_1">^1</xliff:g> muokata <xliff:g id="COUNT">^2</xliff:g> audiotiedostoa?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Muokataan audiotiedostoa…}other{Muokataan <xliff:g id="COUNT">^1</xliff:g> audiotiedostoa…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Saako <xliff:g id="APP_NAME_0">^1</xliff:g> muokata tätä videota?}other{Saako <xliff:g id="APP_NAME_1">^1</xliff:g> muokata <xliff:g id="COUNT">^2</xliff:g> videota?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Muokataan videota…}other{Muokataan <xliff:g id="COUNT">^1</xliff:g> videota…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Saako <xliff:g id="APP_NAME_0">^1</xliff:g> muokata tätä kuvaa?}other{Saako <xliff:g id="APP_NAME_1">^1</xliff:g> muokata <xliff:g id="COUNT">^2</xliff:g> kuvaa?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Muokataan valokuvaa…}other{Muokataan <xliff:g id="COUNT">^1</xliff:g> valokuvaa…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Saako <xliff:g id="APP_NAME_0">^1</xliff:g> muokata tätä?}other{Saako <xliff:g id="APP_NAME_1">^1</xliff:g> muokata <xliff:g id="COUNT">^2</xliff:g> kohdetta?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Muokataan kohdetta…}other{Muokataan <xliff:g id="COUNT">^1</xliff:g> kohdetta…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Saako <xliff:g id="APP_NAME_0">^1</xliff:g> siirtää tämän audiotiedoston roskakoriin?}other{Saako <xliff:g id="APP_NAME_1">^1</xliff:g> siirtää <xliff:g id="COUNT">^2</xliff:g> audiotiedostoa roskakoriin?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Siirretään audiotiedostoa roskakoriin…}other{Siirretään <xliff:g id="COUNT">^1</xliff:g> audiotiedostoa roskakoriin…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Saako <xliff:g id="APP_NAME_0">^1</xliff:g> siirtää tämän videon roskakoriin?}other{Saako <xliff:g id="APP_NAME_1">^1</xliff:g> siirtää <xliff:g id="COUNT">^2</xliff:g> videota roskakoriin?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Siirretään videota roskakoriin…}other{Siirretään <xliff:g id="COUNT">^1</xliff:g> videota roskakoriin…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Saako <xliff:g id="APP_NAME_0">^1</xliff:g> siirtää tämän kuvan roskakoriin?}other{Saako <xliff:g id="APP_NAME_1">^1</xliff:g> siirtää <xliff:g id="COUNT">^2</xliff:g> kuvaa roskakoriin?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Siirretään valokuvaa roskakoriin…}other{Siirretään <xliff:g id="COUNT">^1</xliff:g> valokuvaa roskakoriin…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Saako <xliff:g id="APP_NAME_0">^1</xliff:g> siirtää tämän roskakoriin?}other{Saako <xliff:g id="APP_NAME_1">^1</xliff:g> siirtää <xliff:g id="COUNT">^2</xliff:g> kohdetta roskakoriin?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Siirretään kohdetta roskakoriin…}other{Siirretään <xliff:g id="COUNT">^1</xliff:g> kohdetta roskakoriin…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Saako <xliff:g id="APP_NAME_0">^1</xliff:g> ottaa tämän audiotiedoston pois roskakorista?}other{Saako <xliff:g id="APP_NAME_1">^1</xliff:g> ottaa <xliff:g id="COUNT">^2</xliff:g> audiotiedostoa pois roskakorista?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Siirretään audiotiedostoa pois roskakorista…}other{Siirretään <xliff:g id="COUNT">^1</xliff:g> audiotiedostoa pois roskakorista…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Saako <xliff:g id="APP_NAME_0">^1</xliff:g> ottaa tämän videon pois roskakorista?}other{Saako <xliff:g id="APP_NAME_1">^1</xliff:g> ottaa <xliff:g id="COUNT">^2</xliff:g> videota pois roskakorista?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Siirretään videota pois roskakorista…}other{Siirretään <xliff:g id="COUNT">^1</xliff:g> videota pois roskakorista…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Saako <xliff:g id="APP_NAME_0">^1</xliff:g> ottaa tämän kuvan pois roskakorista?}other{Saako <xliff:g id="APP_NAME_1">^1</xliff:g> ottaa <xliff:g id="COUNT">^2</xliff:g> kuvaa pois roskakorista?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Siirretään valokuvaa pois roskakorista…}other{Siirretään <xliff:g id="COUNT">^1</xliff:g> valokuvaa pois roskakorista…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Saako <xliff:g id="APP_NAME_0">^1</xliff:g> ottaa tämän pois roskakorista?}other{Saako <xliff:g id="APP_NAME_1">^1</xliff:g> ottaa <xliff:g id="COUNT">^2</xliff:g> kohdetta pois roskakorista?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Siirretään kohdetta pois roskakorista…}other{Siirretään <xliff:g id="COUNT">^1</xliff:g> kohdetta pois roskakorista…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Saako <xliff:g id="APP_NAME_0">^1</xliff:g> poistaa tämän audiotiedoston?}other{Saako <xliff:g id="APP_NAME_1">^1</xliff:g> poistaa <xliff:g id="COUNT">^2</xliff:g> audiotiedostoa?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Poistetaan audiotiedostoa…}other{Poistetaan <xliff:g id="COUNT">^1</xliff:g> audiotiedostoa…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Saako <xliff:g id="APP_NAME_0">^1</xliff:g> poistaa tämän videon?}other{Saako <xliff:g id="APP_NAME_1">^1</xliff:g> poistaa <xliff:g id="COUNT">^2</xliff:g> videota?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Poistetaan videota…}other{Poistetaan <xliff:g id="COUNT">^1</xliff:g> videota…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Saako <xliff:g id="APP_NAME_0">^1</xliff:g> poistaa tämän kuvan?}other{Saako <xliff:g id="APP_NAME_1">^1</xliff:g> poistaa <xliff:g id="COUNT">^2</xliff:g> kuvaa?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Poistetaan valokuvaa…}other{Poistetaan <xliff:g id="COUNT">^1</xliff:g> valokuvaa…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Saako <xliff:g id="APP_NAME_0">^1</xliff:g> poistaa tämän?}other{Saako <xliff:g id="APP_NAME_1">^1</xliff:g> poistaa <xliff:g id="COUNT">^2</xliff:g> kohdetta?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Poistetaan kohdetta…}other{Poistetaan <xliff:g id="COUNT">^1</xliff:g> kohdetta…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> ei voi käsitellä mediatiedostoja"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Mediasisällön käsittely peruttiin"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Virhe mediasisällön käsittelyssä"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index de7245c..6534709 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Sélecteur de photos"</string>
<string name="artist_label" msgid="8105600993099120273">"Artiste"</string>
<string name="unknown" msgid="2059049215682829375">"Inconnu"</string>
<string name="root_images" msgid="5861633549189045666">"Images"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Continuer"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Autoriser"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Refuser"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">Plus <xliff:g id="COUNT_1">^1</xliff:g> élément supplémentaire</item>
- <item quantity="other">Plus <xliff:g id="COUNT_1">^1</xliff:g> éléments supplémentaires</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}one{+<xliff:g id="COUNT_1">^1</xliff:g>}many{+<xliff:g id="COUNT_1">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Plus <xliff:g id="COUNT_0">^1</xliff:g> élément supplémentaire}one{Plus <xliff:g id="COUNT_1">^1</xliff:g> élément supplémentaire}many{Plus <xliff:g id="COUNT_1">^1</xliff:g> éléments supplémentaires}other{Plus <xliff:g id="COUNT_1">^1</xliff:g> éléments supplémentaires}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Effacer les fichiers temporaires des applications"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aimerait supprimer certains fichiers temporaires. Ceci peut entraîner une utilisation accrue de la pile ou des données cellulaires."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Suppression des fichiers temporaires des applications en cours…"</string>
<string name="clear" msgid="5524638938415865915">"Effacer"</string>
<string name="allow" msgid="8885707816848569619">"Autoriser"</string>
<string name="deny" msgid="6040983710442068936">"Refuser"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> fichier audio?</item>
- <item quantity="other">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> fichiers audio?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one">Modification de <xliff:g id="COUNT">^1</xliff:g> fichier audio en cours…</item>
- <item quantity="other">Modification de <xliff:g id="COUNT">^1</xliff:g> fichiers audio en cours…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> vidéo?</item>
- <item quantity="other">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> vidéos?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one">Modification de <xliff:g id="COUNT">^1</xliff:g> vidéo en cours…</item>
- <item quantity="other">Modification de <xliff:g id="COUNT">^1</xliff:g> vidéos en cours…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> photo?</item>
- <item quantity="other">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> photos?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one">Modification de <xliff:g id="COUNT">^1</xliff:g> photo en cours…</item>
- <item quantity="other">Modification de <xliff:g id="COUNT">^1</xliff:g> photos en cours…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> élément?</item>
- <item quantity="other">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> éléments?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one">Modification de <xliff:g id="COUNT">^1</xliff:g> élément en cours…</item>
- <item quantity="other">Modification de <xliff:g id="COUNT">^1</xliff:g> éléments en cours…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à déplacer <xliff:g id="COUNT">^2</xliff:g> fichier audio dans la corbeille?</item>
- <item quantity="other">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à déplacer <xliff:g id="COUNT">^2</xliff:g> fichiers audio dans la corbeille?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one">Déplacement de <xliff:g id="COUNT">^1</xliff:g> fichier audio vers la corbeille en cours…</item>
- <item quantity="other">Déplacement de <xliff:g id="COUNT">^1</xliff:g> fichiers audio vers la corbeille en cours…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à déplacer <xliff:g id="COUNT">^2</xliff:g> vidéo dans la corbeille?</item>
- <item quantity="other">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à déplacer <xliff:g id="COUNT">^2</xliff:g> vidéos dans la corbeille?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one">Déplacement de <xliff:g id="COUNT">^1</xliff:g> vidéo vers la corbeille en cours…</item>
- <item quantity="other">Déplacement de <xliff:g id="COUNT">^1</xliff:g> vidéos vers la corbeille en cours…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à déplacer <xliff:g id="COUNT">^2</xliff:g> photo dans la corbeille?</item>
- <item quantity="other">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à déplacer <xliff:g id="COUNT">^2</xliff:g> photos dans la corbeille?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one">Déplacement de <xliff:g id="COUNT">^1</xliff:g> photo vers la corbeille en cours…</item>
- <item quantity="other">Déplacement de <xliff:g id="COUNT">^1</xliff:g> photos vers la corbeille en cours…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à déplacer <xliff:g id="COUNT">^2</xliff:g> élément dans la corbeille?</item>
- <item quantity="other">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à déplacer <xliff:g id="COUNT">^2</xliff:g> éléments dans la corbeille?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one">Déplacement de <xliff:g id="COUNT">^1</xliff:g> élément vers la corbeille en cours…</item>
- <item quantity="other">Déplacement de <xliff:g id="COUNT">^1</xliff:g> éléments vers la corbeille en cours…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à restaurer <xliff:g id="COUNT">^2</xliff:g> fichier audio de la corbeille?</item>
- <item quantity="other">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à restaurer <xliff:g id="COUNT">^2</xliff:g> fichiers audio de la corbeille?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one">Restauration de <xliff:g id="COUNT">^1</xliff:g> fichier audio de la corbeille en cours…</item>
- <item quantity="other">Restauration de <xliff:g id="COUNT">^1</xliff:g> fichiers audio de la corbeille en cours…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à restaurer <xliff:g id="COUNT">^2</xliff:g> vidéo de la corbeille?</item>
- <item quantity="other">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à restaurer <xliff:g id="COUNT">^2</xliff:g> vidéos de la corbeille?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one">Restauration de <xliff:g id="COUNT">^1</xliff:g> vidéo de la corbeille en cours…</item>
- <item quantity="other">Restauration de <xliff:g id="COUNT">^1</xliff:g> vidéos de la corbeille en cours…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à restaurer <xliff:g id="COUNT">^2</xliff:g> photo de la corbeille?</item>
- <item quantity="other">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à restaurer <xliff:g id="COUNT">^2</xliff:g> photos de la corbeille?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one">Restauration de <xliff:g id="COUNT">^1</xliff:g> photo de la corbeille en cours…</item>
- <item quantity="other">Restauration de <xliff:g id="COUNT">^1</xliff:g> photos de la corbeille en cours…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à restaurer <xliff:g id="COUNT">^2</xliff:g> élément de la corbeille?</item>
- <item quantity="other">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à restaurer <xliff:g id="COUNT">^2</xliff:g> éléments de la corbeille?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one">Restauration de <xliff:g id="COUNT">^1</xliff:g> élément de la corbeille en cours…</item>
- <item quantity="other">Restauration de <xliff:g id="COUNT">^1</xliff:g> éléments de la corbeille en cours…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> fichier audio?</item>
- <item quantity="other">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> fichiers audio?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one">Suppression de <xliff:g id="COUNT">^1</xliff:g> fichier audio en cours…</item>
- <item quantity="other">Suppression de <xliff:g id="COUNT">^1</xliff:g> fichiers audio en cours…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> vidéo?</item>
- <item quantity="other">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> vidéos?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one">Suppression de <xliff:g id="COUNT">^1</xliff:g> vidéo en cours…</item>
- <item quantity="other">Suppression de <xliff:g id="COUNT">^1</xliff:g> vidéos en cours…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> photo?</item>
- <item quantity="other">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> photos?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one">Suppression de <xliff:g id="COUNT">^1</xliff:g> photo en cours…</item>
- <item quantity="other">Suppression de <xliff:g id="COUNT">^1</xliff:g> photos en cours…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> élément?</item>
- <item quantity="other">Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> éléments?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one">Suppression de <xliff:g id="COUNT">^1</xliff:g> élément en cours…</item>
- <item quantity="other">Suppression de <xliff:g id="COUNT">^1</xliff:g> éléments en cours…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Ajouter"</string>
+ <string name="deselect" msgid="4297825044827769490">"Désélectionner"</string>
+ <string name="deselected" msgid="8488133193326208475">"Désélectionné"</string>
+ <string name="select" msgid="2704765470563027689">"Sélectionner"</string>
+ <string name="selected" msgid="9151797369975828124">"Sélectionné"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Sélectionnez jusqu\'à <xliff:g id="COUNT_0">^1</xliff:g> élément}one{Sélectionnez jusqu\'à <xliff:g id="COUNT_1">^1</xliff:g> élément}many{Sélectionnez jusqu\'à <xliff:g id="COUNT_1">^1</xliff:g> éléments}other{Sélectionnez jusqu\'à <xliff:g id="COUNT_1">^1</xliff:g> éléments}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Récentes"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Aucune photo ni aucune vidéo"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Aucun album"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Afficher la sélection"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Photos"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albums"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Aperçu"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Passez au profil professionnel"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Passez au profil personnel"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Bloqué par votre administrateur"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Il est interdit d\'accéder aux données professionnelles à partir d\'une application personnelle"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Il est interdit d\'accéder aux données personnelles à partir d\'une application professionnelle"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Les applications professionnelles sont interrompues"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Pour ouvrir des photos professionnelles, activez vos applications professionnelles, puis réessayez"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Cette application ne peut accéder qu\'aux photos que vous sélectionnez"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> élément}one{<xliff:g id="COUNT_1">^1</xliff:g> élément}many{<xliff:g id="COUNT_1">^1</xliff:g> éléments}other{<xliff:g id="COUNT_1">^1</xliff:g> éléments}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Ajouter (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Appareil photo"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Téléchargements"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Favoris"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Captures d\'écran"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Photo animée"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> prise le <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Vidéo prise le <xliff:g id="TIME">%1$s</xliff:g> d\'une durée de <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Photo"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Photo animée"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Désactivez le son de la vidéo"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Réactivez le son de la vidéo"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Faites jouer la vidéo"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Suspendez la vidéo"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Le contenu multimédia dans le nuage est maintenant offert par <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"non sélectionné"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à modifier ce fichier audio?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> fichier audio?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> fichiers audio?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> fichiers audio?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Modification du fichier audio en cours…}one{Modification de <xliff:g id="COUNT">^1</xliff:g> fichier audio en cours…}many{Modification de <xliff:g id="COUNT">^1</xliff:g> fichiers audio en cours…}other{Modification de <xliff:g id="COUNT">^1</xliff:g> fichiers audio en cours…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à modifier cette vidéo?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> vidéo?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> vidéos?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> vidéos?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Modification de la vidéo en cours…}one{Modification de <xliff:g id="COUNT">^1</xliff:g> vidéo en cours…}many{Modification de <xliff:g id="COUNT">^1</xliff:g> vidéos en cours…}other{Modification de <xliff:g id="COUNT">^1</xliff:g> vidéos en cours…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à modifier cette photo?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> photo?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> photos?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> photos?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Modification de la photo en cours…}one{Modification de <xliff:g id="COUNT">^1</xliff:g> photo en cours…}many{Modification de <xliff:g id="COUNT">^1</xliff:g> photos en cours…}other{Modification de <xliff:g id="COUNT">^1</xliff:g> photos en cours…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à modifier cet élément?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> élément?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> éléments?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> éléments?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Modification de l\'élément en cours…}one{Modification de <xliff:g id="COUNT">^1</xliff:g> élément en cours…}many{Modification de <xliff:g id="COUNT">^1</xliff:g> éléments en cours…}other{Modification de <xliff:g id="COUNT">^1</xliff:g> éléments en cours…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à déplacer ce fichier audio vers la corbeille?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à déplacer <xliff:g id="COUNT">^2</xliff:g> fichier audio vers la corbeille?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à déplacer <xliff:g id="COUNT">^2</xliff:g> fichiers audio vers la corbeille?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à déplacer <xliff:g id="COUNT">^2</xliff:g> fichiers audio vers la corbeille?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Déplacement du fichier audio vers la corbeille en cours…}one{Déplacement de <xliff:g id="COUNT">^1</xliff:g> fichier audio vers la corbeille en cours…}many{Déplacement de <xliff:g id="COUNT">^1</xliff:g> fichiers audio vers la corbeille en cours…}other{Déplacement de <xliff:g id="COUNT">^1</xliff:g> fichiers audio vers la corbeille en cours…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à déplacer cette vidéo vers la corbeille?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à déplacer <xliff:g id="COUNT">^2</xliff:g> vidéo vers la corbeille?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à déplacer <xliff:g id="COUNT">^2</xliff:g> vidéos vers la corbeille?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à déplacer <xliff:g id="COUNT">^2</xliff:g> vidéos vers la corbeille?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Déplacement de la vidéo vers la corbeille en cours…}one{Déplacement de <xliff:g id="COUNT">^1</xliff:g> vidéo vers la corbeille en cours…}many{Déplacement de <xliff:g id="COUNT">^1</xliff:g> vidéos vers la corbeille en cours…}other{Déplacement de <xliff:g id="COUNT">^1</xliff:g> vidéos vers la corbeille en cours…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à déplacer cette photo vers la corbeille?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à déplacer <xliff:g id="COUNT">^2</xliff:g> photo vers la corbeille?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à déplacer <xliff:g id="COUNT">^2</xliff:g> photos vers la corbeille?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à déplacer <xliff:g id="COUNT">^2</xliff:g> photos vers la corbeille?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Déplacement de la photo vers la corbeille en cours…}one{Déplacement de <xliff:g id="COUNT">^1</xliff:g> photo vers la corbeille en cours…}many{Déplacement de <xliff:g id="COUNT">^1</xliff:g> photos vers la corbeille en cours…}other{Déplacement de <xliff:g id="COUNT">^1</xliff:g> photos vers la corbeille en cours…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à déplacer cet élément vers la corbeille?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à déplacer <xliff:g id="COUNT">^2</xliff:g> élément vers la corbeille?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à déplacer <xliff:g id="COUNT">^2</xliff:g> éléments vers la corbeille?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à déplacer <xliff:g id="COUNT">^2</xliff:g> éléments vers la corbeille?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Déplacement de l\'élément vers la corbeille en cours…}one{Déplacement de <xliff:g id="COUNT">^1</xliff:g> élément vers la corbeille en cours…}many{Déplacement de <xliff:g id="COUNT">^1</xliff:g> éléments vers la corbeille en cours…}other{Déplacement de <xliff:g id="COUNT">^1</xliff:g> éléments vers la corbeille en cours…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à restaurer ce fichier audio de la corbeille?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à restaurer <xliff:g id="COUNT">^2</xliff:g> fichier audio de la corbeille?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à restaurer <xliff:g id="COUNT">^2</xliff:g> fichiers audio de la corbeille?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à restaurer <xliff:g id="COUNT">^2</xliff:g> fichiers audio de la corbeille?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Restauration du fichier audio de la corbeille en cours…}one{Restauration de <xliff:g id="COUNT">^1</xliff:g> fichier audio de la corbeille en cours…}many{Restauration de <xliff:g id="COUNT">^1</xliff:g> fichiers audio de la corbeille en cours…}other{Restauration de <xliff:g id="COUNT">^1</xliff:g> fichiers audio de la corbeille en cours…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à restaurer cette vidéo de la corbeille?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à restaurer <xliff:g id="COUNT">^2</xliff:g> vidéo de la corbeille?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à restaurer <xliff:g id="COUNT">^2</xliff:g> vidéos de la corbeille?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à restaurer <xliff:g id="COUNT">^2</xliff:g> vidéos de la corbeille?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Restauration de la vidéo de la corbeille en cours…}one{Restauration de <xliff:g id="COUNT">^1</xliff:g> vidéo de la corbeille en cours…}many{Restauration de <xliff:g id="COUNT">^1</xliff:g> vidéos de la corbeille en cours…}other{Restauration de <xliff:g id="COUNT">^1</xliff:g> vidéos de la corbeille en cours…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à restaurer cette photo de la corbeille?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à restaurer <xliff:g id="COUNT">^2</xliff:g> photo de la corbeille?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à restaurer <xliff:g id="COUNT">^2</xliff:g> photos de la corbeille?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à restaurer <xliff:g id="COUNT">^2</xliff:g> photos de la corbeille?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Restauration de la photo de la corbeille en cours…}one{Restauration de <xliff:g id="COUNT">^1</xliff:g> photo de la corbeille en cours…}many{Restauration de <xliff:g id="COUNT">^1</xliff:g> photos de la corbeille en cours…}other{Restauration de <xliff:g id="COUNT">^1</xliff:g> photos de la corbeille en cours…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à restaurer cet élément de la corbeille?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à restaurer <xliff:g id="COUNT">^2</xliff:g> élément de la corbeille?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à restaurer <xliff:g id="COUNT">^2</xliff:g> éléments de la corbeille?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à restaurer <xliff:g id="COUNT">^2</xliff:g> éléments de la corbeille?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Restauration de l\'élément de la corbeille en cours…}one{Restauration de <xliff:g id="COUNT">^1</xliff:g> élément de la corbeille en cours…}many{Restauration de <xliff:g id="COUNT">^1</xliff:g> éléments de la corbeille en cours…}other{Restauration de <xliff:g id="COUNT">^1</xliff:g> éléments de la corbeille en cours…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à supprimer ce fichier audio?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> fichier audio?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> fichiers audio?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> fichiers audio?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Suppression du fichier audio en cours…}one{Suppression de <xliff:g id="COUNT">^1</xliff:g> fichier audio en cours…}many{Suppression de <xliff:g id="COUNT">^1</xliff:g> fichiers audio en cours…}other{Suppression de <xliff:g id="COUNT">^1</xliff:g> fichiers audio en cours…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à supprimer cette vidéo?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> vidéo?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> vidéos?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> vidéos?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Suppression de la vidéo en cours…}one{Suppression de <xliff:g id="COUNT">^1</xliff:g> vidéo en cours…}many{Suppression de <xliff:g id="COUNT">^1</xliff:g> vidéos en cours…}other{Suppression de <xliff:g id="COUNT">^1</xliff:g> vidéos en cours…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à supprimer cette photo?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> photo?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> photos?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> photos?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Suppression de la photo en cours…}one{Suppression de <xliff:g id="COUNT">^1</xliff:g> photo en cours…}many{Suppression de <xliff:g id="COUNT">^1</xliff:g> photos en cours…}other{Suppression de <xliff:g id="COUNT">^1</xliff:g> photos en cours…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à supprimer cet élément?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> élément?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> éléments?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> éléments?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Suppression de l\'élément en cours…}one{Suppression de <xliff:g id="COUNT">^1</xliff:g> élément en cours…}many{Suppression de <xliff:g id="COUNT">^1</xliff:g> éléments en cours…}other{Suppression de <xliff:g id="COUNT">^1</xliff:g> éléments en cours…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> ne peut pas traiter les fichiers multimédias"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Le traitement du contenu multimédia a été annulé"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Une erreur s\'est produite durant le traitement du contenu multimédia"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index c995739..fdd3c24 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Outil de sélection des photos"</string>
<string name="artist_label" msgid="8105600993099120273">"Artiste"</string>
<string name="unknown" msgid="2059049215682829375">"Inconnu"</string>
<string name="root_images" msgid="5861633549189045666">"Images"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Continuer"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Autoriser"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Refuser"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">+ <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+ <xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">Plus <xliff:g id="COUNT_1">^1</xliff:g> autre élément</item>
- <item quantity="other">Plus <xliff:g id="COUNT_1">^1</xliff:g> autres éléments</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+ <xliff:g id="COUNT_0">^1</xliff:g>}one{+ <xliff:g id="COUNT_1">^1</xliff:g>}many{+ <xliff:g id="COUNT_1">^1</xliff:g>}other{+ <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Plus <xliff:g id="COUNT_0">^1</xliff:g> autre élément}one{Plus <xliff:g id="COUNT_1">^1</xliff:g> autre élément}many{Plus <xliff:g id="COUNT_1">^1</xliff:g> autres éléments}other{Plus <xliff:g id="COUNT_1">^1</xliff:g> autres éléments}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Effacer les fichiers d\'application temporaires"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> souhaite supprimer quelques fichiers temporaires, ce qui risque de solliciter davantage la batterie et le réseau de données mobiles."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Suppression des fichiers d\'application temporaires…"</string>
<string name="clear" msgid="5524638938415865915">"Effacer"</string>
<string name="allow" msgid="8885707816848569619">"Autoriser"</string>
<string name="deny" msgid="6040983710442068936">"Refuser"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> fichier audio ?</item>
- <item quantity="other">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> fichiers audio ?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one">Modification de <xliff:g id="COUNT">^1</xliff:g> fichier audio…</item>
- <item quantity="other">Modification de <xliff:g id="COUNT">^1</xliff:g> fichiers audio…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> vidéo ?</item>
- <item quantity="other">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> vidéos ?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one">Modification de <xliff:g id="COUNT">^1</xliff:g> vidéo…</item>
- <item quantity="other">Modification de <xliff:g id="COUNT">^1</xliff:g> vidéos…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> photo ?</item>
- <item quantity="other">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> photos ?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one">Modification de <xliff:g id="COUNT">^1</xliff:g> photo…</item>
- <item quantity="other">Modification de <xliff:g id="COUNT">^1</xliff:g> photos…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> élément ?</item>
- <item quantity="other">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> éléments ?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one">Modification de <xliff:g id="COUNT">^1</xliff:g> élément…</item>
- <item quantity="other">Modification de <xliff:g id="COUNT">^1</xliff:g> éléments…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à placer <xliff:g id="COUNT">^2</xliff:g> fichier audio dans la corbeille ?</item>
- <item quantity="other">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à placer <xliff:g id="COUNT">^2</xliff:g> fichiers audio dans la corbeille ?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one">Placement de <xliff:g id="COUNT">^1</xliff:g> fichier audio dans la corbeille…</item>
- <item quantity="other">Placement de <xliff:g id="COUNT">^1</xliff:g> fichiers audio dans la corbeille…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à placer <xliff:g id="COUNT">^2</xliff:g> vidéo dans la corbeille ?</item>
- <item quantity="other">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à placer <xliff:g id="COUNT">^2</xliff:g> vidéos dans la corbeille ?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one">Placement de <xliff:g id="COUNT">^1</xliff:g> vidéo dans la corbeille…</item>
- <item quantity="other">Placement de <xliff:g id="COUNT">^1</xliff:g> vidéos dans la corbeille…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à placer <xliff:g id="COUNT">^2</xliff:g> photo dans la corbeille ?</item>
- <item quantity="other">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à placer <xliff:g id="COUNT">^2</xliff:g> photos dans la corbeille ?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one">Placement de <xliff:g id="COUNT">^1</xliff:g> photo dans la corbeille…</item>
- <item quantity="other">Placement de <xliff:g id="COUNT">^1</xliff:g> photos dans la corbeille…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à placer <xliff:g id="COUNT">^2</xliff:g> élément dans la corbeille ?</item>
- <item quantity="other">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à placer <xliff:g id="COUNT">^2</xliff:g> éléments dans la corbeille ?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one">Placement de <xliff:g id="COUNT">^1</xliff:g> élément dans la corbeille…</item>
- <item quantity="other">Placement de <xliff:g id="COUNT">^1</xliff:g> éléments dans la corbeille…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à retirer <xliff:g id="COUNT">^2</xliff:g> fichier audio de la corbeille ?</item>
- <item quantity="other">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à retirer <xliff:g id="COUNT">^2</xliff:g> fichiers audio de la corbeille ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one">Retrait de <xliff:g id="COUNT">^1</xliff:g> fichier audio de la corbeille…</item>
- <item quantity="other">Retrait de <xliff:g id="COUNT">^1</xliff:g> fichiers audio de la corbeille…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à retirer <xliff:g id="COUNT">^2</xliff:g> vidéo de la corbeille ?</item>
- <item quantity="other">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à retirer <xliff:g id="COUNT">^2</xliff:g> vidéos de la corbeille ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one">Retrait de <xliff:g id="COUNT">^1</xliff:g> vidéo de la corbeille…</item>
- <item quantity="other">Retrait de <xliff:g id="COUNT">^1</xliff:g> vidéos de la corbeille…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à retirer <xliff:g id="COUNT">^2</xliff:g> photo de la corbeille ?</item>
- <item quantity="other">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à retirer <xliff:g id="COUNT">^2</xliff:g> photos de la corbeille ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one">Retrait de <xliff:g id="COUNT">^1</xliff:g> photo de la corbeille…</item>
- <item quantity="other">Retrait de <xliff:g id="COUNT">^1</xliff:g> photos de la corbeille…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à retirer <xliff:g id="COUNT">^2</xliff:g> élément de la corbeille ?</item>
- <item quantity="other">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à retirer <xliff:g id="COUNT">^2</xliff:g> éléments de la corbeille ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one">Retrait de <xliff:g id="COUNT">^1</xliff:g> élément de la corbeille…</item>
- <item quantity="other">Retrait de <xliff:g id="COUNT">^1</xliff:g> éléments de la corbeille…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> fichier audio ?</item>
- <item quantity="other">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> fichiers audio ?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one">Suppression de <xliff:g id="COUNT">^1</xliff:g> fichier audio…</item>
- <item quantity="other">Suppression de <xliff:g id="COUNT">^1</xliff:g> fichiers audio…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> vidéo ?</item>
- <item quantity="other">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> vidéos ?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one">Suppression de <xliff:g id="COUNT">^1</xliff:g> vidéo…</item>
- <item quantity="other">Suppression de <xliff:g id="COUNT">^1</xliff:g> vidéos…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> photo ?</item>
- <item quantity="other">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> photos ?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one">Suppression de <xliff:g id="COUNT">^1</xliff:g> photo…</item>
- <item quantity="other">Suppression de <xliff:g id="COUNT">^1</xliff:g> photos…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> élément ?</item>
- <item quantity="other">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> éléments ?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one">Suppression de <xliff:g id="COUNT">^1</xliff:g> élément…</item>
- <item quantity="other">Suppression de <xliff:g id="COUNT">^1</xliff:g> éléments…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Ajouter"</string>
+ <string name="deselect" msgid="4297825044827769490">"Désélectionner"</string>
+ <string name="deselected" msgid="8488133193326208475">"Désélectionné"</string>
+ <string name="select" msgid="2704765470563027689">"Sélectionner"</string>
+ <string name="selected" msgid="9151797369975828124">"Sélectionné"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Sélectionnez <xliff:g id="COUNT_0">^1</xliff:g> élément maximum}one{Sélectionnez <xliff:g id="COUNT_1">^1</xliff:g> élément maximum}many{Sélectionnez <xliff:g id="COUNT_1">^1</xliff:g> éléments maximum}other{Sélectionnez <xliff:g id="COUNT_1">^1</xliff:g> éléments maximum}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Récente(s)"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Aucune photo ni vidéo"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Aucun album"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Afficher la sélection"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Photos"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albums"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Aperçu"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Passer au professionnel"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Passer au personnel"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Bloqué par votre administrateur"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Vous n\'êtes pas autorisé à accéder à des données professionnelles depuis une appli personnelle"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Vous n\'êtes pas autorisé à accéder à des données à caractère personnel depuis une appli professionnelle"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Applis professionnelles en pause"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Pour ouvrir des photos professionnelles, activez vos applis professionnelles, puis réessayez"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Cette appli peut uniquement accéder aux photos que vous sélectionnez"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> élément}one{<xliff:g id="COUNT_1">^1</xliff:g> élément}many{<xliff:g id="COUNT_1">^1</xliff:g> éléments}other{<xliff:g id="COUNT_1">^1</xliff:g> éléments}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Ajouter (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Appareil photo"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Téléchargements"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Favoris"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Captures d\'écran"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Photo animée"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> prise le <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Vidéo d\'une durée de <xliff:g id="DURATION">%2$s</xliff:g> filmée le <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Photo"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Photo animée"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Couper le son de la vidéo"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Réactiver le son de la vidéo"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Lire la vidéo"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Mettre la vidéo en pause"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Fichier multimédia cloud désormais disponible depuis <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"non sélectionné"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à modifier ce fichier audio ?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> fichier audio ?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> fichiers audio ?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> fichiers audio ?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Modification du fichier audio…}one{Modification de <xliff:g id="COUNT">^1</xliff:g> fichier audio…}many{Modification de <xliff:g id="COUNT">^1</xliff:g> fichiers audio…}other{Modification de <xliff:g id="COUNT">^1</xliff:g> fichiers audio…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à modifier cette vidéo ?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> vidéo ?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> vidéos ?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> vidéos ?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Modification de la vidéo…}one{Modification de <xliff:g id="COUNT">^1</xliff:g> vidéo…}many{Modification de <xliff:g id="COUNT">^1</xliff:g> vidéos…}other{Modification de <xliff:g id="COUNT">^1</xliff:g> vidéos…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à modifier cette photo ?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> photo ?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> photos ?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> photos ?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Modification de la photo…}one{Modification de <xliff:g id="COUNT">^1</xliff:g> photo…}many{Modification de <xliff:g id="COUNT">^1</xliff:g> photos…}other{Modification de <xliff:g id="COUNT">^1</xliff:g> photos…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à modifier cet élément ?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> élément ?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> éléments ?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> éléments ?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Modification de l\'élément…}one{Modification de <xliff:g id="COUNT">^1</xliff:g> élément…}many{Modification de <xliff:g id="COUNT">^1</xliff:g> éléments…}other{Modification de <xliff:g id="COUNT">^1</xliff:g> éléments…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à placer ce fichier audio dans la corbeille ?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à placer <xliff:g id="COUNT">^2</xliff:g> fichier audio dans la corbeille ?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à placer <xliff:g id="COUNT">^2</xliff:g> fichiers audio dans la corbeille ?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à placer <xliff:g id="COUNT">^2</xliff:g> fichiers audio dans la corbeille ?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Placement du fichier audio dans la corbeille…}one{Placement de <xliff:g id="COUNT">^1</xliff:g> fichier audio dans la corbeille…}many{Placement de <xliff:g id="COUNT">^1</xliff:g> fichiers audio dans la corbeille…}other{Placement de <xliff:g id="COUNT">^1</xliff:g> fichiers audio dans la corbeille…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à placer cette vidéo dans la corbeille ?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à placer <xliff:g id="COUNT">^2</xliff:g> vidéo dans la corbeille ?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à placer <xliff:g id="COUNT">^2</xliff:g> vidéos dans la corbeille ?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à placer <xliff:g id="COUNT">^2</xliff:g> vidéos dans la corbeille ?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Placement de la vidéo dans la corbeille…}one{Placement de <xliff:g id="COUNT">^1</xliff:g> vidéo dans la corbeille…}many{Placement de <xliff:g id="COUNT">^1</xliff:g> vidéos dans la corbeille…}other{Placement de <xliff:g id="COUNT">^1</xliff:g> vidéos dans la corbeille…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à placer cette photo dans la corbeille ?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à placer <xliff:g id="COUNT">^2</xliff:g> photo dans la corbeille ?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à placer <xliff:g id="COUNT">^2</xliff:g> photos dans la corbeille ?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à placer <xliff:g id="COUNT">^2</xliff:g> photos dans la corbeille ?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Placement de la photo dans la corbeille…}one{Placement de <xliff:g id="COUNT">^1</xliff:g> photo dans la corbeille…}many{Placement de <xliff:g id="COUNT">^1</xliff:g> photos dans la corbeille…}other{Placement de <xliff:g id="COUNT">^1</xliff:g> photos dans la corbeille…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à placer cet élément dans la corbeille ?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à placer <xliff:g id="COUNT">^2</xliff:g> élément dans la corbeille ?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à placer <xliff:g id="COUNT">^2</xliff:g> éléments dans la corbeille ?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à placer <xliff:g id="COUNT">^2</xliff:g> éléments dans la corbeille ?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Placement de l\'élément dans la corbeille…}one{Placement de <xliff:g id="COUNT">^1</xliff:g> élément dans la corbeille…}many{Placement de <xliff:g id="COUNT">^1</xliff:g> éléments dans la corbeille…}other{Placement de <xliff:g id="COUNT">^1</xliff:g> éléments dans la corbeille…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à retirer ce fichier audio de la corbeille ?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à retirer <xliff:g id="COUNT">^2</xliff:g> fichier audio de la corbeille ?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à retirer <xliff:g id="COUNT">^2</xliff:g> fichiers audio de la corbeille ?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à retirer <xliff:g id="COUNT">^2</xliff:g> fichiers audio de la corbeille ?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Retrait du fichier audio de la corbeille…}one{Retrait de <xliff:g id="COUNT">^1</xliff:g> fichier audio de la corbeille…}many{Retrait de <xliff:g id="COUNT">^1</xliff:g> fichiers audio de la corbeille…}other{Retrait de <xliff:g id="COUNT">^1</xliff:g> fichiers audio de la corbeille…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à retirer cette vidéo de la corbeille ?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à retirer <xliff:g id="COUNT">^2</xliff:g> vidéo de la corbeille ?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à retirer <xliff:g id="COUNT">^2</xliff:g> vidéos de la corbeille ?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à retirer <xliff:g id="COUNT">^2</xliff:g> vidéos de la corbeille ?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Retrait de la vidéo de la corbeille…}one{Retrait de <xliff:g id="COUNT">^1</xliff:g> vidéo de la corbeille…}many{Retrait de <xliff:g id="COUNT">^1</xliff:g> vidéos de la corbeille…}other{Retrait de <xliff:g id="COUNT">^1</xliff:g> vidéos de la corbeille…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à retirer cette photo de la corbeille ?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à retirer <xliff:g id="COUNT">^2</xliff:g> photo de la corbeille ?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à retirer <xliff:g id="COUNT">^2</xliff:g> photos de la corbeille ?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à retirer <xliff:g id="COUNT">^2</xliff:g> photos de la corbeille ?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Retrait de la photo de la corbeille…}one{Retrait de <xliff:g id="COUNT">^1</xliff:g> photo de la corbeille…}many{Retrait de <xliff:g id="COUNT">^1</xliff:g> photos de la corbeille…}other{Retrait de <xliff:g id="COUNT">^1</xliff:g> photos de la corbeille…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à retirer cet élément de la corbeille ?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à retirer <xliff:g id="COUNT">^2</xliff:g> élément de la corbeille ?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à retirer <xliff:g id="COUNT">^2</xliff:g> éléments de la corbeille ?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à retirer <xliff:g id="COUNT">^2</xliff:g> éléments de la corbeille ?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Retrait de l\'élément de la corbeille…}one{Retrait de <xliff:g id="COUNT">^1</xliff:g> élément de la corbeille…}many{Retrait de <xliff:g id="COUNT">^1</xliff:g> éléments de la corbeille…}other{Retrait de <xliff:g id="COUNT">^1</xliff:g> éléments de la corbeille…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à supprimer ce fichier audio ?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> fichier audio ?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> fichiers audio ?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> fichiers audio ?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Suppression du fichier audio…}one{Suppression de <xliff:g id="COUNT">^1</xliff:g> fichier audio…}many{Suppression de <xliff:g id="COUNT">^1</xliff:g> fichiers audio…}other{Suppression de <xliff:g id="COUNT">^1</xliff:g> fichiers audio…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à supprimer cette vidéo ?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> vidéo ?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> vidéos ?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> vidéos ?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Suppression de la vidéo…}one{Suppression de <xliff:g id="COUNT">^1</xliff:g> vidéo…}many{Suppression de <xliff:g id="COUNT">^1</xliff:g> vidéos…}other{Suppression de <xliff:g id="COUNT">^1</xliff:g> vidéos…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à supprimer cette photo ?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> photo ?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> photos ?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> photos ?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Suppression de la photo…}one{Suppression de <xliff:g id="COUNT">^1</xliff:g> photo…}many{Suppression de <xliff:g id="COUNT">^1</xliff:g> photos…}other{Suppression de <xliff:g id="COUNT">^1</xliff:g> photos…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Autoriser <xliff:g id="APP_NAME_0">^1</xliff:g> à supprimer cet élément ?}one{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> élément ?}many{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> éléments ?}other{Autoriser <xliff:g id="APP_NAME_1">^1</xliff:g> à supprimer <xliff:g id="COUNT">^2</xliff:g> éléments ?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Suppression de l\'élément…}one{Suppression de <xliff:g id="COUNT">^1</xliff:g> élément…}many{Suppression de <xliff:g id="COUNT">^1</xliff:g> éléments…}other{Suppression de <xliff:g id="COUNT">^1</xliff:g> éléments…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> ne peut pas traiter les fichiers multimédias"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Traitement des contenus multimédias annulé"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Erreur de traitement des contenus multimédias"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index aa53246..6037874 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Selector de fotos"</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>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Continuar"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Permitir"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Denegar"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other"><xliff:g id="COUNT_1">^1</xliff:g> máis</item>
- <item quantity="one"><xliff:g id="COUNT_0">^1</xliff:g> máis</item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">E <xliff:g id="COUNT_1">^1</xliff:g> elementos adicionais</item>
- <item quantity="one">E <xliff:g id="COUNT_0">^1</xliff:g> elemento adicional</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> máis}other{<xliff:g id="COUNT_1">^1</xliff:g> máis}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{E <xliff:g id="COUNT_0">^1</xliff:g> elemento adicional}other{E <xliff:g id="COUNT_1">^1</xliff:g> elementos adicionais}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Borrar os ficheiros temporais das aplicacións"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> quere borrar algúns ficheiros temporais. Por este motivo, pode aumentar o uso da batería e dos datos móbiles."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Borrando ficheiros temporais das aplicacións…"</string>
<string name="clear" msgid="5524638938415865915">"Borrar"</string>
<string name="allow" msgid="8885707816848569619">"Permitir"</string>
<string name="deny" msgid="6040983710442068936">"Denegar"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> ficheiros de audio?</item>
- <item quantity="one">Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este ficheiro de audio?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">Modificando <xliff:g id="COUNT">^1</xliff:g> ficheiros de audio…</item>
- <item quantity="one">Modificando 1 ficheiro de audio…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> vídeos?</item>
- <item quantity="one">Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este vídeo?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">Modificando <xliff:g id="COUNT">^1</xliff:g> vídeos…</item>
- <item quantity="one">Modificando 1 vídeo…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> fotos?</item>
- <item quantity="one">Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> modifique esta foto?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">Modificando <xliff:g id="COUNT">^1</xliff:g> fotos…</item>
- <item quantity="one">Modificando 1 foto…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> elementos?</item>
- <item quantity="one">Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este elemento?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">Modificando <xliff:g id="COUNT">^1</xliff:g> elementos…</item>
- <item quantity="one">Modificando 1 elemento…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> ficheiros de audio á papeleira?</item>
- <item quantity="one">Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> mova este ficheiro de audio á papeleira?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">Movendo <xliff:g id="COUNT">^1</xliff:g> ficheiros de audio á papeleira…</item>
- <item quantity="one">Movendo 1 ficheiro de audio á papeleira…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> vídeos á papeleira?</item>
- <item quantity="one">Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> mova este vídeo á papeleira?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">Movendo <xliff:g id="COUNT">^1</xliff:g> vídeos á papeleira…</item>
- <item quantity="one">Movendo 1 vídeo á papeleira…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> fotos á papeleira?</item>
- <item quantity="one">Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> mova esta foto á papeleira?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">Movendo <xliff:g id="COUNT">^1</xliff:g> fotos á papeleira…</item>
- <item quantity="one">Movendo 1 foto á papeleira…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> elementos á papeleira?</item>
- <item quantity="one">Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> mova este elemento á papeleira?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">Movendo <xliff:g id="COUNT">^1</xliff:g> elementos á papeleira…</item>
- <item quantity="one">Movendo 1 elemento á papeleira…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> saque <xliff:g id="COUNT">^2</xliff:g> ficheiros de audio da papeleira?</item>
- <item quantity="one">Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> saque este ficheiro de audio da papeleira?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">Sacando <xliff:g id="COUNT">^1</xliff:g> ficheiros de audio da papeleira…</item>
- <item quantity="one">Sacando 1 ficheiro de audio da papeleira…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> saque <xliff:g id="COUNT">^2</xliff:g> vídeos da papeleira?</item>
- <item quantity="one">Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> saque este vídeo da papeleira?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">Sacando <xliff:g id="COUNT">^1</xliff:g> vídeos da papeleira…</item>
- <item quantity="one">Sacando 1 vídeo da papeleira…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> saque <xliff:g id="COUNT">^2</xliff:g> fotos da papeleira?</item>
- <item quantity="one">Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> saque esta foto da papeleira?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">Sacando <xliff:g id="COUNT">^1</xliff:g> fotos da papeleira…</item>
- <item quantity="one">Sacando 1 foto da papeleira…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> saque <xliff:g id="COUNT">^2</xliff:g> elementos da papeleira?</item>
- <item quantity="one">Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> saque este elemento da papeleira?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">Sacando <xliff:g id="COUNT">^1</xliff:g> elementos da papeleira…</item>
- <item quantity="one">Sacando 1 elemento da papeleira…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> elimine <xliff:g id="COUNT">^2</xliff:g> ficheiros de audio?</item>
- <item quantity="one">Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> elimine este ficheiro de audio?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">Eliminando <xliff:g id="COUNT">^1</xliff:g> ficheiros de audio…</item>
- <item quantity="one">Eliminando 1 ficheiro de audio…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> elimine <xliff:g id="COUNT">^2</xliff:g> vídeos?</item>
- <item quantity="one">Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> elimine este vídeo?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">Eliminando <xliff:g id="COUNT">^1</xliff:g> vídeos…</item>
- <item quantity="one">Eliminando 1 vídeo…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> elimine <xliff:g id="COUNT">^2</xliff:g> fotos?</item>
- <item quantity="one">Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> elimine esta foto?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">Eliminando <xliff:g id="COUNT">^1</xliff:g> fotos…</item>
- <item quantity="one">Eliminando 1 foto…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> elimine <xliff:g id="COUNT">^2</xliff:g> elementos?</item>
- <item quantity="one">Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> elimine este elemento?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">Eliminando <xliff:g id="COUNT">^1</xliff:g> elementos…</item>
- <item quantity="one">Eliminando 1 elemento…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Engadir"</string>
+ <string name="deselect" msgid="4297825044827769490">"Anular selección"</string>
+ <string name="deselected" msgid="8488133193326208475">"Anulouse a selección"</string>
+ <string name="select" msgid="2704765470563027689">"Seleccionar"</string>
+ <string name="selected" msgid="9151797369975828124">"Seleccionado"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Selecciona ata <xliff:g id="COUNT_0">^1</xliff:g> elemento}other{Selecciona ata <xliff:g id="COUNT_1">^1</xliff:g> elementos}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Recentes"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Non hai fotos nin vídeos"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Non hai álbums"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Ver elemento seleccionado"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Fotos"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Álbums"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Vista previa"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Cambiar ao perfil de traballo"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Cambiar ao perfil persoal"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Bloqueado polo administrador"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Non está permitido acceder aos datos do traballo desde unha aplicación persoal"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Non está permitido acceder aos datos persoais desde unha aplicación do traballo"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Puxéronse en pausa as aplicacións do traballo"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Para abrir fotos do perfil de traballo, activa as aplicacións do traballo e, a continuación, téntao de novo"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Esta aplicación só pode acceder ás fotos que selecciones"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> elemento}other{<xliff:g id="COUNT_1">^1</xliff:g> elementos}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Engadir (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Cámara"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Descargas"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Favoritos"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Capturas de pantalla"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Foto con movemento"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> (data en que se fixo: <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Vídeo gravado o <xliff:g id="TIME">%1$s</xliff:g>. Duración: <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Foto"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Foto con movemento"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Silenciar vídeo"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Activar son do vídeo"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Reproducir vídeo"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Pór vídeo en pausa"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Agora podes acceder desde <xliff:g id="PKG_NAME">%1$s</xliff:g> ao contido multimedia gardado na nube"</string>
+ <string name="not_selected" msgid="2244008151669896758">"elemento non seleccionado"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este ficheiro de audio?}other{Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> ficheiros de audio?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Modificando 1 ficheiro de audio…}other{Modificando <xliff:g id="COUNT">^1</xliff:g> ficheiros de audio…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este vídeo?}other{Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> vídeos?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Modificando 1 vídeo…}other{Modificando <xliff:g id="COUNT">^1</xliff:g> vídeos…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> modifique esta foto?}other{Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> fotos?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Modificando 1 foto…}other{Modificando <xliff:g id="COUNT">^1</xliff:g> fotos…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este elemento?}other{Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> elementos?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Modificando 1 elemento…}other{Modificando <xliff:g id="COUNT">^1</xliff:g> elementos…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> mova este ficheiro de audio á papeleira?}other{Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> ficheiros de audio á papeleira?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Movendo 1 ficheiro de audio á papeleira…}other{Movendo <xliff:g id="COUNT">^1</xliff:g> ficheiros de audio á papeleira…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> mova este vídeo á papeleira?}other{Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> vídeos á papeleira?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Movendo 1 vídeo á papeleira…}other{Movendo <xliff:g id="COUNT">^1</xliff:g> vídeos á papeleira…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> mova esta foto á papeleira?}other{Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> fotos á papeleira?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Movendo 1 foto á papeleira…}other{Movendo <xliff:g id="COUNT">^1</xliff:g> fotos á papeleira…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> mova este elemento á papeleira?}other{Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> elementos á papeleira?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Movendo 1 elemento á papeleira…}other{Movendo <xliff:g id="COUNT">^1</xliff:g> elementos á papeleira…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> saque este ficheiro de audio da papeleira?}other{Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> saque <xliff:g id="COUNT">^2</xliff:g> ficheiros de audio da papeleira?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Sacando 1 ficheiro de audio da papeleira…}other{Sacando <xliff:g id="COUNT">^1</xliff:g> ficheiros de audio da papeleira…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> saque este vídeo da papeleira?}other{Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> saque <xliff:g id="COUNT">^2</xliff:g> vídeos da papeleira?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Sacando 1 vídeo da papeleira…}other{Sacando <xliff:g id="COUNT">^1</xliff:g> vídeos da papeleira…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> saque esta foto da papeleira?}other{Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> saque <xliff:g id="COUNT">^2</xliff:g> fotos da papeleira?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Sacando 1 foto da papeleira…}other{Sacando <xliff:g id="COUNT">^1</xliff:g> fotos da papeleira…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> saque este elemento da papeleira?}other{Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> saque <xliff:g id="COUNT">^2</xliff:g> elementos da papeleira?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Sacando 1 elemento da papeleira…}other{Sacando <xliff:g id="COUNT">^1</xliff:g> elementos da papeleira…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> elimine este ficheiro de audio?}other{Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> elimine <xliff:g id="COUNT">^2</xliff:g> ficheiros de audio?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Eliminando 1 ficheiro de audio…}other{Eliminando <xliff:g id="COUNT">^1</xliff:g> ficheiros de audio…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> elimine este vídeo?}other{Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> elimine <xliff:g id="COUNT">^2</xliff:g> vídeos?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Eliminando 1 vídeo…}other{Eliminando <xliff:g id="COUNT">^1</xliff:g> vídeos…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> elimine esta foto?}other{Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> elimine <xliff:g id="COUNT">^2</xliff:g> fotos?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Eliminando 1 foto…}other{Eliminando <xliff:g id="COUNT">^1</xliff:g> fotos…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Queres permitir que <xliff:g id="APP_NAME_0">^1</xliff:g> elimine este elemento?}other{Queres permitir que <xliff:g id="APP_NAME_1">^1</xliff:g> elimine <xliff:g id="COUNT">^2</xliff:g> elementos?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Eliminando 1 elemento…}other{Eliminando <xliff:g id="COUNT">^1</xliff:g> elementos…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> non pode procesar ficheiros multimedia"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Cancelouse o procesamento do contido multimedia"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Produciuse un erro no procesamento do contido multimedia"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index f80d1c1..0c5669d 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"મીડિયા"</string>
<string name="storage_description" msgid="4081716890357580107">"સ્થાનિક સ્ટોરેજ"</string>
<string name="app_label" msgid="9035307001052716210">"મીડિયા સ્ટોરેજ"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"ફોટો પિકર"</string>
<string name="artist_label" msgid="8105600993099120273">"કલાકાર"</string>
<string name="unknown" msgid="2059049215682829375">"અજાણ"</string>
<string name="root_images" msgid="5861633549189045666">"છબીઓ"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"આગળ વધો"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"મંજૂરી આપો"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"નકારો"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">ઉપરાંત <xliff:g id="COUNT_1">^1</xliff:g> વધારાની આઇટમ</item>
- <item quantity="other">ઉપરાંત <xliff:g id="COUNT_1">^1</xliff:g> વધારાની આઇટમ</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}one{+<xliff:g id="COUNT_1">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{ઉપરાંત <xliff:g id="COUNT_0">^1</xliff:g> વધારાની આઇટમ}one{ઉપરાંત <xliff:g id="COUNT_1">^1</xliff:g> વધારાની આઇટમ}other{ઉપરાંત <xliff:g id="COUNT_1">^1</xliff:g> વધારાની આઇટમ}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"ઍપની બધી અસ્થાયી ફાઇલ સાફ કરો"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> કેટલીક અસ્થાયી ફાઇલોને સાફ કરવા માગે છે. આના પરિણામે બૅટરી અથવા સેલ્યુલર ડેટાના વપરાશમાં વધારો થઈ શકે છે."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"ઍપની બધી અસ્થાયી ફાઇલો સાફ કરી રહ્યાં છીએ…"</string>
<string name="clear" msgid="5524638938415865915">"સાફ કરો"</string>
<string name="allow" msgid="8885707816848569619">"મંજૂરી આપો"</string>
<string name="deny" msgid="6040983710442068936">"નકારો"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ઑડિયો ફાઇલમાં ફેરફાર કરવાની મંજૂરી આપીએ?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ઑડિયો ફાઇલમાં ફેરફાર કરવાની મંજૂરી આપીએ?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ઑડિયો ફાઇલમાં ફેરફાર કરી રહ્યાં છીએ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ઑડિયો ફાઇલમાં ફેરફાર કરી રહ્યાં છીએ…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> વીડિયોમાં ફેરફાર કરવાની મંજૂરી આપીએ?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> વીડિયોમાં ફેરફાર કરવાની મંજૂરી આપીએ?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> વીડિયોમાં ફેરફાર કરી રહ્યાં છીએ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> વીડિયોમાં ફેરફાર કરી રહ્યાં છીએ…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ફોટામાં ફેરફાર કરવાની મંજૂરી આપીએ?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ફોટામાં ફેરફાર કરવાની મંજૂરી આપીએ?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ફોટોમાં ફેરફાર કરી રહ્યાં છીએ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ફોટોમાં ફેરફાર કરી રહ્યાં છીએ…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> આઇટમમાં ફેરફાર કરવાની મંજૂરી આપીએ?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> આઇટમમાં ફેરફાર કરવાની મંજૂરી આપીએ?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> આઇટમમાં ફેરફાર કરી રહ્યાં છીએ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> આઇટમમાં ફેરફાર કરી રહ્યાં છીએ…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ઑડિયો ફાઇલને ટ્રેશમાં ખસેડવાની મંજૂરી આપીએ?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ઑડિયો ફાઇલને ટ્રેશમાં ખસેડવાની મંજૂરી આપીએ?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ઑડિયો ફાઇલ ટ્રેશમાં ખસેડી રહ્યાં છીએ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ઑડિયો ફાઇલ ટ્રેશમાં ખસેડી રહ્યાં છીએ…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> વીડિયોને ટ્રેશમાં ખસેડવાની મંજૂરી આપીએ?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> વીડિયોને ટ્રેશમાં ખસેડવાની મંજૂરી આપીએ?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> વીડિયો ટ્રેશમાં ખસેડી રહ્યાં છીએ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> વીડિયો ટ્રેશમાં ખસેડી રહ્યાં છીએ…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ફોટાને ટ્રેશમાં ખસેડવાની મંજૂરી આપીએ?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ફોટાને ટ્રેશમાં ખસેડવાની મંજૂરી આપીએ?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ફોટો ટ્રેશમાં ખસેડી રહ્યાં છીએ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ફોટો ટ્રેશમાં ખસેડી રહ્યાં છીએ…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> આઇટમને ટ્રેશમાં ખસેડવાની મંજૂરી આપીએ?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> આઇટમને ટ્રેશમાં ખસેડવાની મંજૂરી આપીએ?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> આઇટમ ટ્રેશમાં ખસેડી રહ્યાં છીએ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> આઇટમ ટ્રેશમાં ખસેડી રહ્યાં છીએ…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ઑડિયો ફાઇલને ટ્રેશમાંથી બહાર લાવવાની મંજૂરી આપીએ?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ઑડિયો ફાઇલને ટ્રેશમાંથી બહાર લાવવાની મંજૂરી આપીએ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ઑડિયો ફાઇલ ટ્રેશમાંથી બહાર ખસેડી રહ્યાં છીએ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ઑડિયો ફાઇલ ટ્રેશમાંથી બહાર ખસેડી રહ્યાં છીએ…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> વીડિયોને ટ્રેશમાંથી બહાર લાવવાની મંજૂરી આપીએ?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> વીડિયોને ટ્રેશમાંથી બહાર લાવવાની મંજૂરી આપીએ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> વીડિયો ટ્રેશમાંથી બહાર ખસેડી રહ્યાં છીએ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> વીડિયો ટ્રેશમાંથી બહાર ખસેડી રહ્યાં છીએ…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ફોટાને ટ્રેશમાંથી બહાર લાવવાની મંજૂરી આપીએ?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ફોટાને ટ્રેશમાંથી બહાર લાવવાની મંજૂરી આપીએ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ફોટો ટ્રેશમાંથી બહાર ખસેડી રહ્યાં છીએ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ફોટો ટ્રેશમાંથી બહાર ખસેડી રહ્યાં છીએ…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> આઇટમને ટ્રેશમાંથી બહાર લાવવાની મંજૂરી આપીએ?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> આઇટમને ટ્રેશમાંથી બહાર લાવવાની મંજૂરી આપીએ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> આઇટમ ટ્રેશમાંથી બહાર ખસેડી રહ્યાં છીએ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> આઇટમ ટ્રેશમાંથી બહાર ખસેડી રહ્યાં છીએ…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ને આ <xliff:g id="COUNT">^2</xliff:g> ઑડિયો ફાઇલ ડિલીટ કરવાની મંજૂરી આપીએ?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ને આ <xliff:g id="COUNT">^2</xliff:g> ઑડિયો ફાઇલ ડિલીટ કરવાની મંજૂરી આપીએ?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ઑડિયો ફાઇલ ડિલીટ કરી રહ્યાં છીએ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ઑડિયો ફાઇલ ડિલીટ કરી રહ્યાં છીએ…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ને આ <xliff:g id="COUNT">^2</xliff:g> વીડિયો ડિલીટ કરવાની મંજૂરી આપીએ?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ને આ <xliff:g id="COUNT">^2</xliff:g> વીડિયો ડિલીટ કરવાની મંજૂરી આપીએ?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> વીડિયો ડિલીટ કરી રહ્યાં છીએ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> વીડિયો ડિલીટ કરી રહ્યાં છીએ…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ને આ <xliff:g id="COUNT">^2</xliff:g> ફોટો ડિલીટ કરવાની મંજૂરી આપીએ?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ને આ <xliff:g id="COUNT">^2</xliff:g> ફોટો ડિલીટ કરવાની મંજૂરી આપીએ?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ફોટો ડિલીટ કરી રહ્યાં છીએ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ફોટો ડિલીટ કરી રહ્યાં છીએ…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ને આ <xliff:g id="COUNT">^2</xliff:g> આઇટમ ડિલીટ કરવાની મંજૂરી આપીએ?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ને આ <xliff:g id="COUNT">^2</xliff:g> આઇટમ ડિલીટ કરવાની મંજૂરી આપીએ?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> આઇટમ ડિલીટ કરી રહ્યાં છીએ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> આઇટમ ડિલીટ કરી રહ્યાં છીએ…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"ઉમેરો"</string>
+ <string name="deselect" msgid="4297825044827769490">"નાપસંદ કરો"</string>
+ <string name="deselected" msgid="8488133193326208475">"નાપસંદ કર્યું"</string>
+ <string name="select" msgid="2704765470563027689">"પસંદ કરો"</string>
+ <string name="selected" msgid="9151797369975828124">"પસંદ કર્યું"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> જેટલી આઇટમ પસંદ કરો}one{<xliff:g id="COUNT_1">^1</xliff:g> જેટલી આઇટમ પસંદ કરો}other{<xliff:g id="COUNT_1">^1</xliff:g> જેટલી આઇટમ પસંદ કરો}}"</string>
+ <string name="recent" msgid="6694613584743207874">"તાજેતરના"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"કોઈ ફોટો અથવા વીડિયો નથી"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"કોઈ આલ્બમ નથી"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"પસંદ કરેલા ફોટા જુઓ"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"ફોટા"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"આલ્બમ"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"પ્રીવ્યૂ કરો"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"ઑફિસની પ્રોફાઇલ પર સ્વિચ કરો"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"વ્યક્તિગત પ્રોફાઇલ પર સ્વિચ કરો"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"તમારા વ્યવસ્થાપકે સુવિધા બ્લૉક કરી છે"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"વ્યક્તિગત ઍપ પરથી ઑફિસનો ડેટા ઍક્સેસ કરવાની પરવાનગી નથી"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"ઑફિસ માટેની ઍપ પરથી વ્યક્તિગત ડેટા ઍક્સેસ કરવાની પરવાનગી નથી"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"ઑફિસ માટેની ઍપ થોભાવવામાં આવી છે"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"ઑફિસના ફોટા ખોલવા માટે, તમારી ઑફિસ માટેની ઍપ ખોલો અને પછી ફરી પ્રયાસ કરો"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"આ ઍપ માત્ર તમે પસંદ કરેલા ફોટા જ ઍક્સેસ કરી શકે છે"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> આઇટમ}one{<xliff:g id="COUNT_1">^1</xliff:g> આઇટમ}other{<xliff:g id="COUNT_1">^1</xliff:g> આઇટમ}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"ઉમેરો (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"કૅમેરા"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"ડાઉનલોડ"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"મનપસંદ"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"સ્ક્રીનશૉટ"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"મોશન ફોટો"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> <xliff:g id="TIME">%2$s</xliff:g> વાગ્યે લેવાયો"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"<xliff:g id="TIME">%1$s</xliff:g> વાગ્યે લેવામાં આવેલો <xliff:g id="DURATION">%2$s</xliff:g>ની અવધિનો વીડિયો"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"ફોટો"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"મોશન ફોટો"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"વીડિયો મ્યૂટ કરો"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"વીડિયોનો અવાજ ચાલુ કરો"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"વીડિયો ચલાવો"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"વીડિયો થોભાવો"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"ક્લાઉડ મીડિયા હવે <xliff:g id="PKG_NAME">%1$s</xliff:g>માંથી પણ ઉપલબ્ધ છે"</string>
+ <string name="not_selected" msgid="2244008151669896758">"પસંદ નહીં કરેલી"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ને આ ઑડિયો ફાઇલમાં ફેરફાર કરવાની મંજૂરી આપીએ?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ઑડિયો ફાઇલમાં ફેરફાર કરવાની મંજૂરી આપીએ?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ઑડિયો ફાઇલમાં ફેરફાર કરવાની મંજૂરી આપીએ?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{ઑડિયો ફાઇલમાં ફેરફાર કરી રહ્યાં છીએ…}one{<xliff:g id="COUNT">^1</xliff:g> ઑડિયો ફાઇલમાં ફેરફાર કરી રહ્યાં છીએ…}other{<xliff:g id="COUNT">^1</xliff:g> ઑડિયો ફાઇલમાં ફેરફાર કરી રહ્યાં છીએ…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ને આ વીડિયોમાં ફેરફાર કરવાની મંજૂરી આપીએ?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> વીડિયોમાં ફેરફાર કરવાની મંજૂરી આપીએ?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> વીડિયોમાં ફેરફાર કરવાની મંજૂરી આપીએ?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{વીડિયોમાં ફેરફાર કરી રહ્યાં છીએ…}one{<xliff:g id="COUNT">^1</xliff:g> વીડિયોમાં ફેરફાર કરી રહ્યાં છીએ…}other{<xliff:g id="COUNT">^1</xliff:g> વીડિયોમાં ફેરફાર કરી રહ્યાં છીએ…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ને આ ફોટામાં ફેરફાર કરવાની મંજૂરી આપીએ?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ફોટામાં ફેરફાર કરવાની મંજૂરી આપીએ?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ફોટામાં ફેરફાર કરવાની મંજૂરી આપીએ?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{ફોટોમાં ફેરફાર કરી રહ્યાં છીએ…}one{<xliff:g id="COUNT">^1</xliff:g> ફોટોમાં ફેરફાર કરી રહ્યાં છીએ…}other{<xliff:g id="COUNT">^1</xliff:g> ફોટામાં ફેરફાર કરી રહ્યાં છીએ…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ને આ આઇટમમાં ફેરફાર કરવાની મંજૂરી આપીએ?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> આઇટમમાં ફેરફાર કરવાની મંજૂરી આપીએ?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> આઇટમમાં ફેરફાર કરવાની મંજૂરી આપીએ?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{આઇટમમાં ફેરફાર કરી રહ્યાં છીએ…}one{<xliff:g id="COUNT">^1</xliff:g> આઇટમમાં ફેરફાર કરી રહ્યાં છીએ…}other{<xliff:g id="COUNT">^1</xliff:g> આઇટમમાં ફેરફાર કરી રહ્યાં છીએ…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ને આ ઑડિયો ફાઇલને ટ્રેશમાં ખસેડવાની મંજૂરી આપીએ?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ઑડિયો ફાઇલને ટ્રેશમાં ખસેડવાની મંજૂરી આપીએ?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ઑડિયો ફાઇલને ટ્રેશમાં ખસેડવાની મંજૂરી આપીએ?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{ઑડિયો ફાઇલ ટ્રેશમાં ખસેડી રહ્યાં છીએ…}one{<xliff:g id="COUNT">^1</xliff:g> ઑડિયો ફાઇલ ટ્રેશમાં ખસેડી રહ્યાં છીએ…}other{<xliff:g id="COUNT">^1</xliff:g> ઑડિયો ફાઇલ ટ્રેશમાં ખસેડી રહ્યાં છીએ…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ને આ વીડિયોને ટ્રેશમાં ખસેડવાની મંજૂરી આપીએ?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> વીડિયોને ટ્રેશમાં ખસેડવાની મંજૂરી આપીએ?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> વીડિયોને ટ્રેશમાં ખસેડવાની મંજૂરી આપીએ?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{વીડિયો ટ્રેશમાં ખસેડી રહ્યાં છીએ…}one{<xliff:g id="COUNT">^1</xliff:g> વીડિયો ટ્રેશમાં ખસેડી રહ્યાં છીએ…}other{<xliff:g id="COUNT">^1</xliff:g> વીડિયો ટ્રેશમાં ખસેડી રહ્યાં છીએ…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ને આ ફોટાને ટ્રેશમાં ખસેડવાની મંજૂરી આપીએ?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ફોટાને ટ્રેશમાં ખસેડવાની મંજૂરી આપીએ?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ફોટાને ટ્રેશમાં ખસેડવાની મંજૂરી આપીએ?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{ફોટો ટ્રેશમાં ખસેડી રહ્યાં છીએ…}one{<xliff:g id="COUNT">^1</xliff:g> ફોટો ટ્રેશમાં ખસેડી રહ્યાં છીએ…}other{<xliff:g id="COUNT">^1</xliff:g> ફોટા ટ્રેશમાં ખસેડી રહ્યાં છીએ…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ને આ આઇટમને ટ્રેશમાં ખસેડવાની મંજૂરી આપીએ?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> આઇટમને ટ્રેશમાં ખસેડવાની મંજૂરી આપીએ?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> આઇટમને ટ્રેશમાં ખસેડવાની મંજૂરી આપીએ?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{આઇટમ ટ્રેશમાં ખસેડી રહ્યાં છીએ…}one{<xliff:g id="COUNT">^1</xliff:g> આઇટમ ટ્રેશમાં ખસેડી રહ્યાં છીએ…}other{<xliff:g id="COUNT">^1</xliff:g> આઇટમ ટ્રેશમાં ખસેડી રહ્યાં છીએ…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ને આ ઑડિયો ફાઇલને ટ્રેશમાંથી બહાર લાવવાની મંજૂરી આપીએ?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ઑડિયો ફાઇલને ટ્રેશમાંથી બહાર લાવવાની મંજૂરી આપીએ?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ઑડિયો ફાઇલને ટ્રેશમાંથી બહાર લાવવાની મંજૂરી આપીએ?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{ઑડિયો ફાઇલ ટ્રેશમાંથી બહાર ખસેડી રહ્યાં છીએ…}one{<xliff:g id="COUNT">^1</xliff:g> ઑડિયો ફાઇલ ટ્રેશમાંથી બહાર ખસેડી રહ્યાં છીએ…}other{<xliff:g id="COUNT">^1</xliff:g> ઑડિયો ફાઇલ ટ્રેશમાંથી બહાર ખસેડી રહ્યાં છીએ…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ને આ વીડિયોને ટ્રેશમાંથી બહાર લાવવાની મંજૂરી આપીએ?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> વીડિયોને ટ્રેશમાંથી બહાર લાવવાની મંજૂરી આપીએ?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> વીડિયોને ટ્રેશમાંથી બહાર લાવવાની મંજૂરી આપીએ?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{વીડિયો ટ્રેશમાંથી બહાર ખસેડી રહ્યાં છીએ…}one{<xliff:g id="COUNT">^1</xliff:g> વીડિયો ટ્રેશમાંથી બહાર ખસેડી રહ્યાં છીએ…}other{<xliff:g id="COUNT">^1</xliff:g> વીડિયો ટ્રેશમાંથી બહાર ખસેડી રહ્યાં છીએ…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ને આ ફોટાને ટ્રેશમાંથી બહાર લાવવાની મંજૂરી આપીએ?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ફોટાને ટ્રેશમાંથી બહાર લાવવાની મંજૂરી આપીએ?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ફોટાને ટ્રેશમાંથી બહાર લાવવાની મંજૂરી આપીએ?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{ફોટો ટ્રેશમાંથી બહાર ખસેડી રહ્યાં છીએ…}one{<xliff:g id="COUNT">^1</xliff:g> ફોટો ટ્રેશમાંથી બહાર ખસેડી રહ્યાં છીએ…}other{<xliff:g id="COUNT">^1</xliff:g> ફોટા ટ્રેશમાંથી બહાર ખસેડી રહ્યાં છીએ…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ને આ આઇટમ ટ્રેશમાંથી બહાર લાવવાની મંજૂરી આપીએ?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> આઇટમને ટ્રેશમાંથી બહાર લાવવાની મંજૂરી આપીએ?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> આઇટમને ટ્રેશમાંથી બહાર લાવવાની મંજૂરી આપીએ?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{આઇટમ ટ્રેશમાંથી બહાર ખસેડી રહ્યાં છીએ…}one{<xliff:g id="COUNT">^1</xliff:g> આઇટમ ટ્રેશમાંથી બહાર ખસેડી રહ્યાં છીએ…}other{<xliff:g id="COUNT">^1</xliff:g> આઇટમ ટ્રેશમાંથી બહાર ખસેડી રહ્યાં છીએ…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ને આ ઑડિયો ફાઇલ ડિલીટ કરવાની મંજૂરી આપીએ?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ઑડિયો ફાઇલ ડિલીટ કરવાની મંજૂરી આપીએ?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ઑડિયો ફાઇલ ડિલીટ કરવાની મંજૂરી આપીએ?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{ઑડિયો ફાઇલ ડિલીટ કરી રહ્યાં છીએ…}one{<xliff:g id="COUNT">^1</xliff:g> ઑડિયો ફાઇલ ડિલીટ કરી રહ્યાં છીએ…}other{<xliff:g id="COUNT">^1</xliff:g> ઑડિયો ફાઇલ ડિલીટ કરી રહ્યાં છીએ…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ને આ વીડિયો ડિલીટ કરવાની મંજૂરી આપીએ?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> વીડિયો ડિલીટ કરવાની મંજૂરી આપીએ?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> વીડિયો ડિલીટ કરવાની મંજૂરી આપીએ?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{વીડિયો ડિલીટ કરી રહ્યાં છીએ…}one{<xliff:g id="COUNT">^1</xliff:g> વીડિયો ડિલીટ કરી રહ્યાં છીએ…}other{<xliff:g id="COUNT">^1</xliff:g> વીડિયો ડિલીટ કરી રહ્યાં છીએ…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ને આ ફોટો ડિલીટ કરવાની મંજૂરી આપીએ?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ફોટો ડિલીટ કરવાની મંજૂરી આપીએ?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ફોટા ડિલીટ કરવાની મંજૂરી આપીએ?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{ફોટો ડિલીટ કરી રહ્યાં છીએ…}one{<xliff:g id="COUNT">^1</xliff:g> ફોટો ડિલીટ કરી રહ્યાં છીએ…}other{<xliff:g id="COUNT">^1</xliff:g> ફોટા ડિલીટ કરી રહ્યાં છીએ…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>ને આ આઇટમ ડિલીટ કરવાની મંજૂરી આપીએ?}one{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> આઇટમ ડિલીટ કરવાની મંજૂરી આપીએ?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> આઇટમ ડિલીટ કરવાની મંજૂરી આપીએ?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{આઇટમ ડિલીટ કરી રહ્યાં છીએ…}one{<xliff:g id="COUNT">^1</xliff:g> આઇટમ ડિલીટ કરી રહ્યાં છીએ…}other{<xliff:g id="COUNT">^1</xliff:g> આઇટમ ડિલીટ કરી રહ્યાં છીએ…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> મીડિયા ફાઇલો પર પ્રક્રિયા કરી શકતું નથી"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"મીડિયા પર થતી પ્રક્રિયા રદ કરવામાં આવી"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"મીડિયા પર થતી પ્રક્રિયામાં ભૂલ આવી"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 5881984..6eed661 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"मीडिया"</string>
<string name="storage_description" msgid="4081716890357580107">"स्थानीय जगह"</string>
<string name="app_label" msgid="9035307001052716210">"मीडिया मेमोरी"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"फ़ोटो पिकर"</string>
<string name="artist_label" msgid="8105600993099120273">"कलाकार"</string>
<string name="unknown" msgid="2059049215682829375">"अज्ञात"</string>
<string name="root_images" msgid="5861633549189045666">"इमेज"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"जारी रखें"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"अनुमति दें"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"अनुमति न दें"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">अन्य <xliff:g id="COUNT_1">^1</xliff:g> आइटम</item>
- <item quantity="other">अन्य <xliff:g id="COUNT_1">^1</xliff:g> आइटम</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> और}one{<xliff:g id="COUNT_1">^1</xliff:g> और}other{<xliff:g id="COUNT_1">^1</xliff:g> और}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{इसके अलावा <xliff:g id="COUNT_0">^1</xliff:g> और आइटम}one{इसके अलावा <xliff:g id="COUNT_1">^1</xliff:g> और आइटम}other{इसके अलावा <xliff:g id="COUNT_1">^1</xliff:g> और आइटम}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"ऐप्लिकेशन से जुड़ी, कुछ समय तक सेव रहने वाली फ़ाइलें मिटाएं"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> कुछ समय तक रहने वाली फ़ाइलें हटाना चाहता है. इससे बैटरी या मोबाइल डेटा का इस्तेमाल बढ़ सकता है."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"ऐप्लिकेशन से जुड़ी, कुछ समय तक सेव रहने वाली फ़ाइलें मिटाई जा रही हैं…"</string>
<string name="clear" msgid="5524638938415865915">"मिटाएं"</string>
<string name="allow" msgid="8885707816848569619">"अनुमति दें"</string>
<string name="deny" msgid="6040983710442068936">"अनुमति न दें"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइल में बदलाव करने की अनुमति देना चाहते हैं?</item>
- <item quantity="other">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइलों में बदलाव करने की अनुमति देना चाहते हैं?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ऑडियो फ़ाइल में बदलाव किया जा रहा है…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ऑडियो फ़ाइलों में बदलाव किए जा रहे हैं…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> वीडियो में बदलाव करने की अनुमति देना चाहते हैं?</item>
- <item quantity="other">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> वीडियो में बदलाव करने की अनुमति देना चाहते हैं?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> वीडियो में बदलाव किया जा रहा है…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> वीडियो में बदलाव किए जा रहे हैं…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> फ़ोटो में बदलाव करने की अनुमति देना चाहते हैं?</item>
- <item quantity="other">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> फ़ोटो में बदलाव करने की अनुमति देना चाहते हैं?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> फ़ोटो में बदलाव किया जा रहा है…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> फ़ोटो में बदलाव किए जा रहे हैं…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> आइटम में बदलाव करने की अनुमति देना चाहते हैं?</item>
- <item quantity="other">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> आइटम में बदलाव करने की अनुमति देना चाहते हैं?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> आइटम में बदलाव किया जा रहा है…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> आइटम में बदलाव किए जा रहे हैं…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइल, ट्रैश में मूव करने की अनुमति देना चाहते हैं?</item>
- <item quantity="other">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइलें, ट्रैश में मूव करने की अनुमति देना चाहते हैं?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ऑडियो फ़ाइल को ट्रैश में भेजा जा रहा है…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ऑडियो फ़ाइलों को ट्रैश में भेजा जा रहा है…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> वीडियो, ट्रैश में मूव करने की अनुमति देना चाहते हैं?</item>
- <item quantity="other">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> वीडियो, ट्रैश में मूव करने की अनुमति देना चाहते हैं?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> वीडियो को ट्रैश में भेजा जा रहा है…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> वीडियो को ट्रैश में भेजा जा रहा है…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> फ़ोटो, ट्रैश में मूव करने की अनुमति देना चाहते हैं?</item>
- <item quantity="other">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> फ़ोटो, ट्रैश में मूव करने की अनुमति देना चाहते हैं?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> फ़ोटो को ट्रैश में भेजा जा रहा है…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> फ़ोटो को ट्रैश में भेजा जा रहा है…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> आइटम, ट्रैश में मूव करने की अनुमति देना चाहते हैं?</item>
- <item quantity="other">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> आइटम, ट्रैश में मूव करने की अनुमति देना चाहते हैं?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> आइटम को ट्रैश में भेजा जा रहा है…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> आइटम को ट्रैश में भेजा जा रहा है…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइल, ट्रैश से बाहर निकालने की अनुमति देना चाहते हैं?</item>
- <item quantity="other">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइलें, ट्रैश से बाहर निकालने की अनुमति देना चाहते हैं?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ऑडियो फ़ाइल को ट्रैश से बाहर निकाला जा रहा है…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ऑडियो फ़ाइलों को ट्रैश से बाहर निकाला जा रहा है…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> वीडियो, ट्रैश से बाहर निकालने की अनुमति देना चाहते हैं?</item>
- <item quantity="other">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> वीडियो, ट्रैश से बाहर निकालने की अनुमति देना चाहते हैं?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> वीडियो को ट्रैश से बाहर निकाला जा रहा है…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> वीडियो को ट्रैश से बाहर निकाला जा रहा है…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> फ़ोटो, ट्रैश से बाहर निकालने की अनुमति देना चाहते हैं?</item>
- <item quantity="other">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> फ़ोटो, ट्रैश से बाहर निकालने की अनुमति देना चाहते हैं?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> फ़ोटो को ट्रैश से बाहर निकाला जा रहा है…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> फ़ोटो को ट्रैश से बाहर निकाला जा रहा है…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> आइटम, ट्रैश से बाहर निकालने की अनुमति देना चाहते हैं?</item>
- <item quantity="other">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> आइटम, ट्रैश से बाहर निकालने की अनुमति देना चाहते हैं?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> आइटम को ट्रैश से बाहर निकाला जा रहा है…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> आइटम को ट्रैश से बाहर निकाला जा रहा है…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइल मिटाने की अनुमति देना चाहते हैं?</item>
- <item quantity="other">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइलें मिटाने की अनुमति देना चाहते हैं?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ऑडियो फ़ाइल मिटाई जा रही है…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ऑडियो फ़ाइलें मिटाई जा रही हैं…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> वीडियो मिटाने की अनुमति देना चाहते हैं?</item>
- <item quantity="other">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> वीडियो मिटाने की अनुमति देना चाहते हैं?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> वीडियो मिटाया जा रहा है…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> वीडियो मिटाए जा रहे हैं…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> फ़ोटो मिटाने की अनुमति देना चाहते हैं?</item>
- <item quantity="other">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> फ़ोटो मिटाने की अनुमति देना चाहते हैं?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> फ़ोटो मिटाई जा रही है…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> फ़ोटो मिटाई जा रही हैं…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> आइटम मिटाने की अनुमति देना चाहते हैं?</item>
- <item quantity="other">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> आइटम मिटाने की अनुमति देना चाहते हैं?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> आइटम मिटाया जा रहा है…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> आइटम मिटाए जा रहे हैं…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"जोड़ें"</string>
+ <string name="deselect" msgid="4297825044827769490">"चुना हुआ हटाएं"</string>
+ <string name="deselected" msgid="8488133193326208475">"चुना हुआ हटाया गया"</string>
+ <string name="select" msgid="2704765470563027689">"चुनें"</string>
+ <string name="selected" msgid="9151797369975828124">"चुना गया"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{ज़्यादा से ज़्यादा <xliff:g id="COUNT_0">^1</xliff:g> आइटम चुनें}one{ज़्यादा से ज़्यादा <xliff:g id="COUNT_1">^1</xliff:g> आइटम चुनें}other{ज़्यादा से ज़्यादा <xliff:g id="COUNT_1">^1</xliff:g> आइटम चुनें}}"</string>
+ <string name="recent" msgid="6694613584743207874">"हाल ही की फ़ोटो और वीडियो"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"कोई फ़ोटो या वीडियो नहीं है"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"कोई एल्बम नहीं है"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"चुनी गई फ़ोटो या वीडियो देखें"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"फ़ोटो"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"एल्बम"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"झलक"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"वर्क प्रोफ़ाइल पर जाएं"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"निजी प्रोफ़ाइल पर जाएं"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"आपके एडमिन ने रोक लगाई है"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"ऑफ़िस के काम से जुड़े डेटा को निजी ऐप्लिकेशन से ऐक्सेस करने की अनुमति नहीं है"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"निजी डेटा को ऑफ़िस के काम से जुड़े ऐप्लिकेशन से ऐक्सेस करने की अनुमति नहीं है"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"वर्क ऐप्लिकेशन रोक दिए गए हैं"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"वर्क फ़ोटो देखने के लिए, ऑफ़िस के काम से जुड़े ऐप्लिकेशन चालू करें और दोबारा कोशिश करें"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"इस ऐप्लिकेशन के पास आपकी उन फ़ोटो का ही ऐक्सेस होता है जिन्हें आपने चुना हो"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> आइटम}one{<xliff:g id="COUNT_1">^1</xliff:g> आइटम}other{<xliff:g id="COUNT_1">^1</xliff:g> आइटम}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"(<xliff:g id="COUNT">^1</xliff:g>) जोड़ें"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"कैमरा"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"डाउनलोड की गई चीज़ें"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"पसंदीदा"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"स्क्रीनशॉट"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"मोशन फ़ोटो"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> को <xliff:g id="TIME">%2$s</xliff:g> पर लिया गया"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"वीडियो, <xliff:g id="TIME">%1$s</xliff:g> बजे बनाया गया था और इसका कुल समय <xliff:g id="DURATION">%2$s</xliff:g> है"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"फ़ोटो"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"मोशन फ़ोटो"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"वीडियो म्यूट करें"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"वीडियो अनम्यूट करें"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"वीडियो चलाएं"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"वीडियो रोकें"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"क्लाउड मीडिया अब <xliff:g id="PKG_NAME">%1$s</xliff:g> पर उपलब्ध है"</string>
+ <string name="not_selected" msgid="2244008151669896758">"नहीं चुना गया"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{क्या <xliff:g id="APP_NAME_0">^1</xliff:g> को इस ऑडियो फ़ाइल में बदलाव करने की अनुमति देनी है?}one{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइल में बदलाव करने की अनुमति देनी है?}other{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइलों में बदलाव करने की अनुमति देनी है?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{ऑडियो फ़ाइल में बदलाव किया जा रहा है…}one{<xliff:g id="COUNT">^1</xliff:g> ऑडियो फ़ाइल में बदलाव किया जा रहा है…}other{<xliff:g id="COUNT">^1</xliff:g> ऑडियो फ़ाइलों में बदलाव किया जा रहा है…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{क्या <xliff:g id="APP_NAME_0">^1</xliff:g> को इस वीडियो में बदलाव करने की अनुमति देनी है?}one{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> वीडियो में बदलाव करने की अनुमति देनी है?}other{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> वीडियो में बदलाव करने की अनुमति देनी है?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{वीडियो में बदलाव किया जा रहा है…}one{<xliff:g id="COUNT">^1</xliff:g> वीडियो में बदलाव किया जा रहा है…}other{<xliff:g id="COUNT">^1</xliff:g> वीडियो में बदलाव किया जा रहा है…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{क्या <xliff:g id="APP_NAME_0">^1</xliff:g> को इस फ़ोटो में बदलाव करने की अनुमति देनी है?}one{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> फ़ोटो में बदलाव करने की अनुमति देनी है?}other{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> फ़ोटो में बदलाव करने की अनुमति देनी है?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{फ़ोटो में बदलाव किया जा रहा है…}one{<xliff:g id="COUNT">^1</xliff:g> फ़ोटो में बदलाव किया जा रहा है…}other{<xliff:g id="COUNT">^1</xliff:g> फ़ोटो में बदलाव किया जा रहा है…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{क्या <xliff:g id="APP_NAME_0">^1</xliff:g> को इस आइटम में बदलाव करने की अनुमति देनी है?}one{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> आइटम में बदलाव करने की अनुमति देनी है?}other{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> आइटम में बदलाव करने की अनुमति देनी है?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{आइटम में बदलाव किया जा रहा है…}one{<xliff:g id="COUNT">^1</xliff:g> आइटम में बदलाव किया जा रहा है…}other{<xliff:g id="COUNT">^1</xliff:g> आइटम में बदलाव किया जा रहा है…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{क्या <xliff:g id="APP_NAME_0">^1</xliff:g> को यह ऑडियो फ़ाइल, ट्रैश में मूव करने की अनुमति देनी है?}one{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइल, ट्रैश में मूव करने की अनुमति देनी है?}other{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइलें, ट्रैश में मूव करने की अनुमति देनी है?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{ऑडियो फ़ाइल ट्रैश में भेजी जा रही है…}one{<xliff:g id="COUNT">^1</xliff:g> ऑडियो फ़ाइल ट्रैश में भेजी जा रही है…}other{<xliff:g id="COUNT">^1</xliff:g> ऑडियो फ़ाइलें ट्रैश में भेजी जा रही हैं…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{क्या <xliff:g id="APP_NAME_0">^1</xliff:g> को यह वीडियो, ट्रैश में मूव करने की अनुमति देनी है?}one{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> वीडियो, ट्रैश में मूव करने की अनुमति देनी है?}other{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> वीडियो, ट्रैश में मूव करने की अनुमति देनी है?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{वीडियो ट्रैश में भेजा जा रहा है…}one{<xliff:g id="COUNT">^1</xliff:g> वीडियो ट्रैश में भेजा जा रहा है…}other{<xliff:g id="COUNT">^1</xliff:g> वीडियो ट्रैश में भेजे जा रहे हैं…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{क्या <xliff:g id="APP_NAME_0">^1</xliff:g> को यह फ़ोटो, ट्रैश में मूव करने की अनुमति देनी है?}one{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> फ़ोटो, ट्रैश में मूव करने की अनुमति देनी है?}other{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> फ़ोटो, ट्रैश में मूव करने की अनुमति देनी है?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{फ़ोटो ट्रैश में भेजी जा रही है…}one{<xliff:g id="COUNT">^1</xliff:g> फ़ोटो ट्रैश में भेजी जा रही है…}other{<xliff:g id="COUNT">^1</xliff:g> फ़ोटो ट्रैश में भेजी जा रही है…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{क्या <xliff:g id="APP_NAME_0">^1</xliff:g> को यह आइटम, ट्रैश में मूव करने की अनुमति देनी है?}one{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> आइटम, ट्रैश में मूव करने की अनुमति देनी है?}other{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> आइटम, ट्रैश में मूव करने की अनुमति देनी है?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{आइटम को ट्रैश में भेजा जा रहा है…}one{<xliff:g id="COUNT">^1</xliff:g> आइटम को ट्रैश में भेजा जा रहा है…}other{<xliff:g id="COUNT">^1</xliff:g> आइटम को ट्रैश में भेजा जा रहा है…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{क्या <xliff:g id="APP_NAME_0">^1</xliff:g> को यह ऑडियो फ़ाइल, ट्रैश से बाहर निकालने की अनुमति देनी है?}one{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइल, ट्रैश से बाहर निकालने की अनुमति देनी है?}other{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइलें, ट्रैश से बाहर निकालने की अनुमति देनी है?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{ऑडियो फ़ाइल ट्रैश से बाहर निकाली जा रही है…}one{<xliff:g id="COUNT">^1</xliff:g> ऑडियो फ़ाइल ट्रैश से बाहर निकाली जा रही है…}other{<xliff:g id="COUNT">^1</xliff:g> ऑडियो फ़ाइलें ट्रैश से बाहर निकाली जा रही हैं…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{क्या <xliff:g id="APP_NAME_0">^1</xliff:g> को यह वीडियो, ट्रैश से बाहर निकालने की अनुमति देनी है?}one{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> वीडियो, ट्रैश से बाहर निकालने की अनुमति देनी है?}other{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> वीडियो, ट्रैश से बाहर निकालने की अनुमति देनी है?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{वीडियो ट्रैश से बाहर निकाला जा रहा है…}one{<xliff:g id="COUNT">^1</xliff:g> वीडियो ट्रैश से बाहर निकाला जा रहा है…}other{<xliff:g id="COUNT">^1</xliff:g> वीडियो ट्रैश से बाहर निकाले जा रहे हैं…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{क्या <xliff:g id="APP_NAME_0">^1</xliff:g> को यह फ़ोटो, ट्रैश से बाहर निकालने की अनुमति देनी है?}one{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> फ़ोटो, ट्रैश से बाहर निकालने की अनुमति देनी है?}other{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> फ़ोटो, ट्रैश से बाहर निकालने की अनुमति देनी है?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{फ़ोटो ट्रैश से बाहर निकाली जा रही है…}one{<xliff:g id="COUNT">^1</xliff:g> फ़ोटो ट्रैश से बाहर निकाली जा रही है…}other{<xliff:g id="COUNT">^1</xliff:g> फ़ोटो ट्रैश से बाहर निकाली जा रही हैं…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{क्या <xliff:g id="APP_NAME_0">^1</xliff:g> को यह आइटम, ट्रैश से बाहर निकालने की अनुमति देनी है?}one{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> आइटम, ट्रैश से बाहर निकालने की अनुमति देनी है?}other{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> आइटम, ट्रैश से बाहर निकालने की अनुमति देनी है?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{आइटम को ट्रैश से बाहर निकाला जा रहा है…}one{<xliff:g id="COUNT">^1</xliff:g> आइटम को ट्रैश से बाहर निकाला जा रहा है…}other{<xliff:g id="COUNT">^1</xliff:g> आइटम ट्रैश से बाहर निकाले जा रहे हैं…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{क्या <xliff:g id="APP_NAME_0">^1</xliff:g> को यह ऑडियो फ़ाइल मिटाने की अनुमति देनी है?}one{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइल मिटाने की अनुमति देनी है?}other{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइलें मिटाने की अनुमति देनी है?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{ऑडियो फ़ाइल मिटाई जा रही है…}one{<xliff:g id="COUNT">^1</xliff:g> ऑडियो फ़ाइल मिटाई जा रही है…}other{<xliff:g id="COUNT">^1</xliff:g> ऑडियो फ़ाइलें मिटाई जा रही हैं…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{क्या <xliff:g id="APP_NAME_0">^1</xliff:g> को यह वीडियो मिटाने की अनुमति देनी है?}one{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> वीडियो मिटाने की अनुमति देनी है?}other{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> वीडियो मिटाने की अनुमति देनी है?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{वीडियो मिटाया जा रहा है…}one{<xliff:g id="COUNT">^1</xliff:g> वीडियो मिटाया जा रहा है…}other{<xliff:g id="COUNT">^1</xliff:g> वीडियो मिटाए जा रहे हैं…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{क्या <xliff:g id="APP_NAME_0">^1</xliff:g> को यह फ़ोटो मिटाने की अनुमति देनी है?}one{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> फ़ोटो मिटाने की अनुमति देनी है?}other{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> फ़ोटो मिटाने की अनुमति देनी है?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{फ़ोटो मिटाई जा रही है…}one{<xliff:g id="COUNT">^1</xliff:g> फ़ोटो मिटाई जा रही है…}other{<xliff:g id="COUNT">^1</xliff:g> फ़ोटो मिटाई जा रही हैं…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{क्या <xliff:g id="APP_NAME_0">^1</xliff:g> को यह आइटम मिटाने की अनुमति देनी है?}one{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> आइटम मिटाने की अनुमति देनी है?}other{क्या <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> आइटम मिटाने की अनुमति देनी है?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{आइटम मिटाया जा रहा है…}one{<xliff:g id="COUNT">^1</xliff:g> आइटम मिटाया जा रहा है…}other{<xliff:g id="COUNT">^1</xliff:g> आइटम मिटाए जा रहे हैं…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> मीडिया फ़ाइलों को प्रोसेस नहीं कर सकता"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"मीडिया को प्रोसेस करने की कार्रवाई रद्द की गई"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"मीडिया को प्रोसेस करने में गड़बड़ी हुई"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 776b290..e259cf1 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Alat za odabir fotografija"</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>
@@ -29,182 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Nastavi"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Dopusti"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Odbij"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">i još <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="few">i još <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">i još <xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">Plus sljedeći broj dodatnih stavki: <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="few">Plus sljedeći broj dodatnih stavki: <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">Plus sljedeći broj dodatnih stavki: <xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{i još <xliff:g id="COUNT_0">^1</xliff:g>}one{i još <xliff:g id="COUNT_1">^1</xliff:g>}few{i još <xliff:g id="COUNT_1">^1</xliff:g>}other{i još <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Plus sljedeći broj dodatnih stavki: <xliff:g id="COUNT_0">^1</xliff:g>}one{Plus sljedeći broj dodatnih stavki: <xliff:g id="COUNT_1">^1</xliff:g>}few{Plus sljedeći broj dodatnih stavki: <xliff:g id="COUNT_1">^1</xliff:g>}other{Plus sljedeći broj dodatnih stavki: <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Brisanje privremenih datoteka aplikacija"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> želi izbrisati neke privremene datoteke. Zbog toga može doći do veće upotrebe baterije ili mobilnih podataka."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Brišu se privremene datoteke aplikacija…"</string>
<string name="clear" msgid="5524638938415865915">"Izbriši"</string>
<string name="allow" msgid="8885707816848569619">"Dopusti"</string>
<string name="deny" msgid="6040983710442068936">"Odbij"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izmijeni <xliff:g id="COUNT">^2</xliff:g> audiodatoteku?</item>
- <item quantity="few">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izmijeni <xliff:g id="COUNT">^2</xliff:g> audiodatoteke?</item>
- <item quantity="other">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izmijeni <xliff:g id="COUNT">^2</xliff:g> audiodatoteka?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one">Mijenjanje <xliff:g id="COUNT">^1</xliff:g> audiodatoteke…</item>
- <item quantity="few">Mijenjanje <xliff:g id="COUNT">^1</xliff:g> audiodatoteke…</item>
- <item quantity="other">Mijenjanje <xliff:g id="COUNT">^1</xliff:g> audiodatoteka…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izmijeni <xliff:g id="COUNT">^2</xliff:g> videozapis?</item>
- <item quantity="few">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izmijeni <xliff:g id="COUNT">^2</xliff:g> videozapisa?</item>
- <item quantity="other">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izmijeni <xliff:g id="COUNT">^2</xliff:g> videozapisa?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one">Mijenjanje <xliff:g id="COUNT">^1</xliff:g> videozapisa…</item>
- <item quantity="few">Mijenjanje <xliff:g id="COUNT">^1</xliff:g> videozapisa…</item>
- <item quantity="other">Mijenjanje <xliff:g id="COUNT">^1</xliff:g> videozapisa…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izmijeni <xliff:g id="COUNT">^2</xliff:g> fotografiju?</item>
- <item quantity="few">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izmijeni <xliff:g id="COUNT">^2</xliff:g> fotografije?</item>
- <item quantity="other">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izmijeni <xliff:g id="COUNT">^2</xliff:g> fotografija?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one">Mijenjanje <xliff:g id="COUNT">^1</xliff:g> fotografije…</item>
- <item quantity="few">Mijenjanje <xliff:g id="COUNT">^1</xliff:g> fotografije…</item>
- <item quantity="other">Mijenjanje <xliff:g id="COUNT">^1</xliff:g> fotografija…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izmijeni <xliff:g id="COUNT">^2</xliff:g> stavku?</item>
- <item quantity="few">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izmijeni <xliff:g id="COUNT">^2</xliff:g> stavke?</item>
- <item quantity="other">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izmijeni <xliff:g id="COUNT">^2</xliff:g> stavki?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one">Mijenjanje <xliff:g id="COUNT">^1</xliff:g> stavke…</item>
- <item quantity="few">Mijenjanje <xliff:g id="COUNT">^1</xliff:g> stavke…</item>
- <item quantity="other">Mijenjanje <xliff:g id="COUNT">^1</xliff:g> stavki…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> audiodatoteku u otpad?</item>
- <item quantity="few">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> audiodatoteke u otpad?</item>
- <item quantity="other">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> audiodatoteka u otpad?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one">Premještanje <xliff:g id="COUNT">^1</xliff:g> audiodatoteke u otpad…</item>
- <item quantity="few">Premještanje <xliff:g id="COUNT">^1</xliff:g> audiodatoteke u otpad…</item>
- <item quantity="other">Premještanje <xliff:g id="COUNT">^1</xliff:g> audiodatoteka u otpad…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> videozapis u otpad?</item>
- <item quantity="few">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> videozapisa u otpad?</item>
- <item quantity="other">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> videozapisa u otpad?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one">Premještanje <xliff:g id="COUNT">^1</xliff:g> videozapisa u otpad…</item>
- <item quantity="few">Premještanje <xliff:g id="COUNT">^1</xliff:g> videozapisa u otpad…</item>
- <item quantity="other">Premještanje <xliff:g id="COUNT">^1</xliff:g> videozapisa u otpad…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> fotografiju u otpad?</item>
- <item quantity="few">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> fotografije u otpad?</item>
- <item quantity="other">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> fotografija u otpad?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one">Premještanje <xliff:g id="COUNT">^1</xliff:g> fotografije u otpad…</item>
- <item quantity="few">Premještanje <xliff:g id="COUNT">^1</xliff:g> fotografije u otpad…</item>
- <item quantity="other">Premještanje <xliff:g id="COUNT">^1</xliff:g> fotografija u otpad…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> stavku u otpad?</item>
- <item quantity="few">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> stavke u otpad?</item>
- <item quantity="other">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> stavki u otpad?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one">Premještanje <xliff:g id="COUNT">^1</xliff:g> stavke u otpad…</item>
- <item quantity="few">Premještanje <xliff:g id="COUNT">^1</xliff:g> stavke u otpad…</item>
- <item quantity="other">Premještanje <xliff:g id="COUNT">^1</xliff:g> stavki u otpad…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> audiodatoteku iz otpada?</item>
- <item quantity="few">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> audiodatoteke iz otpada?</item>
- <item quantity="other">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> audiodatoteka iz otpada?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one">Premještanje <xliff:g id="COUNT">^1</xliff:g> audiodatoteke iz otpada…</item>
- <item quantity="few">Premještanje <xliff:g id="COUNT">^1</xliff:g> audiodatoteke iz otpada…</item>
- <item quantity="other">Premještanje <xliff:g id="COUNT">^1</xliff:g> audiodatoteka iz otpada…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> videozapis iz otpada?</item>
- <item quantity="few">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> videozapisa iz otpada?</item>
- <item quantity="other">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> videozapisa iz otpada?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one">Premještanje <xliff:g id="COUNT">^1</xliff:g> videozapisa iz otpada…</item>
- <item quantity="few">Premještanje <xliff:g id="COUNT">^1</xliff:g> videozapisa iz otpada…</item>
- <item quantity="other">Premještanje <xliff:g id="COUNT">^1</xliff:g> videozapisa iz otpada…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> fotografiju iz otpada?</item>
- <item quantity="few">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> fotografije iz otpada?</item>
- <item quantity="other">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> fotografija iz otpada?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one">Premještanje <xliff:g id="COUNT">^1</xliff:g> fotografije iz otpada…</item>
- <item quantity="few">Premještanje <xliff:g id="COUNT">^1</xliff:g> fotografije iz otpada…</item>
- <item quantity="other">Premještanje <xliff:g id="COUNT">^1</xliff:g> fotografija iz otpada…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> stavku iz otpada?</item>
- <item quantity="few">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> stavke iz otpada?</item>
- <item quantity="other">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> stavki iz otpada?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one">Premještanje <xliff:g id="COUNT">^1</xliff:g> stavke iz otpada…</item>
- <item quantity="few">Premještanje <xliff:g id="COUNT">^1</xliff:g> stavke iz otpada…</item>
- <item quantity="other">Premještanje <xliff:g id="COUNT">^1</xliff:g> stavki iz otpada…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izbriše <xliff:g id="COUNT">^2</xliff:g> audiodatoteku?</item>
- <item quantity="few">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izbriše <xliff:g id="COUNT">^2</xliff:g> audiodatoteke?</item>
- <item quantity="other">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izbriše <xliff:g id="COUNT">^2</xliff:g> audiodatoteka?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one">Brisanje <xliff:g id="COUNT">^1</xliff:g> audiodatoteke…</item>
- <item quantity="few">Brisanje <xliff:g id="COUNT">^1</xliff:g> audiodatoteke…</item>
- <item quantity="other">Brisanje <xliff:g id="COUNT">^1</xliff:g> audiodatoteka…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izbriše <xliff:g id="COUNT">^2</xliff:g> videozapis?</item>
- <item quantity="few">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izbriše <xliff:g id="COUNT">^2</xliff:g> videozapisa?</item>
- <item quantity="other">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izbriše <xliff:g id="COUNT">^2</xliff:g> videozapisa?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one">Brisanje <xliff:g id="COUNT">^1</xliff:g> videozapisa…</item>
- <item quantity="few">Brisanje <xliff:g id="COUNT">^1</xliff:g> videozapisa…</item>
- <item quantity="other">Brisanje <xliff:g id="COUNT">^1</xliff:g> videozapisa…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izbriše <xliff:g id="COUNT">^2</xliff:g> fotografiju?</item>
- <item quantity="few">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izbriše <xliff:g id="COUNT">^2</xliff:g> fotografije?</item>
- <item quantity="other">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izbriše <xliff:g id="COUNT">^2</xliff:g> fotografija?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one">Brisanje <xliff:g id="COUNT">^1</xliff:g> fotografije…</item>
- <item quantity="few">Brisanje <xliff:g id="COUNT">^1</xliff:g> fotografije…</item>
- <item quantity="other">Brisanje <xliff:g id="COUNT">^1</xliff:g> fotografija…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izbriše <xliff:g id="COUNT">^2</xliff:g> stavku?</item>
- <item quantity="few">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izbriše <xliff:g id="COUNT">^2</xliff:g> stavke?</item>
- <item quantity="other">Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izbriše <xliff:g id="COUNT">^2</xliff:g> stavki?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one">Brisanje <xliff:g id="COUNT">^1</xliff:g> stavke…</item>
- <item quantity="few">Brisanje <xliff:g id="COUNT">^1</xliff:g> stavke…</item>
- <item quantity="other">Brisanje <xliff:g id="COUNT">^1</xliff:g> stavki…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Dodaj"</string>
+ <string name="deselect" msgid="4297825044827769490">"Poništi odabir"</string>
+ <string name="deselected" msgid="8488133193326208475">"Odabir poništen"</string>
+ <string name="select" msgid="2704765470563027689">"Odaberi"</string>
+ <string name="selected" msgid="9151797369975828124">"Odabrano"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Odaberite najviše <xliff:g id="COUNT_0">^1</xliff:g> stavku}one{Odaberite najviše <xliff:g id="COUNT_1">^1</xliff:g> stavku}few{Odaberite najviše <xliff:g id="COUNT_1">^1</xliff:g> stavke}other{Odaberite najviše <xliff:g id="COUNT_1">^1</xliff:g> stavki}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Nedavno"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Nema fotografija i videozapisa"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Nema albuma"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Prikaži odabrano"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Fotografije"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albumi"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Pregled"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Prijeđite na poslovni"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Prijeđite na osobni"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Blokirao vaš administrator"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Pristupanje poslovnim podacima putem osobne aplikacije nije dopušteno"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Pristupanje osobnim podacima putem poslovne aplikacije nije dopušteno"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Poslovne aplikacije su pauzirane"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Da biste otvorili fotografije s poslovnog profila, uključite poslovne aplikacije i pokušajte ponovo"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Ova aplikacija može pristupiti samo fotografijama koje odaberete"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> stavka}one{<xliff:g id="COUNT_1">^1</xliff:g> stavka}few{<xliff:g id="COUNT_1">^1</xliff:g> stavke}other{<xliff:g id="COUNT_1">^1</xliff:g> stavki}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Dodaj (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Preuzimanja"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Omiljeno"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Snimke zaslona"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Fotografija s videom"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g>, snimljeno: <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Videozapis snimljen <xliff:g id="TIME">%1$s</xliff:g> u trajanju od <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Fotografija"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Fotografija s videom"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Isključi kameru"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Uključi kameru"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Reproduciraj videozapis"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Pauziraj videozapis"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Medijski sadržaj u oblaku sada je dostupan iz aplikacije <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"nije odabrano"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g> da izmijeni tu audiodatoteku?}one{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izmijeni <xliff:g id="COUNT">^2</xliff:g> audiodatoteku?}few{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izmijeni <xliff:g id="COUNT">^2</xliff:g> audiodatoteke?}other{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izmijeni <xliff:g id="COUNT">^2</xliff:g> audiodatoteka?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Mijenjanje audiodatoteke…}one{Mijenjanje <xliff:g id="COUNT">^1</xliff:g> audiodatoteke…}few{Mijenjanje <xliff:g id="COUNT">^1</xliff:g> audiodatoteke…}other{Mijenjanje <xliff:g id="COUNT">^1</xliff:g> audiodatoteka…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g> da izmijeni taj videozapis?}one{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izmijeni <xliff:g id="COUNT">^2</xliff:g> videozapis?}few{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izmijeni <xliff:g id="COUNT">^2</xliff:g> videozapisa?}other{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izmijeni <xliff:g id="COUNT">^2</xliff:g> videozapisa?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Mijenjanje videozapisa…}one{Mijenjanje <xliff:g id="COUNT">^1</xliff:g> videozapisa…}few{Mijenjanje <xliff:g id="COUNT">^1</xliff:g> videozapisa…}other{Mijenjanje <xliff:g id="COUNT">^1</xliff:g> videozapisa…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g> da izmijeni tu fotografiju?}one{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izmijeni <xliff:g id="COUNT">^2</xliff:g> fotografiju?}few{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izmijeni <xliff:g id="COUNT">^2</xliff:g> fotografije?}other{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izmijeni <xliff:g id="COUNT">^2</xliff:g> fotografija?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Mijenjanje fotografije…}one{Mijenjanje <xliff:g id="COUNT">^1</xliff:g> fotografije…}few{Mijenjanje <xliff:g id="COUNT">^1</xliff:g> fotografije…}other{Mijenjanje <xliff:g id="COUNT">^1</xliff:g> fotografija…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g> da izmijeni tu stavku?}one{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izmijeni <xliff:g id="COUNT">^2</xliff:g> stavku?}few{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izmijeni <xliff:g id="COUNT">^2</xliff:g> stavke?}other{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izmijeni <xliff:g id="COUNT">^2</xliff:g> stavki?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Mijenjanje stavke…}one{Mijenjanje <xliff:g id="COUNT">^1</xliff:g> stavke…}few{Mijenjanje <xliff:g id="COUNT">^1</xliff:g> stavke…}other{Mijenjanje <xliff:g id="COUNT">^1</xliff:g> stavki…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g> da premjesti tu audiodatoteku u otpad?}one{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> audiodatoteku u otpad?}few{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> audiodatoteke u otpad?}other{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> audiodatoteka u otpad?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Premještanje audiodatoteke u otpad…}one{Premještanje <xliff:g id="COUNT">^1</xliff:g> audiodatoteke u otpad…}few{Premještanje <xliff:g id="COUNT">^1</xliff:g> audiodatoteke u otpad…}other{Premještanje <xliff:g id="COUNT">^1</xliff:g> audiodatoteka u otpad…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g> da premjesti taj videozapis u otpad?}one{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> videozapis u otpad?}few{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> videozapisa u otpad?}other{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> videozapisa u otpad?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Premještanje videozapisa u otpad…}one{Premještanje <xliff:g id="COUNT">^1</xliff:g> videozapisa u otpad…}few{Premještanje <xliff:g id="COUNT">^1</xliff:g> videozapisa u otpad…}other{Premještanje <xliff:g id="COUNT">^1</xliff:g> videozapisa u otpad…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g>da premjesti tu fotografiju u otpad?}one{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> fotografiju u otpad?}few{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> fotografije u otpad?}other{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> fotografija u otpad?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Premještanje fotografije u otpad…}one{Premještanje <xliff:g id="COUNT">^1</xliff:g> fotografije u otpad…}few{Premještanje <xliff:g id="COUNT">^1</xliff:g> fotografije u otpad…}other{Premještanje <xliff:g id="COUNT">^1</xliff:g> fotografija u otpad…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g> da premjesti tu stavku u otpad?}one{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> stavku u otpad?}few{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> stavke u otpad?}other{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> stavki u otpad?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Premještanje stavke u otpad…}one{Premještanje <xliff:g id="COUNT">^1</xliff:g> stavke u otpad…}few{Premještanje <xliff:g id="COUNT">^1</xliff:g> stavke u otpad…}other{Premještanje <xliff:g id="COUNT">^1</xliff:g> stavki u otpad…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g> da premjesti tu audiodatoteku iz otpada?}one{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> audiodatoteku iz otpada?}few{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> audiodatoteke iz otpada?}other{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> audiodatoteka iz otpada?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Premještanje audiodatoteke iz otpada…}one{Premještanje <xliff:g id="COUNT">^1</xliff:g> audiodatoteke iz otpada…}few{Premještanje <xliff:g id="COUNT">^1</xliff:g> audiodatoteke iz otpada…}other{Premještanje <xliff:g id="COUNT">^1</xliff:g> audiodatoteka iz otpada…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g> da premjesti taj videozapis iz otpada?}one{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> videozapis iz otpada?}few{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> videozapisa iz otpada?}other{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> videozapisa iz otpada?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Premještanje videozapisa iz otpada…}one{Premještanje <xliff:g id="COUNT">^1</xliff:g> videozapisa iz otpada…}few{Premještanje <xliff:g id="COUNT">^1</xliff:g> videozapisa iz otpada…}other{Premještanje <xliff:g id="COUNT">^1</xliff:g> videozapisa iz otpada…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g> da premjesti tu fotografiju iz otpada?}one{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> fotografiju iz otpada?}few{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> fotografije iz otpada?}other{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> fotografija iz otpada?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Premještanje fotografije iz otpada…}one{Premještanje <xliff:g id="COUNT">^1</xliff:g> fotografije iz otpada…}few{Premještanje <xliff:g id="COUNT">^1</xliff:g> fotografije iz otpada…}other{Premještanje <xliff:g id="COUNT">^1</xliff:g> fotografija iz otpada…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g> da premjesti tu stavku iz otpada?}one{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> stavku iz otpada?}few{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> stavke iz otpada?}other{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da premjesti <xliff:g id="COUNT">^2</xliff:g> stavki iz otpada?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Premještanje stavke iz otpada…}one{Premještanje <xliff:g id="COUNT">^1</xliff:g> stavke iz otpada…}few{Premještanje <xliff:g id="COUNT">^1</xliff:g> stavke iz otpada…}other{Premještanje <xliff:g id="COUNT">^1</xliff:g> stavki iz otpada…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g> da izbriše tu audiodatoteku?}one{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izbriše <xliff:g id="COUNT">^2</xliff:g> audiodatoteku?}few{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izbriše <xliff:g id="COUNT">^2</xliff:g> audiodatoteke?}other{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izbriše <xliff:g id="COUNT">^2</xliff:g> audiodatoteka?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Brisanje audiodatoteke…}one{Brisanje <xliff:g id="COUNT">^1</xliff:g> audiodatoteke…}few{Brisanje <xliff:g id="COUNT">^1</xliff:g> audiodatoteke…}other{Brisanje <xliff:g id="COUNT">^1</xliff:g> audiodatoteka…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g> da izbriše taj videozapis?}one{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izbriše <xliff:g id="COUNT">^2</xliff:g> videozapis?}few{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izbriše <xliff:g id="COUNT">^2</xliff:g> videozapisa?}other{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izbriše <xliff:g id="COUNT">^2</xliff:g> videozapisa?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Brisanje videozapisa…}one{Brisanje <xliff:g id="COUNT">^1</xliff:g> videozapisa…}few{Brisanje <xliff:g id="COUNT">^1</xliff:g> videozapisa…}other{Brisanje <xliff:g id="COUNT">^1</xliff:g> videozapisa…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g> da izbriše tu fotografiju?}one{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izbriše <xliff:g id="COUNT">^2</xliff:g> fotografiju?}few{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izbriše <xliff:g id="COUNT">^2</xliff:g> fotografije?}other{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izbriše <xliff:g id="COUNT">^2</xliff:g> fotografija?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Brisanje fotografije…}one{Brisanje <xliff:g id="COUNT">^1</xliff:g> fotografije…}few{Brisanje <xliff:g id="COUNT">^1</xliff:g> fotografije…}other{Brisanje <xliff:g id="COUNT">^1</xliff:g> fotografija…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g> da izbriše tu stavku?}one{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izbriše <xliff:g id="COUNT">^2</xliff:g> stavku?}few{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izbriše <xliff:g id="COUNT">^2</xliff:g> stavke?}other{Želite li dopustiti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g> da izbriše <xliff:g id="COUNT">^2</xliff:g> stavki?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Brisanje stavke…}one{Brisanje <xliff:g id="COUNT">^1</xliff:g> stavke…}few{Brisanje <xliff:g id="COUNT">^1</xliff:g> stavke…}other{Brisanje <xliff:g id="COUNT">^1</xliff:g> stavki…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"Aplikacija <xliff:g id="APP_NAME">%s</xliff:g> ne može obraditi medijske datoteke"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Obrada medijskih sadržaja otkazana"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Pogreška prilikom obrade medijskih sadržaja"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index e3ea512..b383849 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Fotóválasztó"</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>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Folytatás"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Engedélyezés"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Tiltás"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">És további <xliff:g id="COUNT_1">^1</xliff:g> elem</item>
- <item quantity="one">És további <xliff:g id="COUNT_0">^1</xliff:g> elem</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{És további <xliff:g id="COUNT_0">^1</xliff:g> elem}other{És további <xliff:g id="COUNT_1">^1</xliff:g> elem}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Ideiglenes alkalmazásfájlok törlése"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"A(z) <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> szeretne törölni néhány ideiglenes fájlt. Ez nagyobb akkumulátor- és mobiladat-használatot eredményezhet."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Ideiglenes alkalmazásfájlok törlése…"</string>
<string name="clear" msgid="5524638938415865915">"Törlés"</string>
<string name="allow" msgid="8885707816848569619">"Engedélyezés"</string>
<string name="deny" msgid="6040983710442068936">"Tiltás"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> hangfájl módosítását?</item>
- <item quantity="one">Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek a hangfájlnak a módosítását?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> audiofájl módosítása folyamatban van…</item>
- <item quantity="one">Az audiofájl módosítása folyamatban van…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> videó módosítását?</item>
- <item quantity="one">Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek a videónak a módosítását?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> videó módosítása folyamatban van…</item>
- <item quantity="one">A videó módosítása folyamatban van…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> fotó módosítását?</item>
- <item quantity="one">Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek a fotónak a módosítását?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> fotó módosítása folyamatban van…</item>
- <item quantity="one">A fotó módosítása folyamatban van…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> elem módosítását?</item>
- <item quantity="one">Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek az elemnek a módosítását?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> elem módosítása folyamatban van…</item>
- <item quantity="one">Az elem módosítása folyamatban van…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> hangfájlnak a kukába helyezését?</item>
- <item quantity="one">Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek a hangfájlnak a kukába helyezését?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> audiofájl áthelyezése a kukába…</item>
- <item quantity="one">Az audiofájl áthelyezése a kukába…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> videónak a kukába helyezését?</item>
- <item quantity="one">Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek a videónak a kukába helyezését?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> videó áthelyezése a kukába…</item>
- <item quantity="one">Videó áthelyezése a kukába…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> fotónak a kukába helyezését?</item>
- <item quantity="one">Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek a fotónak a kukába helyezését?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> fotó áthelyezése a kukába…</item>
- <item quantity="one">Fotó áthelyezése a kukába…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> elemnek a kukába helyezését?</item>
- <item quantity="one">Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek az elemnek a kukába helyezését?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> elem áthelyezése a kukába…</item>
- <item quantity="one">Az elem áthelyezése a kukába…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> hangfájlnak a kukából való visszaállítását?</item>
- <item quantity="one">Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek a hangfájlnak a kukából való visszaállítását?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> audiofájl visszaállítása a kukából…</item>
- <item quantity="one">Audiofájl visszaállítása a kukából…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> videónak a kukából való visszaállítását?</item>
- <item quantity="one">Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek a videónak a kukából való visszaállítását?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> videó visszaállítása a kukából…</item>
- <item quantity="one">Videó visszaállítása a kukából…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> fotónak a kukából való visszaállítását?</item>
- <item quantity="one">Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek a fotónak a kukából való visszaállítását?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> fotó visszaállítása a kukából…</item>
- <item quantity="one">Fotó visszaállítása a kukából…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> elemnek a kukából való visszaállítását?</item>
- <item quantity="one">Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek az elemnek a kukából való visszaállítását?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> elem visszaállítása a kukából…</item>
- <item quantity="one">Elem visszaállítása a kukából…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> audiofájl törlését?</item>
- <item quantity="one">Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek az audiofájlnak a törlését?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> audiofájl törlése folyamatban van…</item>
- <item quantity="one">Az audiofájl törlése folyamatban van…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> videó törlését?</item>
- <item quantity="one">Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek a videónak a törlését?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> videó törlése folyamatban van…</item>
- <item quantity="one">A videó törlése folyamatban van…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> fotó törlését?</item>
- <item quantity="one">Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek a fotónak a törlését?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> fotó törlése folyamatban van…</item>
- <item quantity="one">A fotó törlése folyamatban van…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> elem törlését?</item>
- <item quantity="one">Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek az elemnek a törlését?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> elem törlése folyamatban van…</item>
- <item quantity="one">Az elem törlése folyamatban van…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Hozzáadás"</string>
+ <string name="deselect" msgid="4297825044827769490">"Jelölés törlése"</string>
+ <string name="deselected" msgid="8488133193326208475">"Kijelölés megszüntetve"</string>
+ <string name="select" msgid="2704765470563027689">"Kiválasztás"</string>
+ <string name="selected" msgid="9151797369975828124">"Kijelölve"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Legfeljebb <xliff:g id="COUNT_0">^1</xliff:g> elemet jelölhet ki}other{Legfeljebb <xliff:g id="COUNT_1">^1</xliff:g> elemet jelölhet ki}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Legújabbak"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Nincsenek képek vagy videók"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Nincsenek albumok"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Kijelöltek megnézése"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Fotók"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albumok"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Előnézet"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Átváltás munkaprofilra"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Átváltás személyes profilra"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Rendszergazda által letiltva"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"A munkahelyi adatokhoz való hozzáférés személyes alkalmazással nincs engedélyezve"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"A személyes adatokhoz való hozzáférés munkahelyi alkalmazással nincs engedélyezve"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"A munkahelyi alkalmazások szüneteltetve vannak"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"A munkahelyi fotók megnyitásához kapcsolja be a munkahelyi alkalmazásokat, majd próbálkozzon újra"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Ez az alkalmazás csak az Ön által kiválasztott fotókhoz férhet hozzá"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> elem}other{<xliff:g id="COUNT_1">^1</xliff:g> elem}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Hozzáadás (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Letöltések"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Kedvencek"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Képernyőképek"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Mozgókép"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g>, készítés időpontja: <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Videó készítésének ideje és hossza: <xliff:g id="TIME">%1$s</xliff:g>, <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Fotó"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Mozgó fotó"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Videó némítása"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Videó némításának feloldása"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Videó lejátszása"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Videó szüneteltetése"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"A felhőbeli médiatartalmak már hozzáférhetők a következőből: <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"nincs kiválasztva"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek a hangfájlnak a módosítását?}other{Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> hangfájl módosítását?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Az audiofájl módosítása folyamatban van…}other{<xliff:g id="COUNT">^1</xliff:g> audiofájl módosítása folyamatban van…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek a videónak a módosítását?}other{Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> videó módosítását?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{A videó módosítása folyamatban van…}other{<xliff:g id="COUNT">^1</xliff:g> videó módosítása folyamatban van…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek a fotónak a módosítását?}other{Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> fotó módosítását?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{A fotó módosítása folyamatban van…}other{<xliff:g id="COUNT">^1</xliff:g> fotó módosítása folyamatban van…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek az elemnek a módosítását?}other{Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> elem módosítását?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Az elem módosítása folyamatban van…}other{<xliff:g id="COUNT">^1</xliff:g> elem módosítása folyamatban van…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek a hangfájlnak a kukába helyezését?}other{Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> hangfájlnak a kukába helyezését?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Az audiofájl áthelyezése a kukába…}other{<xliff:g id="COUNT">^1</xliff:g> audiofájl áthelyezése a kukába…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek a videónak a kukába helyezését?}other{Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> videónak a kukába helyezését?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Videó áthelyezése a kukába…}other{<xliff:g id="COUNT">^1</xliff:g> videó áthelyezése a kukába…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek a fotónak a kukába helyezését?}other{Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> fotónak a kukába helyezését?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Fotó áthelyezése a kukába…}other{<xliff:g id="COUNT">^1</xliff:g> fotó áthelyezése a kukába…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek az elemnek a kukába helyezését?}other{Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> elemnek a kukába helyezését?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Az elem áthelyezése a kukába…}other{<xliff:g id="COUNT">^1</xliff:g> elem áthelyezése a kukába…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek a hangfájlnak a kukából való visszaállítását?}other{Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> hangfájlnak a kukából való visszaállítását?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Audiofájl visszaállítása a kukából…}other{<xliff:g id="COUNT">^1</xliff:g> audiofájl visszaállítása a kukából…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek a videónak a kukából való visszaállítását?}other{Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> videónak a kukából való visszaállítását?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Videó visszaállítása a kukából…}other{<xliff:g id="COUNT">^1</xliff:g> videó visszaállítása a kukából…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek a fotónak a kukából való visszaállítását?}other{Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> fotónak a kukából való visszaállítását?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Fotó visszaállítása a kukából…}other{<xliff:g id="COUNT">^1</xliff:g> fotó visszaállítása a kukából…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek az elemnek a kukából való visszaállítását?}other{Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> elemnek a kukából való visszaállítását?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Elem visszaállítása a kukából…}other{<xliff:g id="COUNT">^1</xliff:g> elem visszaállítása a kukából…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek az audiofájlnak a törlését?}other{Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> audiofájl törlését?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Az audiofájl törlése folyamatban van…}other{<xliff:g id="COUNT">^1</xliff:g> audiofájl törlése folyamatban van…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek a videónak a törlését?}other{Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> videó törlését?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{A videó törlése folyamatban van…}other{<xliff:g id="COUNT">^1</xliff:g> videó törlése folyamatban van…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek a fotónak a törlését?}other{Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> fotó törlését?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{A fotó törlése folyamatban van…}other{<xliff:g id="COUNT">^1</xliff:g> fotó törlése folyamatban van…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Engedélyezi a(z) <xliff:g id="APP_NAME_0">^1</xliff:g> számára ennek az elemnek a törlését?}other{Engedélyezi a(z) <xliff:g id="APP_NAME_1">^1</xliff:g> számára <xliff:g id="COUNT">^2</xliff:g> elem törlését?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Az elem törlése folyamatban van…}other{<xliff:g id="COUNT">^1</xliff:g> elem törlése folyamatban van…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"A(z) <xliff:g id="APP_NAME">%s</xliff:g> nem tudja feldolgozni a médiafájlokat"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Mediatartalom feldolgozása megszakítva"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Médiatartalom-feldolgozási hiba"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 095e238..5c6112e 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Մեդիա"</string>
<string name="storage_description" msgid="4081716890357580107">"Սարքի հիշողություն"</string>
<string name="app_label" msgid="9035307001052716210">"Մեդիա կրիչ"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"Լուսանկարի ընտրիչ"</string>
<string name="artist_label" msgid="8105600993099120273">"Կատարող"</string>
<string name="unknown" msgid="2059049215682829375">"Անհայտ"</string>
<string name="root_images" msgid="5861633549189045666">"Պատկերներ"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Շարունակել"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Թույլատրել"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Մերժել"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">Ու ևս <xliff:g id="COUNT_1">^1</xliff:g> լրացուցիչ տարր</item>
- <item quantity="other">Ու ևս <xliff:g id="COUNT_1">^1</xliff:g> լրացուցիչ տարր</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}one{+<xliff:g id="COUNT_1">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Ու ևս <xliff:g id="COUNT_0">^1</xliff:g> լրացուցիչ տարր}one{Ու ևս <xliff:g id="COUNT_1">^1</xliff:g> լրացուցիչ տարր}other{Ու ևս <xliff:g id="COUNT_1">^1</xliff:g> լրացուցիչ տարր}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Ջնջեք հավելվածի լրացուցիչ ֆայլերը"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> հավելվածը հայցում է որոշ ժամանակավոր ֆայլեր ջնջելու թույլտվություն։ Դրա համար կարող է օգտագործվել ավելի շատ մարտկոցի լիցք կամ բջջային ինտերնետ։"</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Հավելվածների ժամանակավոր ֆայլերը ջնջվում են…"</string>
<string name="clear" msgid="5524638938415865915">"Ջնջել"</string>
<string name="allow" msgid="8885707816848569619">"Թույլատրել"</string>
<string name="deny" msgid="6040983710442068936">"Մերժել"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին վերականգնել <xliff:g id="COUNT">^2</xliff:g> աուդիո ֆայլ աղբարկղից</item>
- <item quantity="other">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին վերականգնել <xliff:g id="COUNT">^2</xliff:g> աուդիո ֆայլ աղբարկղից</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> աուդիո ֆայլ փոփոխվում է…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> աուդիո ֆայլ փոփոխվում է…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին փոփոխել <xliff:g id="COUNT">^2</xliff:g> տեսանյութ</item>
- <item quantity="other">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին փոփոխել <xliff:g id="COUNT">^2</xliff:g> տեսանյութ</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> տեսանյութ փոփոխվում է…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> տեսանյութ փոփոխվում է…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին փոփոխել <xliff:g id="COUNT">^2</xliff:g> լուսանկար</item>
- <item quantity="other">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին փոփոխել <xliff:g id="COUNT">^2</xliff:g> լուսանկար</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> լուսանկար փոփոխվում է…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> լուսանկար փոփոխվում է…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին փոփոխել <xliff:g id="COUNT">^2</xliff:g> տարր</item>
- <item quantity="other">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին փոփոխել <xliff:g id="COUNT">^2</xliff:g> տարր</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> տարր փոփոխվում է…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> տարր փոփոխվում է…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին տեղափոխել <xliff:g id="COUNT">^2</xliff:g> աուդիո ֆայլ աղբարկղ</item>
- <item quantity="other">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին տեղափոխել <xliff:g id="COUNT">^2</xliff:g> աուդիո ֆայլ աղբարկղ</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> աուդիո ֆայլ տեղափոխվում է աղբարկղ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> աուդիո ֆայլ տեղափոխվում է աղբարկղ…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին տեղափոխել <xliff:g id="COUNT">^2</xliff:g> տեսանյութ աղբարկղ</item>
- <item quantity="other">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին տեղափոխել <xliff:g id="COUNT">^2</xliff:g> տեսանյութ աղբարկղ</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> տեսանյութ տեղափոխվում է աղբարկղ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> տեսանյութ տեղափոխվում է աղբարկղ…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին տեղափոխել <xliff:g id="COUNT">^2</xliff:g> լուսանկար աղբարկղ</item>
- <item quantity="other">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին տեղափոխել <xliff:g id="COUNT">^2</xliff:g> լուսանկար աղբարկղ</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> լուսանկար տեղափոխվում է աղբարկղ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> լուսանկար տեղափոխվում է աղբարկղ…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին տեղափոխել <xliff:g id="COUNT">^2</xliff:g> տարր աղբարկղ</item>
- <item quantity="other">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին տեղափոխել <xliff:g id="COUNT">^2</xliff:g> տարր աղբարկղ</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> տարր տեղափոխվում է աղբարկղ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> տարր տեղափոխվում է աղբարկղ…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին վերականգնել<xliff:g id="COUNT">^2</xliff:g> աուդիո ֆայլ աղբարկղից</item>
- <item quantity="other">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին վերականգնել<xliff:g id="COUNT">^2</xliff:g> աուդիո ֆայլ աղբարկղից</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> աուդիո ֆայլ վերականգնվում է աղբարկղից…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> աուդիո ֆայլ վերականգնվում է աղբարկղից…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին վերականգնել <xliff:g id="COUNT">^2</xliff:g> տեսանյութ աղբարկղից</item>
- <item quantity="other">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին վերականգնել <xliff:g id="COUNT">^2</xliff:g> տեսանյութ աղբարկղից</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> տեսանյութ վերականգնվում է աղբարկղից…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> տեսանյութ վերականգնվում է աղբարկղից…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին վերականգնել <xliff:g id="COUNT">^2</xliff:g> լուսանկար աղբարկղից</item>
- <item quantity="other">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին վերականգնել <xliff:g id="COUNT">^2</xliff:g> լուսանկար աղբարկղից</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> լուսանկար վերականգնվում է աղբարկղից…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> լուսանկար վերականգնվում է աղբարկղից…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին վերականգնել <xliff:g id="COUNT">^2</xliff:g> տարր աղբարկղից</item>
- <item quantity="other">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին վերականգնել <xliff:g id="COUNT">^2</xliff:g> տարր աղբարկղից</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> տարր վերականգնվում է աղբարկղից…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> տարր վերականգնվում է աղբարկղից…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին ջնջել <xliff:g id="COUNT">^2</xliff:g> աուդիո ֆայլ</item>
- <item quantity="other">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին ջնջել <xliff:g id="COUNT">^2</xliff:g> աուդիո ֆայլ</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> աուդիո ֆայլ ջնջվում է…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> աուդիո ֆայլ ջնջվում է…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին ջնջել <xliff:g id="COUNT">^2</xliff:g> տեսանյութ</item>
- <item quantity="other">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին ջնջել <xliff:g id="COUNT">^2</xliff:g> տեսանյութ</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> տեսանյութ ջնջվում է…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> տեսանյութ ջնջվում է…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին ջնջել <xliff:g id="COUNT">^2</xliff:g> լուսանկար</item>
- <item quantity="other">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին ջնջել <xliff:g id="COUNT">^2</xliff:g> լուսանկար</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> լուսանկար ջնջվում է…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> լուսանկար ջնջվում է…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին ջնջել <xliff:g id="COUNT">^2</xliff:g> տարր</item>
- <item quantity="other">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին ջնջել <xliff:g id="COUNT">^2</xliff:g> տարր</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> տարր ջնջվում է…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> տարր ջնջվում է…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Ավելացնել"</string>
+ <string name="deselect" msgid="4297825044827769490">"Ապընտրել"</string>
+ <string name="deselected" msgid="8488133193326208475">"Ապընտրված"</string>
+ <string name="select" msgid="2704765470563027689">"Ընտրել"</string>
+ <string name="selected" msgid="9151797369975828124">"Ընտրված"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Ընտրեք մինչև <xliff:g id="COUNT_0">^1</xliff:g> տարր}one{Ընտրեք մինչև <xliff:g id="COUNT_1">^1</xliff:g> տարր}other{Ընտրեք մինչև <xliff:g id="COUNT_1">^1</xliff:g> տարր}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Վերջինները"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Լուսանկարներ կամ տեսանյութեր չկան"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Ալբոմներ չկան"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Դիտել ընտրվածը"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Լուսանկարներ"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Ալբոմներ"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Նախադիտում"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Բացել աշխատանքային պրոֆիլը"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Բացել անձնական պրոֆիլը"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Արգելափակվել է ձեր ադմինիստրատորի կողմից"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Աշխատանքային տվյալները հասանելի չեն անձնական հավելվածում"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Անձնական տվյալները հասանելի չեն աշխատանքային հավելվածում"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Աշխատանքային հավելվածները դադարեցված են"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Աշխատանքային լուսանկարները բացելու համար միացրեք աշխատանքային հավելվածները և նորից փորձեք։"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Այս հավելվածին հասանելի են միայն ձեր ընտրած լուսանկարները"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> տարր}one{<xliff:g id="COUNT_1">^1</xliff:g> տարր}other{<xliff:g id="COUNT_1">^1</xliff:g> տարր}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Ավելացնել (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Տեսախցիկ"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Ներբեռնումներ"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Ընտրանի"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Սքրինշոթներ"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Շարժվող լուսանկար"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g>, որն արվել է <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Տեսանյութ․ նկարահանման ամսաթիվը և ժամը՝ <xliff:g id="TIME">%1$s</xliff:g>, տևողությունը՝ <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Լուսանկար"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Շարժվող լուսանկար"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Անջատել տեսանյութի ձայնը"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Միացնել տեսանյութի ձայնը"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Նվագարկել տեսանյութը"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Դադարեցնել տեսանյութի նվագարկումը"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Ամպային մեդիա բովանդակությունն այժմ հասանելի է <xliff:g id="PKG_NAME">%1$s</xliff:g> հավելվածից"</string>
+ <string name="not_selected" msgid="2244008151669896758">"ընտրված չէ"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Թույլատրե՞լ <xliff:g id="APP_NAME_0">^1</xliff:g> հավելվածին վերականգնել այս աուդիո ֆայլն աղբարկղից}one{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին վերականգնել <xliff:g id="COUNT">^2</xliff:g> աուդիո ֆայլ աղբարկղից}other{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին վերականգնել <xliff:g id="COUNT">^2</xliff:g> աուդիո ֆայլ աղբարկղից}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Աուդիո ֆայլը փոփոխվում է…}one{<xliff:g id="COUNT">^1</xliff:g> աուդիո ֆայլ փոփոխվում է…}other{<xliff:g id="COUNT">^1</xliff:g> աուդիո ֆայլ փոփոխվում է…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Թույլատրե՞լ <xliff:g id="APP_NAME_0">^1</xliff:g> հավելվածին փոփոխել այս տեսանյութը}one{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին փոփոխել <xliff:g id="COUNT">^2</xliff:g> տեսանյութ}other{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին փոփոխել <xliff:g id="COUNT">^2</xliff:g> տեսանյութ}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Տեսանյութը փոփոխվում է…}one{<xliff:g id="COUNT">^1</xliff:g> տեսանյութ փոփոխվում է…}other{<xliff:g id="COUNT">^1</xliff:g> տեսանյութ փոփոխվում է…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Թույլատրե՞լ <xliff:g id="APP_NAME_0">^1</xliff:g> հավելվածին փոփոխել այս լուսանկարը}one{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին փոփոխել <xliff:g id="COUNT">^2</xliff:g> լուսանկար}other{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին փոփոխել <xliff:g id="COUNT">^2</xliff:g> լուսանկար}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Լուսանկարը փոփոխվում է…}one{<xliff:g id="COUNT">^1</xliff:g> լուսանկար փոփոխվում է…}other{<xliff:g id="COUNT">^1</xliff:g> լուսանկար փոփոխվում է…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Թույլատրե՞լ <xliff:g id="APP_NAME_0">^1</xliff:g> հավելվածին փոփոխել այս տարրը}one{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին փոփոխել <xliff:g id="COUNT">^2</xliff:g> տարր}other{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին փոփոխել <xliff:g id="COUNT">^2</xliff:g> տարր}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Տարրը փոփոխվում է…}one{<xliff:g id="COUNT">^1</xliff:g> տարր փոփոխվում է…}other{<xliff:g id="COUNT">^1</xliff:g> տարր փոփոխվում է…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Թույլատրե՞լ <xliff:g id="APP_NAME_0">^1</xliff:g> հավելվածին տեղափոխել այս աուդիո ֆայլն աղբարկղ}one{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին տեղափոխել <xliff:g id="COUNT">^2</xliff:g> աուդիո ֆայլ աղբարկղ}other{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին տեղափոխել <xliff:g id="COUNT">^2</xliff:g> աուդիո ֆայլ աղբարկղ}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Աուդիո ֆայլը տեղափոխվում է աղբարկղ…}one{<xliff:g id="COUNT">^1</xliff:g> աուդիո ֆայլ տեղափոխվում է աղբարկղ…}other{<xliff:g id="COUNT">^1</xliff:g> աուդիո ֆայլ տեղափոխվում է աղբարկղ…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Թույլատրե՞լ <xliff:g id="APP_NAME_0">^1</xliff:g> հավելվածին տեղափոխել այս տեսանյութն աղբարկղ}one{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին տեղափոխել <xliff:g id="COUNT">^2</xliff:g> տեսանյութ աղբարկղ}other{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին տեղափոխել <xliff:g id="COUNT">^2</xliff:g> տեսանյութ աղբարկղ}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Տեսանյութը տեղափոխվում է աղբարկղ…}one{<xliff:g id="COUNT">^1</xliff:g> տեսանյութ տեղափոխվում է աղբարկղ…}other{<xliff:g id="COUNT">^1</xliff:g> տեսանյութ տեղափոխվում է աղբարկղ…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Թույլատրե՞լ <xliff:g id="APP_NAME_0">^1</xliff:g> հավելվածին տեղափոխել այս լուսանկարն աղբարկղ}one{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին տեղափոխել <xliff:g id="COUNT">^2</xliff:g> լուսանկար աղբարկղ}other{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին տեղափոխել <xliff:g id="COUNT">^2</xliff:g> լուսանկար աղբարկղ}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Լուսանկարը տեղափոխվում է աղբարկղ…}one{<xliff:g id="COUNT">^1</xliff:g> լուսանկար տեղափոխվում է աղբարկղ…}other{<xliff:g id="COUNT">^1</xliff:g> լուսանկար տեղափոխվում է աղբարկղ…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Թույլատրե՞լ <xliff:g id="APP_NAME_0">^1</xliff:g> հավելվածին տեղափոխել այս տարրն աղբարկղ}one{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին տեղափոխել <xliff:g id="COUNT">^2</xliff:g> տարր աղբարկղ}other{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին տեղափոխել <xliff:g id="COUNT">^2</xliff:g> տարր աղբարկղ}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Տարրը տեղափոխվում է աղբարկղ…}one{<xliff:g id="COUNT">^1</xliff:g> տարր տեղափոխվում է աղբարկղ…}other{<xliff:g id="COUNT">^1</xliff:g> տարր տեղափոխվում է աղբարկղ…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Թույլատրե՞լ <xliff:g id="APP_NAME_0">^1</xliff:g> հավելվածին վերականգնել այս աուդիո ֆայլն աղբարկղից}one{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին վերականգնել <xliff:g id="COUNT">^2</xliff:g> աուդիո ֆայլ աղբարկղից}other{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին վերականգնել <xliff:g id="COUNT">^2</xliff:g> աուդիո ֆայլ աղբարկղից}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Աուդիո ֆայլը վերականգնվում է աղբարկղից…}one{<xliff:g id="COUNT">^1</xliff:g> աուդիո ֆայլ վերականգնվում է աղբարկղից…}other{<xliff:g id="COUNT">^1</xliff:g> աուդիո ֆայլ վերականգնվում է աղբարկղից…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Թույլատրե՞լ <xliff:g id="APP_NAME_0">^1</xliff:g> հավելվածին վերականգնել այս տեսանյութն աղբարկղից}one{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին վերականգնել <xliff:g id="COUNT">^2</xliff:g> տեսանյութ աղբարկղից}other{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին վերականգնել <xliff:g id="COUNT">^2</xliff:g> տեսանյութ աղբարկղից}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Տեսանյութը վերականգնվում է աղբարկղից…}one{<xliff:g id="COUNT">^1</xliff:g> տեսանյութ վերականգնվում է աղբարկղից…}other{<xliff:g id="COUNT">^1</xliff:g> տեսանյութ վերականգնվում է աղբարկղից…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Թույլատրե՞լ <xliff:g id="APP_NAME_0">^1</xliff:g> հավելվածին վերականգնել այս լուսանկարն աղբարկղից}one{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին վերականգնել <xliff:g id="COUNT">^2</xliff:g> լուսանկար աղբարկղից}other{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին վերականգնել <xliff:g id="COUNT">^2</xliff:g> լուսանկար աղբարկղից}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Լուսանկարը վերականգնվում է աղբարկղից…}one{<xliff:g id="COUNT">^1</xliff:g> լուսանկար վերականգնվում է աղբարկղից…}other{<xliff:g id="COUNT">^1</xliff:g> լուսանկար վերականգնվում է աղբարկղից…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Թույլատրե՞լ <xliff:g id="APP_NAME_0">^1</xliff:g> հավելվածին վերականգնել այս տարրն աղբարկղից}one{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին վերականգնել <xliff:g id="COUNT">^2</xliff:g> տարր աղբարկղից}other{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին վերականգնել <xliff:g id="COUNT">^2</xliff:g> տարր աղբարկղից}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Տարրը վերականգնվում է աղբարկղից…}one{<xliff:g id="COUNT">^1</xliff:g> տարր վերականգնվում է աղբարկղից…}other{<xliff:g id="COUNT">^1</xliff:g> տարր վերականգնվում է աղբարկղից…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Թույլատրե՞լ <xliff:g id="APP_NAME_0">^1</xliff:g> հավելվածին ջնջել այս աուդիո ֆայլը}one{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին ջնջել <xliff:g id="COUNT">^2</xliff:g> աուդիո ֆայլ}other{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին ջնջել <xliff:g id="COUNT">^2</xliff:g> աուդիո ֆայլ}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Աուդիո ֆայլը ջնջվում է…}one{<xliff:g id="COUNT">^1</xliff:g> աուդիո ֆայլ ջնջվում է…}other{<xliff:g id="COUNT">^1</xliff:g> աուդիո ֆայլ ջնջվում է…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Թույլատրե՞լ <xliff:g id="APP_NAME_0">^1</xliff:g> հավելվածին ջնջել այս տեսանյութը}one{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին ջնջել <xliff:g id="COUNT">^2</xliff:g> տեսանյութ}other{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին ջնջել <xliff:g id="COUNT">^2</xliff:g> տեսանյութ}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Տեսանյութը ջնջվում է…}one{<xliff:g id="COUNT">^1</xliff:g> տեսանյութ ջնջվում է…}other{<xliff:g id="COUNT">^1</xliff:g> տեսանյութ ջնջվում է…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Թույլատրե՞լ <xliff:g id="APP_NAME_0">^1</xliff:g> հավելվածին ջնջել այս լուսանկարը}one{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին ջնջել <xliff:g id="COUNT">^2</xliff:g> լուսանկար}other{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին ջնջել <xliff:g id="COUNT">^2</xliff:g> լուսանկար}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Լուսանկարը ջնջվում է…}one{<xliff:g id="COUNT">^1</xliff:g> լուսանկար ջնջվում է…}other{<xliff:g id="COUNT">^1</xliff:g> լուսանկար ջնջվում է…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Թույլատրե՞լ <xliff:g id="APP_NAME_0">^1</xliff:g> հավելվածին ջնջել այս տարրը}one{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին ջնջել <xliff:g id="COUNT">^2</xliff:g> տարր}other{Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին ջնջել <xliff:g id="COUNT">^2</xliff:g> տարր}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Տարրը ջնջվում է…}one{<xliff:g id="COUNT">^1</xliff:g> տարր ջնջվում է…}other{<xliff:g id="COUNT">^1</xliff:g> տարր ջնջվում է…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> հավելվածը չի կարող մեդիաֆայլեր մշակել"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Մեդիաֆայլի մշակումը չեղարկվել է"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Մեդիաֆայլի մշակման սխալ"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 3987c55..4a474c3 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Pemilih foto"</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>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Lanjutkan"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Izinkan"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Tolak"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Plus <xliff:g id="COUNT_1">^1</xliff:g> item tambahan</item>
- <item quantity="one">Plus <xliff:g id="COUNT_0">^1</xliff:g> item tambahan</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Plus <xliff:g id="COUNT_0">^1</xliff:g> item tambahan}other{Plus <xliff:g id="COUNT_1">^1</xliff:g> item tambahan}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Menghapus file aplikasi sementara"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ingin menghapus beberapa file sementara. Ini dapat meningkatkan penggunaan baterai atau data seluler."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Menghapus file aplikasi sementara…"</string>
<string name="clear" msgid="5524638938415865915">"Hapus"</string>
<string name="allow" msgid="8885707816848569619">"Izinkan"</string>
<string name="deny" msgid="6040983710442068936">"Tolak"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> untuk mengubah <xliff:g id="COUNT">^2</xliff:g> file audio?</item>
- <item quantity="one">Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> untuk mengubah file audio ini?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">Mengubah <xliff:g id="COUNT">^1</xliff:g> file audio …</item>
- <item quantity="one">Mengubah file audio …</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> untuk mengubah <xliff:g id="COUNT">^2</xliff:g> video?</item>
- <item quantity="one">Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> untuk mengubah video ini?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">Mengubah <xliff:g id="COUNT">^1</xliff:g> video …</item>
- <item quantity="one">Mengubah video …</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> untuk mengubah <xliff:g id="COUNT">^2</xliff:g> foto?</item>
- <item quantity="one">Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> untuk mengubah foto ini?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">Mengubah <xliff:g id="COUNT">^1</xliff:g> foto …</item>
- <item quantity="one">Mengubah foto …</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> untuk mengubah <xliff:g id="COUNT">^2</xliff:g> item?</item>
- <item quantity="one">Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> untuk mengubah item ini?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">Mengubah <xliff:g id="COUNT">^1</xliff:g> item …</item>
- <item quantity="one">Mengubah item …</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> untuk memindahkan <xliff:g id="COUNT">^2</xliff:g> file audio ke sampah?</item>
- <item quantity="one">Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> untuk memindahkan file audio ini ke sampah?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">Memindahkan <xliff:g id="COUNT">^1</xliff:g> file audio ke sampah …</item>
- <item quantity="one">Memindahkan file audio ke sampah …</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> untuk memindahkan <xliff:g id="COUNT">^2</xliff:g> video ke sampah?</item>
- <item quantity="one">Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> untuk memindahkan video ini ke sampah?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">Memindahkan <xliff:g id="COUNT">^1</xliff:g> video ke sampah …</item>
- <item quantity="one">Memindahkan video ke sampah …</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> untuk memindahkan <xliff:g id="COUNT">^2</xliff:g> foto ke sampah?</item>
- <item quantity="one">Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> untuk memindahkan foto ini ke sampah?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">Memindahkan <xliff:g id="COUNT">^1</xliff:g> foto ke sampah …</item>
- <item quantity="one">Memindahkan foto ke sampah …</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> untuk memindahkan <xliff:g id="COUNT">^2</xliff:g> item ke sampah?</item>
- <item quantity="one">Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> untuk memindahkan item ini ke sampah?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">Memindahkan <xliff:g id="COUNT">^1</xliff:g> item ke sampah …</item>
- <item quantity="one">Memindahkan item ke sampah …</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> untuk mengeluarkan <xliff:g id="COUNT">^2</xliff:g> file audio dari sampah?</item>
- <item quantity="one">Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> untuk mengeluarkan file audio ini dari sampah?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">Memindahkan <xliff:g id="COUNT">^1</xliff:g> file audio dari sampah …</item>
- <item quantity="one">Memindahkan file audio dari sampah …</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> untuk mengeluarkan <xliff:g id="COUNT">^2</xliff:g> video dari sampah?</item>
- <item quantity="one">Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> untuk mengeluarkan video ini dari sampah?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">Memindahkan <xliff:g id="COUNT">^1</xliff:g> video dari sampah …</item>
- <item quantity="one">Memindahkan video dari sampah …</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> untuk mengeluarkan <xliff:g id="COUNT">^2</xliff:g> foto dari sampah?</item>
- <item quantity="one">Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> untuk mengeluarkan foto ini dari sampah?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">Memindahkan <xliff:g id="COUNT">^1</xliff:g> foto dari sampah …</item>
- <item quantity="one">Memindahkan foto dari sampah …</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> untuk mengeluarkan <xliff:g id="COUNT">^2</xliff:g> item dari sampah?</item>
- <item quantity="one">Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> untuk mengeluarkan item ini dari sampah?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">Memindahkan <xliff:g id="COUNT">^1</xliff:g> item dari sampah …</item>
- <item quantity="one">Memindahkan item dari sampah …</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> untuk menghapus <xliff:g id="COUNT">^2</xliff:g> file audio?</item>
- <item quantity="one">Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> untuk menghapus file audio ini?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">Menghapus <xliff:g id="COUNT">^1</xliff:g> file audio …</item>
- <item quantity="one">Menghapus file audio …</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> untuk menghapus <xliff:g id="COUNT">^2</xliff:g> video?</item>
- <item quantity="one">Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> untuk menghapus video ini?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">Menghapus <xliff:g id="COUNT">^1</xliff:g> video …</item>
- <item quantity="one">Menghapus video …</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> untuk menghapus <xliff:g id="COUNT">^2</xliff:g> foto?</item>
- <item quantity="one">Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> untuk menghapus foto ini?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">Menghapus <xliff:g id="COUNT">^1</xliff:g> foto …</item>
- <item quantity="one">Menghapus foto …</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> untuk menghapus <xliff:g id="COUNT">^2</xliff:g> item?</item>
- <item quantity="one">Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> untuk menghapus item ini?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">Menghapus <xliff:g id="COUNT">^1</xliff:g> item …</item>
- <item quantity="one">Menghapus item …</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Tambahkan"</string>
+ <string name="deselect" msgid="4297825044827769490">"Batalkan pilihan"</string>
+ <string name="deselected" msgid="8488133193326208475">"Batal dipilih"</string>
+ <string name="select" msgid="2704765470563027689">"Pilih"</string>
+ <string name="selected" msgid="9151797369975828124">"Dipilih"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Pilih hingga <xliff:g id="COUNT_0">^1</xliff:g> item}other{Pilih hingga <xliff:g id="COUNT_1">^1</xliff:g> item}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Terbaru"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Tidak ada foto atau video"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Tidak ada album"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Lihat yang dipilih"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Foto"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Album"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Pratinjau"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Beralih ke profil kerja"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Beralih ke profil pribadi"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Diblokir oleh admin Anda"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Mengakses data kerja dari aplikasi pribadi tidak diizinkan"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Mengakses data pribadi dari aplikasi kerja tidak diizinkan"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Aplikasi kerja dijeda"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Untuk membuka foto kerja, aktifkan aplikasi kerja lalu coba lagi"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Aplikasi ini hanya dapat mengakses foto yang Anda pilih"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}other{<xliff:g id="COUNT_1">^1</xliff:g> item}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Tambahkan (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Hasil download"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Favorit"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Screenshot"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Foto Motion"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> diambil pada <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Video diambil pada <xliff:g id="TIME">%1$s</xliff:g> dengan durasi <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Foto"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Foto Motion"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Bisukan video"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Bunyikan video"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Putar video"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Jeda video"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Media cloud kini tersedia dari <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"tidak dipilih"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengubah file audio ini?}other{Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengubah <xliff:g id="COUNT">^2</xliff:g> file audio?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Mengubah file audio …}other{Mengubah <xliff:g id="COUNT">^1</xliff:g> file audio …}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengubah video ini?}other{Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengubah <xliff:g id="COUNT">^2</xliff:g> video?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Mengubah video …}other{Mengubah <xliff:g id="COUNT">^1</xliff:g> video …}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengubah foto ini?}other{Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengubah <xliff:g id="COUNT">^2</xliff:g> foto?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Mengubah foto …}other{Mengubah <xliff:g id="COUNT">^1</xliff:g> foto …}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengubah item ini?}other{Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengubah <xliff:g id="COUNT">^2</xliff:g> item?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Mengubah item …}other{Mengubah <xliff:g id="COUNT">^1</xliff:g> item …}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> memindahkan file audio ini ke sampah?}other{Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> memindahkan <xliff:g id="COUNT">^2</xliff:g> file audio ke sampah?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Memindahkan file audio ke sampah …}other{Memindahkan <xliff:g id="COUNT">^1</xliff:g> file audio ke sampah …}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> memindahkan video ini ke sampah?}other{Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> memindahkan <xliff:g id="COUNT">^2</xliff:g> video ke sampah?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Memindahkan video ke sampah …}other{Memindahkan <xliff:g id="COUNT">^1</xliff:g> video ke sampah …}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> memindahkan foto ini ke sampah?}other{Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> memindahkan <xliff:g id="COUNT">^2</xliff:g> foto ke sampah?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Memindahkan foto ke sampah …}other{Memindahkan <xliff:g id="COUNT">^1</xliff:g> foto ke sampah …}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> memindahkan item ini ke sampah?}other{Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> memindahkan <xliff:g id="COUNT">^2</xliff:g> item ke sampah?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Memindahkan item ke sampah …}other{Memindahkan <xliff:g id="COUNT">^1</xliff:g> item ke sampah …}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> memindahkan file audio ini dari sampah?}other{Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> memindahkan <xliff:g id="COUNT">^2</xliff:g> file audio dari sampah?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Memindahkan file audio dari sampah …}other{Memindahkan <xliff:g id="COUNT">^1</xliff:g> file audio dari sampah …}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> memindahkan video ini dari sampah?}other{Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> memindahkan <xliff:g id="COUNT">^2</xliff:g> video dari sampah?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Memindahkan video dari sampah …}other{Memindahkan <xliff:g id="COUNT">^1</xliff:g> video dari sampah …}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> memindahkan foto ini dari sampah?}other{Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> memindahkan <xliff:g id="COUNT">^2</xliff:g> foto dari sampah?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Memindahkan foto dari sampah …}other{Memindahkan <xliff:g id="COUNT">^1</xliff:g> foto dari sampah …}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> memindahkan item ini dari sampah?}other{Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> memindahkan <xliff:g id="COUNT">^2</xliff:g> item dari sampah?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Memindahkan item dari sampah …}other{Memindahkan <xliff:g id="COUNT">^1</xliff:g> item dari sampah …}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> menghapus file audio ini?}other{Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> menghapus <xliff:g id="COUNT">^2</xliff:g> file audio?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Menghapus file audio …}other{Menghapus <xliff:g id="COUNT">^1</xliff:g> file audio …}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> menghapus video ini?}other{Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> menghapus <xliff:g id="COUNT">^2</xliff:g> video?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Menghapus video …}other{Menghapus <xliff:g id="COUNT">^1</xliff:g> video …}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> menghapus foto ini?}other{Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> menghapus <xliff:g id="COUNT">^2</xliff:g> foto?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Menghapus foto …}other{Menghapus <xliff:g id="COUNT">^1</xliff:g> foto …}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Izinkan <xliff:g id="APP_NAME_0">^1</xliff:g> menghapus item ini?}other{Izinkan <xliff:g id="APP_NAME_1">^1</xliff:g> menghapus <xliff:g id="COUNT">^2</xliff:g> item?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Menghapus item …}other{Menghapus <xliff:g id="COUNT">^1</xliff:g> item …}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> tidak dapat memproses file media"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Pemrosesan media dibatalkan"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Pemrosesan media mengalami error"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index cf0d7c8..b2ff858 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Myndaval"</string>
<string name="artist_label" msgid="8105600993099120273">"Flytjandi"</string>
<string name="unknown" msgid="2059049215682829375">"Óþekkt"</string>
<string name="root_images" msgid="5861633549189045666">"Myndir"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Áfram"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Leyfa"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Hafna"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">Auk <xliff:g id="COUNT_1">^1</xliff:g> atriðis til viðbótar</item>
- <item quantity="other">Auk <xliff:g id="COUNT_1">^1</xliff:g> atriða til viðbótar</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}one{+<xliff:g id="COUNT_1">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> atriði í viðbót}one{<xliff:g id="COUNT_1">^1</xliff:g> atriði í viðbót}other{<xliff:g id="COUNT_1">^1</xliff:g> atriði í viðbót}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Hreinsa tímabundnar forritaskrár"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vill fá að hreinsa sumar tímabundnar skrár. Þetta getur haft í för með sér aukna rafhlöðunotkun eða notkun farsímagagna."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Verið er að eyða tímabundnum forritaskrám…"</string>
<string name="clear" msgid="5524638938415865915">"Hreinsa"</string>
<string name="allow" msgid="8885707816848569619">"Leyfa"</string>
<string name="deny" msgid="6040983710442068936">"Hafna"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að breyta <xliff:g id="COUNT">^2</xliff:g> hljóðskrá?</item>
- <item quantity="other">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að breyta <xliff:g id="COUNT">^2</xliff:g> hljóðskrám?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one">Breytir <xliff:g id="COUNT">^1</xliff:g> hljóðskrá…</item>
- <item quantity="other">Breytir <xliff:g id="COUNT">^1</xliff:g> hljóðskrám…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að breyta <xliff:g id="COUNT">^2</xliff:g> myndskeiði?</item>
- <item quantity="other">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að breyta <xliff:g id="COUNT">^2</xliff:g> myndskeiðum?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one">Breytir <xliff:g id="COUNT">^1</xliff:g> myndskeiði…</item>
- <item quantity="other">Breytir <xliff:g id="COUNT">^1</xliff:g> myndskeiðum…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að breyta <xliff:g id="COUNT">^2</xliff:g> mynd?</item>
- <item quantity="other">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að breyta <xliff:g id="COUNT">^2</xliff:g> myndum?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one">Breytir <xliff:g id="COUNT">^1</xliff:g> mynd…</item>
- <item quantity="other">Breytir <xliff:g id="COUNT">^1</xliff:g> myndum…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að breyta <xliff:g id="COUNT">^2</xliff:g> atriði?</item>
- <item quantity="other">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að breyta <xliff:g id="COUNT">^2</xliff:g> atriðum?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one">Breytir <xliff:g id="COUNT">^1</xliff:g> atriði…</item>
- <item quantity="other">Breytir <xliff:g id="COUNT">^1</xliff:g> atriðum…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> hljóðskrá í ruslið?</item>
- <item quantity="other">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> hljóðskrár í ruslið?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one">Færir <xliff:g id="COUNT">^1</xliff:g> hljóðskrá í ruslið…</item>
- <item quantity="other">Færir <xliff:g id="COUNT">^1</xliff:g> hljóðskrár í ruslið…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> myndskeið í ruslið?</item>
- <item quantity="other">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> myndskeið í ruslið?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one">Færir <xliff:g id="COUNT">^1</xliff:g> myndskeið í ruslið…</item>
- <item quantity="other">Færir <xliff:g id="COUNT">^1</xliff:g> myndskeið í ruslið…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> mynd í ruslið?</item>
- <item quantity="other">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> myndir í ruslið?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one">Færir <xliff:g id="COUNT">^1</xliff:g> mynd í ruslið…</item>
- <item quantity="other">Færir <xliff:g id="COUNT">^1</xliff:g> myndir í ruslið…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> atriði í ruslið?</item>
- <item quantity="other">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> atriði í ruslið?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one">Færir <xliff:g id="COUNT">^1</xliff:g> atriði í ruslið…</item>
- <item quantity="other">Færir <xliff:g id="COUNT">^1</xliff:g> atriði í ruslið…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> hljóðskrá úr ruslinu?</item>
- <item quantity="other">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> hljóðskrár úr ruslinu?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one">Færir <xliff:g id="COUNT">^1</xliff:g> hljóðskrá úr ruslinu…</item>
- <item quantity="other">Færir <xliff:g id="COUNT">^1</xliff:g> hljóðskrár úr ruslinu…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> myndskeið úr ruslinu?</item>
- <item quantity="other">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> myndskeið úr ruslinu?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one">Færir <xliff:g id="COUNT">^1</xliff:g> myndskeið úr ruslinu…</item>
- <item quantity="other">Færir <xliff:g id="COUNT">^1</xliff:g> myndskeið úr ruslinu…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> mynd úr ruslinu?</item>
- <item quantity="other">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> myndir úr ruslinu?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one">Færir <xliff:g id="COUNT">^1</xliff:g> mynd úr ruslinu…</item>
- <item quantity="other">Færir <xliff:g id="COUNT">^1</xliff:g> myndir úr ruslinu…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> atriði úr ruslinu?</item>
- <item quantity="other">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> atriði úr ruslinu?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one">Færir <xliff:g id="COUNT">^1</xliff:g> atriði úr ruslinu…</item>
- <item quantity="other">Færir <xliff:g id="COUNT">^1</xliff:g> atriði úr ruslinu…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að eyða <xliff:g id="COUNT">^2</xliff:g> hljóðskrá?</item>
- <item quantity="other">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að eyða <xliff:g id="COUNT">^2</xliff:g> hljóðskrám?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one">Eyðir <xliff:g id="COUNT">^1</xliff:g> hljóðskrá…</item>
- <item quantity="other">Eyðir <xliff:g id="COUNT">^1</xliff:g> hljóðskrám…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að eyða <xliff:g id="COUNT">^2</xliff:g> myndskeiði?</item>
- <item quantity="other">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að eyða <xliff:g id="COUNT">^2</xliff:g> myndskeiðum?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one">Eyðir <xliff:g id="COUNT">^1</xliff:g> myndskeiði…</item>
- <item quantity="other">Eyðir <xliff:g id="COUNT">^1</xliff:g> myndskeiðum…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að eyða <xliff:g id="COUNT">^2</xliff:g> mynd?</item>
- <item quantity="other">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að eyða <xliff:g id="COUNT">^2</xliff:g> myndum?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one">Eyðir <xliff:g id="COUNT">^1</xliff:g> mynd…</item>
- <item quantity="other">Eyðir <xliff:g id="COUNT">^1</xliff:g> myndum…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að eyða <xliff:g id="COUNT">^2</xliff:g> atriði?</item>
- <item quantity="other">Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að eyða <xliff:g id="COUNT">^2</xliff:g> atriðum?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one">Eyðir <xliff:g id="COUNT">^1</xliff:g> atriði…</item>
- <item quantity="other">Eyðir <xliff:g id="COUNT">^1</xliff:g> atriðum…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Bæta við"</string>
+ <string name="deselect" msgid="4297825044827769490">"Afvelja"</string>
+ <string name="deselected" msgid="8488133193326208475">"Afvalið"</string>
+ <string name="select" msgid="2704765470563027689">"Velja"</string>
+ <string name="selected" msgid="9151797369975828124">"Valið"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Veldu allt að <xliff:g id="COUNT_0">^1</xliff:g> atriði}one{Veldu allt að <xliff:g id="COUNT_1">^1</xliff:g> atriði}other{Veldu allt að <xliff:g id="COUNT_1">^1</xliff:g> atriði}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Nýlegt"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Engar myndir eða myndskeið"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Engin albúm"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Skoða valið"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Myndir"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albúm"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Forskoða"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Skipta yfir í vinnusnið"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Skipta yfir í eigið snið"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Útilokað af kerfisstjóra"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Óheimilt er að opna vinnugögn í forriti til einkanota"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Óheimilt er að opna einkagögn í vinnuforriti"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Hlé gert á vinnuforritum"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Kveiktu á vinnuforritunum og reyndu síðan að opna vinnumyndirnar aftur"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Þetta forrit hefur aðeins aðgang að myndunum sem þú velur"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> atriði}one{<xliff:g id="COUNT_1">^1</xliff:g> atriði}other{<xliff:g id="COUNT_1">^1</xliff:g> atriði}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Bæta við (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Myndavél"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Niðurhal"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Uppáhald"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Skjámyndir"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Lifandi mynd"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> frá <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Myndskeið tekið upp <xliff:g id="TIME">%1$s</xliff:g> sem er <xliff:g id="DURATION">%2$s</xliff:g> að lengd"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Mynd"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Lifandi mynd"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Þagga myndskeið"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Hætta að þagga myndskeið"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Spila myndskeið"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Gera hlé á spilun"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Skýjaefni er nú í boði frá <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"ekki valið"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Leyfa <xliff:g id="APP_NAME_0">^1</xliff:g> að breyta þessari hljóðskrá?}one{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að breyta <xliff:g id="COUNT">^2</xliff:g> hljóðskrá?}other{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að breyta <xliff:g id="COUNT">^2</xliff:g> hljóðskrám?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Breytir hljóðskrá…}one{Breytir <xliff:g id="COUNT">^1</xliff:g> hljóðskrá…}other{Breytir <xliff:g id="COUNT">^1</xliff:g> hljóðskrám…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Leyfa <xliff:g id="APP_NAME_0">^1</xliff:g> að breyta þessu myndskeiði?}one{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að breyta <xliff:g id="COUNT">^2</xliff:g> myndskeiði?}other{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að breyta <xliff:g id="COUNT">^2</xliff:g> myndskeiðum?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Breytir myndskeiði…}one{Breytir <xliff:g id="COUNT">^1</xliff:g> myndskeiði…}other{Breytir <xliff:g id="COUNT">^1</xliff:g> myndskeiðum…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Leyfa <xliff:g id="APP_NAME_0">^1</xliff:g> að breyta þessari mynd?}one{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að breyta <xliff:g id="COUNT">^2</xliff:g> mynd?}other{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að breyta <xliff:g id="COUNT">^2</xliff:g> myndum?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Breytir mynd…}one{Breytir <xliff:g id="COUNT">^1</xliff:g> mynd…}other{Breytir <xliff:g id="COUNT">^1</xliff:g> myndum…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Leyfa <xliff:g id="APP_NAME_0">^1</xliff:g> að breyta þessu atriði?}one{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að breyta <xliff:g id="COUNT">^2</xliff:g> atriði?}other{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að breyta <xliff:g id="COUNT">^2</xliff:g> atriðum?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Breytir atriði…}one{Breytir <xliff:g id="COUNT">^1</xliff:g> atriði…}other{Breytir <xliff:g id="COUNT">^1</xliff:g> atriðum…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Leyfa <xliff:g id="APP_NAME_0">^1</xliff:g> að færa þessa hljóðskrá í ruslið?}one{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> hljóðskrá í ruslið?}other{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> hljóðskrár í ruslið?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Færir hljóðskrá í ruslið…}one{Færir <xliff:g id="COUNT">^1</xliff:g> hljóðskrá í ruslið…}other{Færir <xliff:g id="COUNT">^1</xliff:g> hljóðskrár í ruslið…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Leyfa <xliff:g id="APP_NAME_0">^1</xliff:g> að færa þetta myndskeið í ruslið?}one{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> myndskeið í ruslið?}other{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> myndskeið í ruslið?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Færir myndskeið í ruslið…}one{Færir <xliff:g id="COUNT">^1</xliff:g> myndskeið í ruslið…}other{Færir <xliff:g id="COUNT">^1</xliff:g> myndskeið í ruslið…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Leyfa <xliff:g id="APP_NAME_0">^1</xliff:g> að færa þessa mynd í ruslið?}one{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> mynd í ruslið?}other{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> myndir í ruslið?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Færir mynd í ruslið…}one{Færir <xliff:g id="COUNT">^1</xliff:g> mynd í ruslið…}other{Færir <xliff:g id="COUNT">^1</xliff:g> myndir í ruslið…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Leyfa <xliff:g id="APP_NAME_0">^1</xliff:g> að færa þetta atriði í ruslið?}one{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> atriði í ruslið?}other{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> atriði í ruslið?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Færir atriði í ruslið…}one{Færir <xliff:g id="COUNT">^1</xliff:g> atriði í ruslið…}other{Færir <xliff:g id="COUNT">^1</xliff:g> atriði í ruslið…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Leyfa <xliff:g id="APP_NAME_0">^1</xliff:g> að færa þessa hljóðskrá úr ruslinu?}one{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> hljóðskrá úr ruslinu?}other{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> hljóðskrár úr ruslinu?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Færir hljóðskrá úr ruslinu…}one{Færir <xliff:g id="COUNT">^1</xliff:g> hljóðskrá úr ruslinu…}other{Færir <xliff:g id="COUNT">^1</xliff:g> hljóðskrár úr ruslinu…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Leyfa <xliff:g id="APP_NAME_0">^1</xliff:g> að færa þetta myndskeið úr ruslinu?}one{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> myndskeið úr ruslinu?}other{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> myndskeið úr ruslinu?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Færir myndskeið úr ruslinu…}one{Færir <xliff:g id="COUNT">^1</xliff:g> myndskeið úr ruslinu…}other{Færir <xliff:g id="COUNT">^1</xliff:g> myndskeið úr ruslinu…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Leyfa <xliff:g id="APP_NAME_0">^1</xliff:g> að færa þessa mynd úr ruslinu?}one{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> mynd úr ruslinu?}other{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> myndir úr ruslinu?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Færir mynd úr ruslinu…}one{Færir <xliff:g id="COUNT">^1</xliff:g> mynd úr ruslinu…}other{Færir <xliff:g id="COUNT">^1</xliff:g> myndir úr ruslinu…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Leyfa <xliff:g id="APP_NAME_0">^1</xliff:g> að færa þetta atriði úr ruslinu?}one{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> atriði úr ruslinu?}other{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að færa <xliff:g id="COUNT">^2</xliff:g> atriði úr ruslinu?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Færir atriði úr ruslinu…}one{Færir <xliff:g id="COUNT">^1</xliff:g> atriði úr ruslinu…}other{Færir <xliff:g id="COUNT">^1</xliff:g> atriði úr ruslinu…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Leyfa <xliff:g id="APP_NAME_0">^1</xliff:g> að eyða þessari hljóðskrá?}one{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að eyða <xliff:g id="COUNT">^2</xliff:g> hljóðskrá?}other{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að eyða <xliff:g id="COUNT">^2</xliff:g> hljóðskrám?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Eyðir hljóðskrá…}one{Eyðir <xliff:g id="COUNT">^1</xliff:g> hljóðskrá…}other{Eyðir <xliff:g id="COUNT">^1</xliff:g> hljóðskrám…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Leyfa <xliff:g id="APP_NAME_0">^1</xliff:g> að eyða þessu myndskeiði?}one{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að eyða <xliff:g id="COUNT">^2</xliff:g> myndskeiði?}other{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að eyða <xliff:g id="COUNT">^2</xliff:g> myndskeiðum?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Eyðir myndskeiði…}one{Eyðir <xliff:g id="COUNT">^1</xliff:g> myndskeiði…}other{Eyðir <xliff:g id="COUNT">^1</xliff:g> myndskeiðum…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Leyfa <xliff:g id="APP_NAME_0">^1</xliff:g> að eyða þessari mynd?}one{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að eyða <xliff:g id="COUNT">^2</xliff:g> mynd?}other{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að eyða <xliff:g id="COUNT">^2</xliff:g> myndum?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Eyðir mynd…}one{Eyðir <xliff:g id="COUNT">^1</xliff:g> mynd…}other{Eyðir <xliff:g id="COUNT">^1</xliff:g> myndum…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Leyfa <xliff:g id="APP_NAME_0">^1</xliff:g> að eyða þessu atriði?}one{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að eyða <xliff:g id="COUNT">^2</xliff:g> atriði?}other{Leyfa <xliff:g id="APP_NAME_1">^1</xliff:g> að eyða <xliff:g id="COUNT">^2</xliff:g> atriðum?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Eyðir atriði…}one{Eyðir <xliff:g id="COUNT">^1</xliff:g> atriði…}other{Eyðir <xliff:g id="COUNT">^1</xliff:g> atriðum…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> getur ekki unnið úr efnisskrám"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Hætt við úrvinnslu efnis"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Villa við úrvinnslu efnis"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 7906977..309ae64 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Selettore foto"</string>
<string name="artist_label" msgid="8105600993099120273">"Artista"</string>
<string name="unknown" msgid="2059049215682829375">"Sconosciuto"</string>
<string name="root_images" msgid="5861633549189045666">"Immagini"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Continua"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Consenti"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Rifiuta"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Più altri <xliff:g id="COUNT_1">^1</xliff:g> elementi</item>
- <item quantity="one">Più <xliff:g id="COUNT_0">^1</xliff:g> altro elemento</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}many{+<xliff:g id="COUNT_1">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Più <xliff:g id="COUNT_0">^1</xliff:g> altro elemento}many{Plus <xliff:g id="COUNT_1">^1</xliff:g> additional items}other{Più altri <xliff:g id="COUNT_1">^1</xliff:g> elementi}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Cancellare file temporanei delle app"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"L\'app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vorrebbe cancellare alcuni file temporanei. In tal caso potrebbe verificarsi un maggiore utilizzo della batteria o della rete dati."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Cancellazione dei file temporanei delle app…"</string>
<string name="clear" msgid="5524638938415865915">"Cancella"</string>
<string name="allow" msgid="8885707816848569619">"Consenti"</string>
<string name="deny" msgid="6040983710442068936">"Rifiuta"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di modificare <xliff:g id="COUNT">^2</xliff:g> file audio?</item>
- <item quantity="one">Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di modificare questo file audio?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">Modifica di <xliff:g id="COUNT">^1</xliff:g> file audio in corso…</item>
- <item quantity="one">Modifica del file audio in corso…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di modificare <xliff:g id="COUNT">^2</xliff:g> video?</item>
- <item quantity="one">Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di modificare questo video?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">Modifica di <xliff:g id="COUNT">^1</xliff:g> video in corso…</item>
- <item quantity="one">Modifica del video in corso…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di modificare <xliff:g id="COUNT">^2</xliff:g> foto?</item>
- <item quantity="one">Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di modificare questa foto?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">Modifica di <xliff:g id="COUNT">^1</xliff:g> foto in corso…</item>
- <item quantity="one">Modifica della foto in corso…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di modificare <xliff:g id="COUNT">^2</xliff:g> elementi?</item>
- <item quantity="one">Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di modificare questo elemento?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">Modifica di <xliff:g id="COUNT">^1</xliff:g> elementi in corso…</item>
- <item quantity="one">Modifica dell\'elemento in corso…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di spostare <xliff:g id="COUNT">^2</xliff:g> file audio nel cestino?</item>
- <item quantity="one">Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di spostare questo file audio nel cestino?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">Spostamento di <xliff:g id="COUNT">^1</xliff:g> file audio nel cestino in corso…</item>
- <item quantity="one">Spostamento del file audio nel cestino in corso…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di spostare <xliff:g id="COUNT">^2</xliff:g> video nel cestino?</item>
- <item quantity="one">Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di spostare questo video nel cestino?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">Spostamento di <xliff:g id="COUNT">^1</xliff:g> video nel cestino in corso…</item>
- <item quantity="one">Spostamento del video nel cestino in corso…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di spostare <xliff:g id="COUNT">^2</xliff:g> foto nel cestino?</item>
- <item quantity="one">Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di spostare questa foto nel cestino?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">Spostamento di <xliff:g id="COUNT">^1</xliff:g> foto nel cestino in corso…</item>
- <item quantity="one">Spostamento della foto nel cestino in corso…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di spostare <xliff:g id="COUNT">^2</xliff:g> elementi nel cestino?</item>
- <item quantity="one">Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di spostare questo elemento nel cestino?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">Spostamento di <xliff:g id="COUNT">^1</xliff:g> elementi nel cestino in corso…</item>
- <item quantity="one">Spostamento dell\'elemento nel cestino in corso…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di spostare <xliff:g id="COUNT">^2</xliff:g> file audio fuori dal cestino?</item>
- <item quantity="one">Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di spostare questo file audio fuori dal cestino?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">Spostamento di <xliff:g id="COUNT">^1</xliff:g> file audio fuori dal cestino in corso…</item>
- <item quantity="one">Spostamento del file audio fuori dal cestino in corso…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di spostare <xliff:g id="COUNT">^2</xliff:g> video fuori dal cestino?</item>
- <item quantity="one">Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di spostare questo video fuori dal cestino?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">Spostamento di <xliff:g id="COUNT">^1</xliff:g> video fuori dal cestino in corso…</item>
- <item quantity="one">Spostamento del video fuori dal cestino in corso…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di spostare <xliff:g id="COUNT">^2</xliff:g> foto fuori dal cestino?</item>
- <item quantity="one">Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di spostare questa foto fuori dal dispositivo?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">Spostamento di <xliff:g id="COUNT">^1</xliff:g> foto fuori dal cestino in corso…</item>
- <item quantity="one">Spostamento della foto fuori dal cestino in corso…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di spostare <xliff:g id="COUNT">^2</xliff:g> elementi fuori dal cestino?</item>
- <item quantity="one">Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di spostare questo elemento fuori dal cestino?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">Spostamento di <xliff:g id="COUNT">^1</xliff:g> elementi fuori dal cestino in corso…</item>
- <item quantity="one">Spostamento dell\'elemento fuori dal cestino in corso…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di eliminare <xliff:g id="COUNT">^2</xliff:g> file audio?</item>
- <item quantity="one">Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di eliminare questo file audio?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">Eliminazione di <xliff:g id="COUNT">^1</xliff:g> file audio in corso…</item>
- <item quantity="one">Eliminazione del file audio in corso…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di eliminare <xliff:g id="COUNT">^2</xliff:g> video?</item>
- <item quantity="one">Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di eliminare questo video?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">Eliminazione di <xliff:g id="COUNT">^1</xliff:g> video in corso…</item>
- <item quantity="one">Eliminazione del video in corso…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di eliminare <xliff:g id="COUNT">^2</xliff:g> foto?</item>
- <item quantity="one">Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di eliminare questa foto?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">Eliminazione di <xliff:g id="COUNT">^1</xliff:g> foto in corso…</item>
- <item quantity="one">Eliminazione della foto in corso…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di eliminare <xliff:g id="COUNT">^2</xliff:g> elementi?</item>
- <item quantity="one">Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di eliminare questo elemento?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">Eliminazione di <xliff:g id="COUNT">^1</xliff:g> elementi in corso…</item>
- <item quantity="one">Eliminazione dell\'elemento in corso…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Aggiungi"</string>
+ <string name="deselect" msgid="4297825044827769490">"Deseleziona"</string>
+ <string name="deselected" msgid="8488133193326208475">"Deselezionato"</string>
+ <string name="select" msgid="2704765470563027689">"Seleziona"</string>
+ <string name="selected" msgid="9151797369975828124">"Selezionato"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Seleziona massimo <xliff:g id="COUNT_0">^1</xliff:g> elemento}many{Select up to <xliff:g id="COUNT_1">^1</xliff:g> items}other{Seleziona fino a <xliff:g id="COUNT_1">^1</xliff:g> elementi}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Recenti"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Nessun video o foto"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Nessun album"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Visualizza selezione"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Foto"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Album"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Anteprima"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Passa al profilo di lavoro"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Passa al profilo personale"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Accesso bloccato dall\'amministratore"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Non è consentito accedere ai dati di lavoro da un\'app personale"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Non è consentito accedere ai dati personali da un\'app di lavoro"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Le app di lavoro sono in pausa"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Per aprire le foto relative al lavoro, attiva le app di lavoro e riprova"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Questa app può accedere soltanto alle foto selezionate da te"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> elemento}many{<xliff:g id="COUNT_1">^1</xliff:g> items}other{<xliff:g id="COUNT_1">^1</xliff:g> elementi}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Aggiungi (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Fotocamera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Download"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Preferiti"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Screenshot"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Foto in movimento"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"Creazione di <xliff:g id="ITEM_NAME">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Video del giorno <xliff:g id="TIME">%1$s</xliff:g> della durata di <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Foto"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Foto in movimento"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Disattiva audio del video"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Riattiva audio del video"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Riproduci il video"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Metti in pausa il video"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Contenuti multimediali salvati su cloud ora disponibili dall\'app <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"Elemento non selezionato"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di modificare questo file audio?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> audio files?}other{Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di modificare <xliff:g id="COUNT">^2</xliff:g> file audio?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Modifica del file audio in corso…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> audio files…}other{Modifica di <xliff:g id="COUNT">^1</xliff:g> file audio in corso…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di modificare questo video?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> videos?}other{Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di modificare <xliff:g id="COUNT">^2</xliff:g> video?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Modifica del video in corso…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> videos…}other{Modifica di <xliff:g id="COUNT">^1</xliff:g> video in corso…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di modificare questa foto?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> photos?}other{Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di modificare <xliff:g id="COUNT">^2</xliff:g> foto?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Modifica della foto in corso…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> photos…}other{Modifica di <xliff:g id="COUNT">^1</xliff:g> foto in corso…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di modificare questo elemento?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> items?}other{Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di modificare <xliff:g id="COUNT">^2</xliff:g> elementi?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Modifica dell\'elemento in corso…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> items…}other{Modifica di <xliff:g id="COUNT">^1</xliff:g> elementi in corso…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di spostare questo file audio nel cestino?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files to trash?}other{Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di spostare <xliff:g id="COUNT">^2</xliff:g> file audio nel cestino?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Spostamento del file audio nel cestino in corso…}many{Moving <xliff:g id="COUNT">^1</xliff:g> audio files to trash…}other{Spostamento di <xliff:g id="COUNT">^1</xliff:g> file audio nel cestino in corso…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di spostare questo video nel cestino?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos to trash?}other{Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di spostare <xliff:g id="COUNT">^2</xliff:g> video nel cestino?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Spostamento del video nel cestino in corso…}many{Moving <xliff:g id="COUNT">^1</xliff:g> videos to trash…}other{Spostamento di <xliff:g id="COUNT">^1</xliff:g> video nel cestino in corso…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di spostare questa foto nel cestino?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos to trash?}other{Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di spostare <xliff:g id="COUNT">^2</xliff:g> foto nel cestino?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Spostamento della foto nel cestino in corso…}many{Moving <xliff:g id="COUNT">^1</xliff:g> photos to trash…}other{Spostamento di <xliff:g id="COUNT">^1</xliff:g> foto nel cestino in corso…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di spostare questo elemento nel cestino?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items to trash?}other{Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di spostare <xliff:g id="COUNT">^2</xliff:g> elementi nel cestino?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Spostamento dell\'elemento nel cestino in corso…}many{Moving <xliff:g id="COUNT">^1</xliff:g> items to trash…}other{Spostamento di <xliff:g id="COUNT">^1</xliff:g> elementi nel cestino in corso…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di spostare questo file audio fuori dal cestino?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files out of trash?}other{Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di spostare <xliff:g id="COUNT">^2</xliff:g> file audio fuori dal cestino?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Spostamento del file audio fuori dal cestino in corso…}many{Moving <xliff:g id="COUNT">^1</xliff:g> audio files out of trash…}other{Spostamento di <xliff:g id="COUNT">^1</xliff:g> file audio fuori dal cestino in corso…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di spostare questo video fuori dal cestino?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos out of trash?}other{Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di spostare <xliff:g id="COUNT">^2</xliff:g> video fuori dal cestino?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Spostamento del video fuori dal cestino in corso…}many{Moving <xliff:g id="COUNT">^1</xliff:g> videos out of trash…}other{Spostamento di <xliff:g id="COUNT">^1</xliff:g> video fuori dal cestino in corso…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di spostare questa foto fuori dal cestino?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos out of trash?}other{Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di spostare <xliff:g id="COUNT">^2</xliff:g> foto fuori dal cestino?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Spostamento della foto fuori dal cestino in corso…}many{Moving <xliff:g id="COUNT">^1</xliff:g> photos out of trash…}other{Spostamento di <xliff:g id="COUNT">^1</xliff:g> foto fuori dal cestino in corso…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di spostare questo elemento fuori dal cestino?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items out of trash?}other{Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di spostare <xliff:g id="COUNT">^2</xliff:g> elementi fuori dal cestino?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Spostamento dell\'elemento fuori dal cestino in corso…}many{Moving <xliff:g id="COUNT">^1</xliff:g> items out of trash…}other{Spostamento di <xliff:g id="COUNT">^1</xliff:g> elementi fuori dal cestino in corso…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di eliminare questo file audio?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> audio files?}other{Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di eliminare <xliff:g id="COUNT">^2</xliff:g> file audio?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Eliminazione del file audio in corso…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> audio files…}other{Eliminazione di <xliff:g id="COUNT">^1</xliff:g> file audio in corso…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di eliminare questo video?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> videos?}other{Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di eliminare <xliff:g id="COUNT">^2</xliff:g> video?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Eliminazione del video in corso…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> videos…}other{Eliminazione di <xliff:g id="COUNT">^1</xliff:g> video in corso…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di eliminare questa foto?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> photos?}other{Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di eliminare <xliff:g id="COUNT">^2</xliff:g> foto?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Eliminazione della foto in corso…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> photos…}other{Eliminazione di <xliff:g id="COUNT">^1</xliff:g> foto in corso…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Consentire all\'app <xliff:g id="APP_NAME_0">^1</xliff:g> di eliminare questo elemento?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> items?}other{Consentire all\'app <xliff:g id="APP_NAME_1">^1</xliff:g> di eliminare <xliff:g id="COUNT">^2</xliff:g> elementi?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Eliminazione dell\'elemento in corso…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> items…}other{Eliminazione di <xliff:g id="COUNT">^1</xliff:g> elementi in corso…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> non può elaborare file multimediali"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Elaborazione dei contenuti multimediali annullata"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Errore nell\'elaborazione dei contenuti multimediali"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 4552372..1499203 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"מדיה"</string>
<string name="storage_description" msgid="4081716890357580107">"אחסון מקומי"</string>
<string name="app_label" msgid="9035307001052716210">"אחסון מדיה"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"בורר התמונות"</string>
<string name="artist_label" msgid="8105600993099120273">"אומן"</string>
<string name="unknown" msgid="2059049215682829375">"לא ידוע"</string>
<string name="root_images" msgid="5861633549189045666">"תמונות"</string>
@@ -29,216 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"המשך"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"כן, זה בסדר"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"אני לא מרשה"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="two">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="many">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="two">ועוד <xliff:g id="COUNT_1">^1</xliff:g> פריטים נוספים</item>
- <item quantity="many">ועוד <xliff:g id="COUNT_1">^1</xliff:g> פריטים נוספים</item>
- <item quantity="other">ועוד <xliff:g id="COUNT_1">^1</xliff:g> פריטים נוספים</item>
- <item quantity="one">ועוד פריט אחד נוסף (<xliff:g id="COUNT_0">^1</xliff:g>)</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{עוד פריט אחד (<xliff:g id="COUNT_0">^1</xliff:g>)}two{עוד <xliff:g id="COUNT_1">^1</xliff:g> פריטים}many{עוד <xliff:g id="COUNT_1">^1</xliff:g> פריטים}other{עוד <xliff:g id="COUNT_1">^1</xliff:g> פריטים}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{ועוד פריט אחד (<xliff:g id="COUNT_0">^1</xliff:g>) נוסף}two{ועוד <xliff:g id="COUNT_1">^1</xliff:g> פריטים נוספים}many{ועוד <xliff:g id="COUNT_1">^1</xliff:g> פריטים נוספים}other{ועוד <xliff:g id="COUNT_1">^1</xliff:g> פריטים נוספים}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"ניקוי קובצי אפליקציה זמניים"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"קיבלת בקשה מהאפליקציה <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> לניקוי של חלק מהקבצים הזמניים. הפעולה עשויה להגביר את השימוש בסוללה או בחבילת הגלישה."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"מתבצע ניקוי של קובצי אפליקציה זמניים…"</string>
<string name="clear" msgid="5524638938415865915">"ניקוי"</string>
<string name="allow" msgid="8885707816848569619">"אישור"</string>
<string name="deny" msgid="6040983710442068936">"דחייה"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="two">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g id="COUNT">^2</xliff:g> קובצי אודיו?</item>
- <item quantity="many">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g id="COUNT">^2</xliff:g> קובצי אודיו?</item>
- <item quantity="other">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g id="COUNT">^2</xliff:g> קובצי אודיו?</item>
- <item quantity="one">לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> לשנות את קובץ האודיו הזה?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="two">מתבצע שינוי ב-<xliff:g id="COUNT">^1</xliff:g> קובצי אודיו…</item>
- <item quantity="many">מתבצע שינוי ב-<xliff:g id="COUNT">^1</xliff:g> קובצי אודיו…</item>
- <item quantity="other">מתבצע שינוי ב-<xliff:g id="COUNT">^1</xliff:g> קובצי אודיו…</item>
- <item quantity="one">מתבצע שינוי בקובץ אודיו אחד…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="two">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g id="COUNT">^2</xliff:g> סרטונים?</item>
- <item quantity="many">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g id="COUNT">^2</xliff:g> סרטונים?</item>
- <item quantity="other">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g id="COUNT">^2</xliff:g> סרטונים?</item>
- <item quantity="one">לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> לשנות את הסרטון הזה?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="two">מתבצע שינוי ב-<xliff:g id="COUNT">^1</xliff:g> סרטונים…</item>
- <item quantity="many">מתבצע שינוי ב-<xliff:g id="COUNT">^1</xliff:g> סרטונים…</item>
- <item quantity="other">מתבצע שינוי ב-<xliff:g id="COUNT">^1</xliff:g> סרטונים…</item>
- <item quantity="one">מתבצע שינוי בסרטון אחד…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="two">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g id="COUNT">^2</xliff:g> תמונות?</item>
- <item quantity="many">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g id="COUNT">^2</xliff:g> תמונות?</item>
- <item quantity="other">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g id="COUNT">^2</xliff:g> תמונות?</item>
- <item quantity="one">לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> לשנות את התמונה הזו?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="two">מתבצע שינוי ב-<xliff:g id="COUNT">^1</xliff:g> תמונות…</item>
- <item quantity="many">מתבצע שינוי ב-<xliff:g id="COUNT">^1</xliff:g> תמונות…</item>
- <item quantity="other">מתבצע שינוי ב-<xliff:g id="COUNT">^1</xliff:g> תמונות…</item>
- <item quantity="one">מתבצע שינוי בתמונה אחת…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="two">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g id="COUNT">^2</xliff:g> פריטים?</item>
- <item quantity="many">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g id="COUNT">^2</xliff:g> פריטים?</item>
- <item quantity="other">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g id="COUNT">^2</xliff:g> פריטים?</item>
- <item quantity="one">לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> לשנות את הפריט הזה?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="two">מתבצע שינוי ב-<xliff:g id="COUNT">^1</xliff:g> פריטים…</item>
- <item quantity="many">מתבצע שינוי ב-<xliff:g id="COUNT">^1</xliff:g> פריטים…</item>
- <item quantity="other">מתבצע שינוי ב-<xliff:g id="COUNT">^1</xliff:g> פריטים…</item>
- <item quantity="one">מתבצע שינוי בפריט אחד…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="two">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להעביר <xliff:g id="COUNT">^2</xliff:g> קובצי אודיו לאשפה?</item>
- <item quantity="many">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להעביר <xliff:g id="COUNT">^2</xliff:g> קובצי אודיו לאשפה?</item>
- <item quantity="other">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להעביר <xliff:g id="COUNT">^2</xliff:g> קובצי אודיו לאשפה?</item>
- <item quantity="one">לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> להעביר את קובץ האודיו הזה לאשפה?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="two">מתבצעת העברה של <xliff:g id="COUNT">^1</xliff:g> קובצי אודיו לאשפה…</item>
- <item quantity="many">מתבצעת העברה של <xliff:g id="COUNT">^1</xliff:g> קובצי אודיו לאשפה…</item>
- <item quantity="other">מתבצעת העברה של <xliff:g id="COUNT">^1</xliff:g> קובצי אודיו לאשפה…</item>
- <item quantity="one">מתבצעת העברה של קובץ אודיו אחד לאשפה…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="two">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להעביר <xliff:g id="COUNT">^2</xliff:g> סרטונים לאשפה?</item>
- <item quantity="many">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להעביר <xliff:g id="COUNT">^2</xliff:g> סרטונים לאשפה?</item>
- <item quantity="other">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להעביר <xliff:g id="COUNT">^2</xliff:g> סרטונים לאשפה?</item>
- <item quantity="one">לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> להעביר את הסרטון הזה לאשפה?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="two">מתבצעת העברה של <xliff:g id="COUNT">^1</xliff:g> סרטונים לאשפה…</item>
- <item quantity="many">מתבצעת העברה של <xliff:g id="COUNT">^1</xliff:g> סרטונים לאשפה…</item>
- <item quantity="other">מתבצעת העברה של <xliff:g id="COUNT">^1</xliff:g> סרטונים לאשפה…</item>
- <item quantity="one">מתבצעת העברה של סרטון אחד לאשפה…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="two">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להעביר <xliff:g id="COUNT">^2</xliff:g> תמונות לאשפה?</item>
- <item quantity="many">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להעביר <xliff:g id="COUNT">^2</xliff:g> תמונות לאשפה?</item>
- <item quantity="other">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להעביר <xliff:g id="COUNT">^2</xliff:g> תמונות לאשפה?</item>
- <item quantity="one">לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> להעביר את התמונה הזו לאשפה?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="two">מתבצעת העברה של <xliff:g id="COUNT">^1</xliff:g> תמונות לאשפה…</item>
- <item quantity="many">מתבצעת העברה של <xliff:g id="COUNT">^1</xliff:g> תמונות לאשפה…</item>
- <item quantity="other">מתבצעת העברה של <xliff:g id="COUNT">^1</xliff:g> תמונות לאשפה…</item>
- <item quantity="one">מתבצעת העברה של תמונה אחת לאשפה…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="two">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להעביר <xliff:g id="COUNT">^2</xliff:g> פריטים לאשפה?</item>
- <item quantity="many">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להעביר <xliff:g id="COUNT">^2</xliff:g> פריטים לאשפה?</item>
- <item quantity="other">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להעביר <xliff:g id="COUNT">^2</xliff:g> פריטים לאשפה?</item>
- <item quantity="one">לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> להעביר את הפריט הזה לאשפה?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="two">מתבצעת העברה של <xliff:g id="COUNT">^1</xliff:g> פריטים לאשפה…</item>
- <item quantity="many">מתבצעת העברה של <xliff:g id="COUNT">^1</xliff:g> פריטים לאשפה…</item>
- <item quantity="other">מתבצעת העברה של <xliff:g id="COUNT">^1</xliff:g> פריטים לאשפה…</item>
- <item quantity="one">מתבצעת העברה של פריט אחד לאשפה…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="two">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להוציא <xliff:g id="COUNT">^2</xliff:g> קובצי אודיו מהאשפה?</item>
- <item quantity="many">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להוציא <xliff:g id="COUNT">^2</xliff:g> קובצי אודיו מהאשפה?</item>
- <item quantity="other">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להוציא <xliff:g id="COUNT">^2</xliff:g> קובצי אודיו מהאשפה?</item>
- <item quantity="one">לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> להוציא את קובץ האודיו הזה מהאשפה?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="two">מתבצעת הוצאה של <xliff:g id="COUNT">^1</xliff:g> קובצי אודיו מהאשפה…</item>
- <item quantity="many">מתבצעת הוצאה של <xliff:g id="COUNT">^1</xliff:g> קובצי אודיו מהאשפה…</item>
- <item quantity="other">מתבצעת הוצאה של <xliff:g id="COUNT">^1</xliff:g> קובצי אודיו מהאשפה…</item>
- <item quantity="one">מתבצעת הוצאה של קובץ אודיו אחד מהאשפה…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="two">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להוציא <xliff:g id="COUNT">^2</xliff:g> סרטונים מהאשפה?</item>
- <item quantity="many">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להוציא <xliff:g id="COUNT">^2</xliff:g> סרטונים מהאשפה?</item>
- <item quantity="other">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להוציא <xliff:g id="COUNT">^2</xliff:g> סרטונים מהאשפה?</item>
- <item quantity="one">לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> להוציא את הסרטון הזה מהאשפה?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="two">מתבצעת הוצאה של <xliff:g id="COUNT">^1</xliff:g> סרטונים מהאשפה…</item>
- <item quantity="many">מתבצעת הוצאה של <xliff:g id="COUNT">^1</xliff:g> סרטונים מהאשפה…</item>
- <item quantity="other">מתבצעת הוצאה של <xliff:g id="COUNT">^1</xliff:g> סרטונים מהאשפה…</item>
- <item quantity="one">מתבצעת הוצאה של סרטון אחד מהאשפה…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="two">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להוציא <xliff:g id="COUNT">^2</xliff:g> תמונות מהאשפה?</item>
- <item quantity="many">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להוציא <xliff:g id="COUNT">^2</xliff:g> תמונות מהאשפה?</item>
- <item quantity="other">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להוציא <xliff:g id="COUNT">^2</xliff:g> תמונות מהאשפה?</item>
- <item quantity="one">לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> להוציא את התמונה הזו מהאשפה?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="two">מתבצעת הוצאה של <xliff:g id="COUNT">^1</xliff:g> תמונות מהאשפה…</item>
- <item quantity="many">מתבצעת הוצאה של <xliff:g id="COUNT">^1</xliff:g> תמונות מהאשפה…</item>
- <item quantity="other">מתבצעת הוצאה של <xliff:g id="COUNT">^1</xliff:g> תמונות מהאשפה…</item>
- <item quantity="one">מתבצעת הוצאה של תמונה אחת מהאשפה…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="two">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להוציא <xliff:g id="COUNT">^2</xliff:g> פריטים מהאשפה?</item>
- <item quantity="many">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להוציא <xliff:g id="COUNT">^2</xliff:g> פריטים מהאשפה?</item>
- <item quantity="other">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להוציא <xliff:g id="COUNT">^2</xliff:g> פריטים מהאשפה?</item>
- <item quantity="one">לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> להוציא את הפריט הזה מהאשפה?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="two">מתבצעת הוצאה של <xliff:g id="COUNT">^1</xliff:g> פריטים מהאשפה…</item>
- <item quantity="many">מתבצעת הוצאה של <xliff:g id="COUNT">^1</xliff:g> פריטים מהאשפה…</item>
- <item quantity="other">מתבצעת הוצאה של <xliff:g id="COUNT">^1</xliff:g> פריטים מהאשפה…</item>
- <item quantity="one">מתבצעת הוצאה של פריט אחד מהאשפה…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="two">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> למחוק <xliff:g id="COUNT">^2</xliff:g> קובצי אודיו?</item>
- <item quantity="many">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> למחוק <xliff:g id="COUNT">^2</xliff:g> קובצי אודיו?</item>
- <item quantity="other">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> למחוק <xliff:g id="COUNT">^2</xliff:g> קובצי אודיו?</item>
- <item quantity="one">לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> למחוק את קובץ האודיו הזה?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="two">מתבצעת מחיקה של <xliff:g id="COUNT">^1</xliff:g> קובצי אודיו…</item>
- <item quantity="many">מתבצעת מחיקה של <xliff:g id="COUNT">^1</xliff:g> קובצי אודיו…</item>
- <item quantity="other">מתבצעת מחיקה של <xliff:g id="COUNT">^1</xliff:g> קובצי אודיו…</item>
- <item quantity="one">מתבצעת מחיקה של קובץ אודיו אחד…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="two">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> למחוק <xliff:g id="COUNT">^2</xliff:g> סרטונים?</item>
- <item quantity="many">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> למחוק <xliff:g id="COUNT">^2</xliff:g> סרטונים?</item>
- <item quantity="other">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> למחוק <xliff:g id="COUNT">^2</xliff:g> סרטונים?</item>
- <item quantity="one">לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> למחוק את הסרטון הזה?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="two">מתבצעת מחיקה של <xliff:g id="COUNT">^1</xliff:g> סרטונים…</item>
- <item quantity="many">מתבצעת מחיקה של <xliff:g id="COUNT">^1</xliff:g> סרטונים…</item>
- <item quantity="other">מתבצעת מחיקה של <xliff:g id="COUNT">^1</xliff:g> סרטונים…</item>
- <item quantity="one">מתבצעת מחיקה של סרטון אחד</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="two">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> למחוק <xliff:g id="COUNT">^2</xliff:g> תמונות?</item>
- <item quantity="many">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> למחוק <xliff:g id="COUNT">^2</xliff:g> תמונות?</item>
- <item quantity="other">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> למחוק <xliff:g id="COUNT">^2</xliff:g> תמונות?</item>
- <item quantity="one">לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> למחוק את התמונה הזו?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="two">מתבצעת מחיקה של <xliff:g id="COUNT">^1</xliff:g> תמונות…</item>
- <item quantity="many">מתבצעת מחיקה של <xliff:g id="COUNT">^1</xliff:g> תמונות…</item>
- <item quantity="other">מתבצעת מחיקה של <xliff:g id="COUNT">^1</xliff:g> תמונות…</item>
- <item quantity="one">מתבצעת מחיקה של תמונה אחת…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="two">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> למחוק <xliff:g id="COUNT">^2</xliff:g> פריטים?</item>
- <item quantity="many">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> למחוק <xliff:g id="COUNT">^2</xliff:g> פריטים?</item>
- <item quantity="other">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> למחוק <xliff:g id="COUNT">^2</xliff:g> פריטים?</item>
- <item quantity="one">לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> למחוק את הפריט הזה?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="two">מתבצעת מחיקה של <xliff:g id="COUNT">^1</xliff:g> פריטים…</item>
- <item quantity="many">מתבצעת מחיקה של <xliff:g id="COUNT">^1</xliff:g> פריטים…</item>
- <item quantity="other">מתבצעת מחיקה של <xliff:g id="COUNT">^1</xliff:g> פריטים…</item>
- <item quantity="one">מתבצעת מחיקה של פריט אחד…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"הוספה"</string>
+ <string name="deselect" msgid="4297825044827769490">"ביטול הבחירה"</string>
+ <string name="deselected" msgid="8488133193326208475">"הבחירה בוטלה"</string>
+ <string name="select" msgid="2704765470563027689">"בחירה"</string>
+ <string name="selected" msgid="9151797369975828124">"נבחר"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{ניתן לבחור פריט אחד (<xliff:g id="COUNT_0">^1</xliff:g>) לכל היותר}two{ניתן לבחור <xliff:g id="COUNT_1">^1</xliff:g> פריטים לכל היותר}many{ניתן לבחור <xliff:g id="COUNT_1">^1</xliff:g> פריטים לכל היותר}other{ניתן לבחור <xliff:g id="COUNT_1">^1</xliff:g> פריטים לכל היותר}}"</string>
+ <string name="recent" msgid="6694613584743207874">"אחרונות"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"אין תמונות או סרטונים"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"אין אלבומים"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"הצגת הפריטים שנבחרו"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"תמונות"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"אלבומים"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"תצוגה מקדימה"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"לפרופיל העבודה"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"לפרופיל האישי"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"הפעולה נחסמה על ידי מנהל המערכת"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"אי אפשר לגשת לנתוני עבודה דרך אפליקציה לשימוש אישי"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"אי אפשר לגשת למידע אישי דרך אפליקציה לעבודה"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"האפליקציות לעבודה מושהות"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"כדי לפתוח תמונות מפרופיל העבודה, צריך להפעיל את האפליקציות לעבודה ולנסות שוב"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"לאפליקציה הזו יש גישה רק לתמונות שבחרת"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{פריט אחד (<xliff:g id="COUNT_0">^1</xliff:g>)}two{<xliff:g id="COUNT_1">^1</xliff:g> פריטים}many{<xliff:g id="COUNT_1">^1</xliff:g> פריטים}other{<xliff:g id="COUNT_1">^1</xliff:g> פריטים}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"הוספה (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"מצלמה"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"הורדות"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"מועדפים"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"צילומי מסך"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"תמונה עם תנועה"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"הפריט <xliff:g id="ITEM_NAME">%1$s</xliff:g> צולם ב-<xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"הסרטון צולם בשעה <xliff:g id="TIME">%1$s</xliff:g> והמשך שלו הוא <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"תמונה"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"תמונה עם תנועה"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"השתקת הסרטון"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"ביטול ההשתקה של הסרטון"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"הפעלת הסרטון"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"השהיית הסרטון"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"מדיה בענן מתוך <xliff:g id="PKG_NAME">%1$s</xliff:g> זמינה עכשיו"</string>
+ <string name="not_selected" msgid="2244008151669896758">"לא נבחר"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> לשנות את קובץ האודיו הזה?}two{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g id="COUNT">^2</xliff:g> קובצי אודיו?}many{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g id="COUNT">^2</xliff:g> קובצי אודיו?}other{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g id="COUNT">^2</xliff:g> קובצי אודיו?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{מתבצע שינוי בקובץ האודיו…}two{מתבצע שינוי ב-<xliff:g id="COUNT">^1</xliff:g> קובצי אודיו…}many{מתבצע שינוי ב-<xliff:g id="COUNT">^1</xliff:g> קובצי אודיו…}other{מתבצע שינוי ב-<xliff:g id="COUNT">^1</xliff:g> קובצי אודיו…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> לשנות את הסרטון הזה?}two{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g id="COUNT">^2</xliff:g> סרטונים?}many{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g id="COUNT">^2</xliff:g> סרטונים?}other{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g id="COUNT">^2</xliff:g> סרטונים?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{מתבצע שינוי בסרטון…}two{מתבצע שינוי ב-<xliff:g id="COUNT">^1</xliff:g> סרטונים…}many{מתבצע שינוי ב-<xliff:g id="COUNT">^1</xliff:g> סרטונים…}other{מתבצע שינוי ב-<xliff:g id="COUNT">^1</xliff:g> סרטונים…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> לשנות את התמונה הזו?}two{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g id="COUNT">^2</xliff:g> תמונות?}many{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g id="COUNT">^2</xliff:g> תמונות?}other{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g id="COUNT">^2</xliff:g> תמונות?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{מתבצע שינוי בתמונה…}two{מתבצע שינוי ב-<xliff:g id="COUNT">^1</xliff:g> תמונות…}many{מתבצע שינוי ב-<xliff:g id="COUNT">^1</xliff:g> תמונות…}other{מתבצע שינוי ב-<xliff:g id="COUNT">^1</xliff:g> תמונות…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> לשנות את הפריט הזה?}two{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g id="COUNT">^2</xliff:g> פריטים?}many{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g id="COUNT">^2</xliff:g> פריטים?}other{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g id="COUNT">^2</xliff:g> פריטים?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{מתבצע שינוי בפריט…}two{מתבצע שינוי ב-<xliff:g id="COUNT">^1</xliff:g> פריטים…}many{מתבצע שינוי ב-<xliff:g id="COUNT">^1</xliff:g> פריטים…}other{מתבצע שינוי ב-<xliff:g id="COUNT">^1</xliff:g> פריטים…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> להעביר את קובץ האודיו הזה לאשפה?}two{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להעביר <xliff:g id="COUNT">^2</xliff:g> קובצי אודיו לאשפה?}many{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להעביר <xliff:g id="COUNT">^2</xliff:g> קובצי אודיו לאשפה?}other{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להעביר <xliff:g id="COUNT">^2</xliff:g> קובצי אודיו לאשפה?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{מתבצעת העברה של קובץ האודיו לאשפה…}two{מתבצעת העברה של <xliff:g id="COUNT">^1</xliff:g> קובצי אודיו לאשפה…}many{מתבצעת העברה של <xliff:g id="COUNT">^1</xliff:g> קובצי אודיו לאשפה…}other{מתבצעת העברה של <xliff:g id="COUNT">^1</xliff:g> קובצי אודיו לאשפה…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> להעביר את הסרטון הזה לאשפה?}two{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להעביר <xliff:g id="COUNT">^2</xliff:g> סרטונים לאשפה?}many{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להעביר <xliff:g id="COUNT">^2</xliff:g> סרטונים לאשפה?}other{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להעביר <xliff:g id="COUNT">^2</xliff:g> סרטונים לאשפה?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{מתבצעת העברה של הסרטון לאשפה…}two{מתבצעת העברה של <xliff:g id="COUNT">^1</xliff:g> סרטונים לאשפה…}many{מתבצעת העברה של <xliff:g id="COUNT">^1</xliff:g> סרטונים לאשפה…}other{מתבצעת העברה של <xliff:g id="COUNT">^1</xliff:g> סרטונים לאשפה…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> להעביר את התמונה הזו לאשפה?}two{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להעביר <xliff:g id="COUNT">^2</xliff:g> תמונות לאשפה?}many{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להעביר <xliff:g id="COUNT">^2</xliff:g> תמונות לאשפה?}other{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להעביר <xliff:g id="COUNT">^2</xliff:g> תמונות לאשפה?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{מתבצעת העברה של התמונה לאשפה…}two{מתבצעת העברה של <xliff:g id="COUNT">^1</xliff:g> תמונות לאשפה…}many{מתבצעת העברה של <xliff:g id="COUNT">^1</xliff:g> תמונות לאשפה…}other{מתבצעת העברה של <xliff:g id="COUNT">^1</xliff:g> תמונות לאשפה…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> להעביר את הפריט הזה לאשפה?}two{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להעביר <xliff:g id="COUNT">^2</xliff:g> פריטים לאשפה?}many{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להעביר <xliff:g id="COUNT">^2</xliff:g> פריטים לאשפה?}other{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להעביר <xliff:g id="COUNT">^2</xliff:g> פריטים לאשפה?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{מתבצעת העברה של הפריט לאשפה…}two{מתבצעת העברה של <xliff:g id="COUNT">^1</xliff:g> פריטים לאשפה…}many{מתבצעת העברה של <xliff:g id="COUNT">^1</xliff:g> פריטים לאשפה…}other{מתבצעת העברה של <xliff:g id="COUNT">^1</xliff:g> פריטים לאשפה…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> להוציא את קובץ האודיו הזה מהאשפה?}two{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להוציא <xliff:g id="COUNT">^2</xliff:g> קובצי אודיו מהאשפה?}many{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להוציא <xliff:g id="COUNT">^2</xliff:g> קובצי אודיו מהאשפה?}other{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להוציא <xliff:g id="COUNT">^2</xliff:g> קובצי אודיו מהאשפה?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{מתבצעת הוצאה של קובץ האודיו מהאשפה…}two{מתבצעת הוצאה של <xliff:g id="COUNT">^1</xliff:g> קובצי אודיו מהאשפה…}many{מתבצעת הוצאה של <xliff:g id="COUNT">^1</xliff:g> קובצי אודיו מהאשפה…}other{מתבצעת הוצאה של <xliff:g id="COUNT">^1</xliff:g> קובצי אודיו מהאשפה…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> להוציא את הסרטון הזה מהאשפה?}two{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להוציא <xliff:g id="COUNT">^2</xliff:g> סרטונים מהאשפה?}many{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להוציא <xliff:g id="COUNT">^2</xliff:g> סרטונים מהאשפה?}other{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להוציא <xliff:g id="COUNT">^2</xliff:g> סרטונים מהאשפה?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{מתבצעת הוצאה של הסרטון מהאשפה…}two{מתבצעת הוצאה של <xliff:g id="COUNT">^1</xliff:g> סרטונים מהאשפה…}many{מתבצעת הוצאה של <xliff:g id="COUNT">^1</xliff:g> סרטונים מהאשפה…}other{מתבצעת הוצאה של <xliff:g id="COUNT">^1</xliff:g> סרטונים מהאשפה…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> להוציא את התמונה הזו מהאשפה?}two{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להוציא <xliff:g id="COUNT">^2</xliff:g> תמונות מהאשפה?}many{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להוציא <xliff:g id="COUNT">^2</xliff:g> תמונות מהאשפה?}other{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להוציא <xliff:g id="COUNT">^2</xliff:g> תמונות מהאשפה?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{מתבצעת הוצאה של התמונה מהאשפה…}two{מתבצעת הוצאה של <xliff:g id="COUNT">^1</xliff:g> תמונות מהאשפה…}many{מתבצעת הוצאה של <xliff:g id="COUNT">^1</xliff:g> תמונות מהאשפה…}other{מתבצעת הוצאה של <xliff:g id="COUNT">^1</xliff:g> תמונות מהאשפה…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> להוציא את הפריט הזה מהאשפה?}two{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להוציא <xliff:g id="COUNT">^2</xliff:g> פריטים מהאשפה?}many{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להוציא <xliff:g id="COUNT">^2</xliff:g> פריטים מהאשפה?}other{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> להוציא <xliff:g id="COUNT">^2</xliff:g> פריטים מהאשפה?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{מתבצעת הוצאה של הפריט מהאשפה…}two{מתבצעת הוצאה של <xliff:g id="COUNT">^1</xliff:g> פריטים מהאשפה…}many{מתבצעת הוצאה של <xliff:g id="COUNT">^1</xliff:g> פריטים מהאשפה…}other{מתבצעת הוצאה של <xliff:g id="COUNT">^1</xliff:g> פריטים מהאשפה…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> למחוק את קובץ האודיו הזה?}two{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> למחוק <xliff:g id="COUNT">^2</xliff:g> קובצי אודיו?}many{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> למחוק <xliff:g id="COUNT">^2</xliff:g> קובצי אודיו?}other{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> למחוק <xliff:g id="COUNT">^2</xliff:g> קובצי אודיו?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{מתבצעת מחיקה של קובץ האודיו…}two{מתבצעת מחיקה של <xliff:g id="COUNT">^1</xliff:g> קובצי אודיו…}many{מתבצעת מחיקה של <xliff:g id="COUNT">^1</xliff:g> קובצי אודיו…}other{מתבצעת מחיקה של <xliff:g id="COUNT">^1</xliff:g> קובצי אודיו…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> למחוק את הסרטון הזה?}two{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> למחוק <xliff:g id="COUNT">^2</xliff:g> סרטונים?}many{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> למחוק <xliff:g id="COUNT">^2</xliff:g> סרטונים?}other{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> למחוק <xliff:g id="COUNT">^2</xliff:g> סרטונים?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{מתבצעת מחיקה של הסרטון}two{מתבצעת מחיקה של <xliff:g id="COUNT">^1</xliff:g> סרטונים…}many{מתבצעת מחיקה של <xliff:g id="COUNT">^1</xliff:g> סרטונים…}other{מתבצעת מחיקה של <xliff:g id="COUNT">^1</xliff:g> סרטונים…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> למחוק את התמונה הזו?}two{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> למחוק <xliff:g id="COUNT">^2</xliff:g> תמונות?}many{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> למחוק <xliff:g id="COUNT">^2</xliff:g> תמונות?}other{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> למחוק <xliff:g id="COUNT">^2</xliff:g> תמונות?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{מתבצעת מחיקה של התמונה…}two{מתבצעת מחיקה של <xliff:g id="COUNT">^1</xliff:g> תמונות…}many{מתבצעת מחיקה של <xliff:g id="COUNT">^1</xliff:g> תמונות…}other{מתבצעת מחיקה של <xliff:g id="COUNT">^1</xliff:g> תמונות…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{לאפשר לאפליקציה <xliff:g id="APP_NAME_0">^1</xliff:g> למחוק את הפריט הזה?}two{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> למחוק <xliff:g id="COUNT">^2</xliff:g> פריטים?}many{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> למחוק <xliff:g id="COUNT">^2</xliff:g> פריטים?}other{לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> למחוק <xliff:g id="COUNT">^2</xliff:g> פריטים?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{מתבצעת מחיקה של הפריט…}two{מתבצעת מחיקה של <xliff:g id="COUNT">^1</xliff:g> פריטים…}many{מתבצעת מחיקה של <xliff:g id="COUNT">^1</xliff:g> פריטים…}other{מתבצעת מחיקה של <xliff:g id="COUNT">^1</xliff:g> פריטים…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"האפליקציה <xliff:g id="APP_NAME">%s</xliff:g> לא יכולה לעבד קובצי מדיה"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"עיבוד המדיה בוטל"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"שגיאה בעיבוד המדיה"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index e17e1e5..fcdd9b0 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"メディア"</string>
<string name="storage_description" msgid="4081716890357580107">"ローカル ストレージ"</string>
<string name="app_label" msgid="9035307001052716210">"メディア ストレージ"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"写真選択ツール"</string>
<string name="artist_label" msgid="8105600993099120273">"アーティスト"</string>
<string name="unknown" msgid="2059049215682829375">"不明"</string>
<string name="root_images" msgid="5861633549189045666">"画像"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"続行"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"許可"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"許可しない"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">他 <xliff:g id="COUNT_1">^1</xliff:g> 件</item>
- <item quantity="one">他 <xliff:g id="COUNT_0">^1</xliff:g> 件</item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">他 <xliff:g id="COUNT_1">^1</xliff:g> 件の項目</item>
- <item quantity="one">他 <xliff:g id="COUNT_0">^1</xliff:g> 件の項目</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{他 <xliff:g id="COUNT_0">^1</xliff:g> 件}other{他 <xliff:g id="COUNT_1">^1</xliff:g> 件}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{他 <xliff:g id="COUNT_0">^1</xliff:g> 件のアイテム}other{他 <xliff:g id="COUNT_1">^1</xliff:g> 件のアイテム}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"アプリの一時ファイルの削除"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>が一部の一時ファイルを削除する権限を求めています。この処理により、バッテリーやモバイルデータの使用量が増えることがあります。"</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"アプリの一時ファイルを削除しています…"</string>
<string name="clear" msgid="5524638938415865915">"削除"</string>
<string name="allow" msgid="8885707816848569619">"許可"</string>
<string name="deny" msgid="6040983710442068936">"許可しない"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> 件の音声ファイルの変更を <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?</item>
- <item quantity="one">この音声ファイルの変更を <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> 件の音声ファイルを変更しています…</item>
- <item quantity="one">音声ファイルを変更しています…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> 本の動画の変更を <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?</item>
- <item quantity="one">この動画の変更を <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> 本の動画を変更しています…</item>
- <item quantity="one">動画を変更しています…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> 枚の写真の変更を <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?</item>
- <item quantity="one">この写真の変更を <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> 枚の写真を変更しています…</item>
- <item quantity="one">写真を変更しています…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> 件のアイテムの変更を <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?</item>
- <item quantity="one">このアイテムの変更を <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> 件のアイテムを変更しています…</item>
- <item quantity="one">アイテムを変更しています…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> 件の音声ファイルをゴミ箱に移動することを <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?</item>
- <item quantity="one">この音声ファイルをゴミ箱に移動することを <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> 件の音声ファイルをゴミ箱に移動しています…</item>
- <item quantity="one">音声ファイルをゴミ箱に移動しています…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> 本の動画をゴミ箱に移動することを <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?</item>
- <item quantity="one">この動画をゴミ箱に移動することを <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> 本の動画をゴミ箱に移動しています…</item>
- <item quantity="one">動画をゴミ箱に移動しています…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> 枚の写真をゴミ箱に移動することを <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?</item>
- <item quantity="one">この写真をゴミ箱に移動することを <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> 枚の写真をゴミ箱に移動しています…</item>
- <item quantity="one">写真をゴミ箱に移動しています…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> 件のアイテムをゴミ箱に移動することを <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?</item>
- <item quantity="one">このアイテムをゴミ箱に移動することを <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> 件のアイテムをゴミ箱に移動しています…</item>
- <item quantity="one">アイテムをゴミ箱に移動しています…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> 件の音声ファイルをゴミ箱から移動することを <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?</item>
- <item quantity="one">この音声ファイルをゴミ箱から移動することを <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> 件の音声ファイルをゴミ箱から移動しています…</item>
- <item quantity="one">音声ファイルをゴミ箱から移動しています…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> 本の動画をゴミ箱から移動することを <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?</item>
- <item quantity="one">この動画をゴミ箱から移動することを <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> 本の動画をゴミ箱から移動しています…</item>
- <item quantity="one">動画をゴミ箱から移動しています…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> 枚の写真をゴミ箱から移動することを <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?</item>
- <item quantity="one">この写真をゴミ箱から移動することを <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> 枚の写真をゴミ箱から移動しています…</item>
- <item quantity="one">写真をゴミ箱から移動しています…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> 件のアイテムをゴミ箱から移動することを <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?</item>
- <item quantity="one">このアイテムをゴミ箱から移動することを <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> 件のアイテムをゴミ箱から移動しています…</item>
- <item quantity="one">アイテムをゴミ箱から移動しています…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> 件の音声ファイルの削除を <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?</item>
- <item quantity="one">この音声ファイルの削除を <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> 件の音声ファイルを削除しています…</item>
- <item quantity="one">音声ファイルを削除しています…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> 本の動画の削除を <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?</item>
- <item quantity="one">この動画の削除を <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> 本の動画を削除しています…</item>
- <item quantity="one">動画を削除しています…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> 枚の写真の削除を <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?</item>
- <item quantity="one">この写真の削除を <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> 枚の写真を削除しています…</item>
- <item quantity="one">写真を削除しています…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> 件のアイテムの削除を <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?</item>
- <item quantity="one">このアイテムの削除を <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> 件のアイテムを削除しています…</item>
- <item quantity="one">アイテムを削除しています…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"追加"</string>
+ <string name="deselect" msgid="4297825044827769490">"選択を解除"</string>
+ <string name="deselected" msgid="8488133193326208475">"選択解除済み"</string>
+ <string name="select" msgid="2704765470563027689">"選択"</string>
+ <string name="selected" msgid="9151797369975828124">"選択済み"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> 件まで選択できます}other{<xliff:g id="COUNT_1">^1</xliff:g> 件まで選択できます}}"</string>
+ <string name="recent" msgid="6694613584743207874">"最近"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"画像または動画はありません"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"アルバムはありません"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"選択した写真を見る"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"写真"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"アルバム"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"プレビュー"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"仕事用に切り替える"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"個人用に切り替える"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"管理者によりブロックされています"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"個人用アプリから仕事用データにアクセスすることは認められていません"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"仕事用アプリから個人データにアクセスすることは認められていません"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"仕事用アプリは一時停止されています"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"仕事用の写真を開くには、仕事用アプリを有効にしてからもう一度試してください。"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"このアプリは、選択した写真にのみアクセスできます。"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> 件のアイテム}other{<xliff:g id="COUNT_1">^1</xliff:g> 件のアイテム}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"追加(<xliff:g id="COUNT">^1</xliff:g> 件)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"カメラ"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"ダウンロード"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"お気に入り"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"スクリーンショット"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"モーション フォト"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="TIME">%2$s</xliff:g>に撮影した<xliff:g id="ITEM_NAME">%1$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"<xliff:g id="TIME">%1$s</xliff:g>に撮影された動画(時間: <xliff:g id="DURATION">%2$s</xliff:g>)"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"写真"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"モーション フォト"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"動画をミュートします"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"動画のミュートを解除します"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"動画を再生します"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"動画を一時停止します"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"<xliff:g id="PKG_NAME">%1$s</xliff:g> からクラウド メディアを利用できるようになりました"</string>
+ <string name="not_selected" msgid="2244008151669896758">"未選択"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{この音声ファイルの変更を <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?}other{<xliff:g id="COUNT">^2</xliff:g> 件の音声ファイルの変更を <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{音声ファイルを変更しています…}other{<xliff:g id="COUNT">^1</xliff:g> 件の音声ファイルを変更しています…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{この動画の変更を <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?}other{<xliff:g id="COUNT">^2</xliff:g> 本の動画の変更を <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{動画を変更しています…}other{<xliff:g id="COUNT">^1</xliff:g> 本の動画を変更しています…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{この写真の変更を <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?}other{<xliff:g id="COUNT">^2</xliff:g> 枚の写真の変更を <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{写真を変更しています…}other{<xliff:g id="COUNT">^1</xliff:g> 枚の写真を変更しています…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{このアイテムの変更を <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?}other{<xliff:g id="COUNT">^2</xliff:g> 件のアイテムの変更を <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{アイテムを変更しています…}other{<xliff:g id="COUNT">^1</xliff:g> 件のアイテムを変更しています…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{この音声ファイルをゴミ箱に移動することを <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?}other{<xliff:g id="COUNT">^2</xliff:g> 件の音声ファイルをゴミ箱に移動することを <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{音声ファイルをゴミ箱に移動しています…}other{<xliff:g id="COUNT">^1</xliff:g> 件の音声ファイルをゴミ箱に移動しています…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{この動画をゴミ箱に移動することを <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?}other{<xliff:g id="COUNT">^2</xliff:g> 本の動画をゴミ箱に移動することを <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{動画をゴミ箱に移動しています…}other{<xliff:g id="COUNT">^1</xliff:g> 本の動画をゴミ箱に移動しています…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{この写真をゴミ箱に移動することを <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?}other{<xliff:g id="COUNT">^2</xliff:g> 枚の写真をゴミ箱に移動することを <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{写真をゴミ箱に移動しています…}other{<xliff:g id="COUNT">^1</xliff:g> 枚の写真をゴミ箱に移動しています…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{このアイテムをゴミ箱に移動することを <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?}other{<xliff:g id="COUNT">^2</xliff:g> 件のアイテムをゴミ箱に移動することを <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{アイテムをゴミ箱に移動しています…}other{<xliff:g id="COUNT">^1</xliff:g> 件のアイテムをゴミ箱に移動しています…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{この音声ファイルをゴミ箱から移動することを <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?}other{<xliff:g id="COUNT">^2</xliff:g> 件の音声ファイルをゴミ箱から移動することを <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{音声ファイルをゴミ箱から移動しています…}other{<xliff:g id="COUNT">^1</xliff:g> 件の音声ファイルをゴミ箱から移動しています…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{この動画をゴミ箱から移動することを <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?}other{<xliff:g id="COUNT">^2</xliff:g> 本の動画をゴミ箱から移動することを <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{動画をゴミ箱から移動しています…}other{<xliff:g id="COUNT">^1</xliff:g> 本の動画をゴミ箱から移動しています…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{この写真をゴミ箱から移動することを <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?}other{<xliff:g id="COUNT">^2</xliff:g> 枚の写真をゴミ箱から移動することを <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{写真をゴミ箱から移動しています…}other{<xliff:g id="COUNT">^1</xliff:g> 枚の写真をゴミ箱から移動しています…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{このアイテムをゴミ箱から移動することを <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?}other{<xliff:g id="COUNT">^2</xliff:g> 件のアイテムをゴミ箱から移動することを <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{アイテムをゴミ箱から移動しています…}other{<xliff:g id="COUNT">^1</xliff:g> 件のアイテムをゴミ箱から移動しています…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{この音声ファイルの削除を <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?}other{<xliff:g id="COUNT">^2</xliff:g> 件の音声ファイルの削除を <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{音声ファイルを削除しています…}other{<xliff:g id="COUNT">^1</xliff:g> 件の音声ファイルを削除しています…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{この動画の削除を <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?}other{<xliff:g id="COUNT">^2</xliff:g> 本の動画の削除を <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{動画を削除しています…}other{<xliff:g id="COUNT">^1</xliff:g> 本の動画を削除しています…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{この写真の削除を <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?}other{<xliff:g id="COUNT">^2</xliff:g> 枚の写真の削除を <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{写真を削除しています…}other{<xliff:g id="COUNT">^1</xliff:g> 枚の写真を削除しています…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{このアイテムの削除を <xliff:g id="APP_NAME_0">^1</xliff:g> に許可しますか?}other{<xliff:g id="COUNT">^2</xliff:g> 件のアイテムの削除を <xliff:g id="APP_NAME_1">^1</xliff:g> に許可しますか?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{アイテムを削除しています…}other{<xliff:g id="COUNT">^1</xliff:g> 件のアイテムを削除しています…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g>はメディア ファイルを処理できません"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"メディアの処理をキャンセルしました"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"メディア処理エラー"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index d7d8808..b1048d9 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"მედია"</string>
<string name="storage_description" msgid="4081716890357580107">"ადგილობრივი მეხსიერება"</string>
<string name="app_label" msgid="9035307001052716210">"მედიის საცავი"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"ფოტოების ამომრჩევი"</string>
<string name="artist_label" msgid="8105600993099120273">"შემსრულებელი"</string>
<string name="unknown" msgid="2059049215682829375">"უცნობი"</string>
<string name="root_images" msgid="5861633549189045666">"სურათები"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"გაგრძელება"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"დაშვება"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"უარყოფა"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">და კიდევ <xliff:g id="COUNT_1">^1</xliff:g> დამატებითი ერთეული</item>
- <item quantity="one">და კიდევ <xliff:g id="COUNT_0">^1</xliff:g> დამატებითი ერთეული</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{და კიდევ <xliff:g id="COUNT_0">^1</xliff:g> დამატებითი ერთეული}other{და კიდევ <xliff:g id="COUNT_1">^1</xliff:g> დამატებითი ერთეული}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"გასუფთავდეს აპის დროებითი ფაილები?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-ს სურს, გაასუფთაოს დროებითი ფაილები. ამან შეიძლება ზოგიერთი ბატარეის ან მობილური ინტერნეტის გაზრდილი მოხმარება გამოიწვიოს."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"მიმდინარეობს აპის დროებითი ფაილების გასუფთავება…"</string>
<string name="clear" msgid="5524638938415865915">"გასუფთავება"</string>
<string name="allow" msgid="8885707816848569619">"დაშვება"</string>
<string name="deny" msgid="6040983710442068936">"უარყოფა"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, შეცვალოს <xliff:g id="COUNT">^2</xliff:g> აუდიოფაილი?</item>
- <item quantity="one">აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, შეცვალოს ეს აუდიოფაილი?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> აუდიოფაილის მოდიფიკაცია…</item>
- <item quantity="one">მიმდინარეობს აუდიოფაილის მოდიფიკაცია…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, შეცვალოს <xliff:g id="COUNT">^2</xliff:g> ვიდეო?</item>
- <item quantity="one">აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, შეცვალოს ეს ვიდეო?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> ვიდეოს მოდიფიკაცია…</item>
- <item quantity="one">მიმდინარეობს ვიდეოს მოდიფიკაცია…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, შეცვალოს <xliff:g id="COUNT">^2</xliff:g> ფოტო?</item>
- <item quantity="one">აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, შეცვალოს ეს ფოტო?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> ფოტოს მოდიფიკაცია…</item>
- <item quantity="one">მიმდინარეობს ფოტოს მოდიფიკაცია…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, შეცვალოს <xliff:g id="COUNT">^2</xliff:g> ერთეული?</item>
- <item quantity="one">აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, შეცვალოს ეს ერთეული?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> ერთეულის მოდიფიკაცია…</item>
- <item quantity="one">მიმდინარეობს ერთეულის მოდიფიკაცია…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, გადაიტანოს <xliff:g id="COUNT">^2</xliff:g> აუდიოფაილი წაშლილებში?</item>
- <item quantity="one">აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, გადაიტანოს ეს აუდიოფაილი წაშლილებში?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> აუდიოფაილის წაშლილებში გადატანა…</item>
- <item quantity="one">მიმდინარეობს აუდიოფაილის წაშლილებში გადატანა…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, გადაიტანოს <xliff:g id="COUNT">^2</xliff:g> ვიდეო წაშლილებში?</item>
- <item quantity="one">აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, გადაიტანოს ეს ვიდეო წაშლილებში?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> ვიდეოს წაშლილებში გადატანა…</item>
- <item quantity="one">მიმდინარეობს ვიდეოს წაშლილებში გადატანა…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, გადაიტანოს <xliff:g id="COUNT">^2</xliff:g> ფოტო წაშლილებში?</item>
- <item quantity="one">აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, გადაიტანოს ეს ფოტო წაშლილებში?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> ფოტოს წაშლილებში გადატანა…</item>
- <item quantity="one">მიმდინარეობს ფოტოს წაშლილებში გადატანა…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, გადაიტანოს <xliff:g id="COUNT">^2</xliff:g> ერთეული წაშლილებში?</item>
- <item quantity="one">აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, გადაიტანოს ეს ერთეული წაშლილებში?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> ერთეულის წაშლილებში გადატანა…</item>
- <item quantity="one">მიმდინარეობს ერთეულის წაშლილებში გადატანა…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, გადმოიტანოს <xliff:g id="COUNT">^2</xliff:g> აუდიოფაილი წაშლილებიდან?</item>
- <item quantity="one">აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, გადმოიტანოს ეს აუდიოფაილი წაშლილებიდან?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> აუდიოფაილის წაშლილებიდან გადმოტანა…</item>
- <item quantity="one">მიმდინარეობს აუდიოფაილის წაშლილებიდან გადმოტანა…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, გადმოიტანოს <xliff:g id="COUNT">^2</xliff:g> ვიდეო წაშლილებიდან?</item>
- <item quantity="one">აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, გადმოიტანოს ეს ვიდეო წაშლილებიდან?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> ვიდეოს წაშლილებიდან გადმოტანა…</item>
- <item quantity="one">მიმდინარეობს ვიდეოს წაშლილებიდან გადმოტანა…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, გადმოიტანოს <xliff:g id="COUNT">^2</xliff:g> ფოტო წაშლილებიდან?</item>
- <item quantity="one">აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, გადმოიტანოს ეს ფოტო წაშლილებიდან?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> ფოტოს წაშლილებიდან გადმოტანა…</item>
- <item quantity="one">მიმდინარეობს ფოტოს წაშლილებიდან გადმოტანა…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, გადმოიტანოს <xliff:g id="COUNT">^2</xliff:g> ერთეული წაშლილებიდან?</item>
- <item quantity="one">აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, გადმოიტანოს ეს ერთეული წაშლილებიდან?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> ერთეულის წაშლილებიდან გადმოტანა…</item>
- <item quantity="one">მიმდინარეობს ერთეულის წაშლილებიდან გადმოტანა…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, წაშალოს <xliff:g id="COUNT">^2</xliff:g> აუდიოფაილი?</item>
- <item quantity="one">აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, წაშალოს ეს აუდიოფაილი?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> აუდიოფაილის წაშლა…</item>
- <item quantity="one">მიმდინარეობს აუდიოფაილის წაშლა…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, წაშალოს <xliff:g id="COUNT">^2</xliff:g> ვიდეო?</item>
- <item quantity="one">აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, წაშალოს ეს ვიდეო?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> ვიდეოს წაშლა…</item>
- <item quantity="one">მიმდინარეობს ვიდეოს წაშლა…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, წაშალოს <xliff:g id="COUNT">^2</xliff:g> ფოტო?</item>
- <item quantity="one">აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, წაშალოს ეს ფოტო?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> ფოტოს წაშლა…</item>
- <item quantity="one">მიმდინარეობს ფოტოს წაშლა…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, წაშალოს <xliff:g id="COUNT">^2</xliff:g> ერთეული?</item>
- <item quantity="one">აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, წაშალოს ეს ერთეული?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> ერთეულის წაშლა…</item>
- <item quantity="one">მიმდინარეობს ერთეულის წაშლა…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"დამატება"</string>
+ <string name="deselect" msgid="4297825044827769490">"არჩევის გაუქმება"</string>
+ <string name="deselected" msgid="8488133193326208475">"არჩევა გაუქმებულია"</string>
+ <string name="select" msgid="2704765470563027689">"არჩევა"</string>
+ <string name="selected" msgid="9151797369975828124">"არჩეულია"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{აირჩიეთ <xliff:g id="COUNT_0">^1</xliff:g>-მდე ერთეული}other{აირჩიეთ <xliff:g id="COUNT_1">^1</xliff:g>-მდე ერთეული}}"</string>
+ <string name="recent" msgid="6694613584743207874">"ბოლოდროინდელი"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"ფოტოები და ვიდეოები არ არის"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"ალბომები არ არის"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"ხედი არჩეულია"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"ფოტოები"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"ალბომები"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"გადახედვა"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"სამსახურის პროფილზე გადართვა"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"პირად პროფილზე გადართვა"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"დაბლოკილია თქვენი ადმინისტრატორის მიერ"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"პირადი აპიდან სამუშაო სამსახურებრივ მონაცემებზე წვდომა დაუშვებელია"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"სამსახურის აპიდან პერსონალურ მონაცემებზე წვდომა დაუშვებელია"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"სამსახურის აპები დაპაუზებულია"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"სამსახურის ფოტოების გასახსნელად ჩართეთ თქვენი სამსახურის აპები და შემდეგ ცადეთ ხელახლა"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"ეს აპი მხოლოდ თქვენ მიერ მონიშნულ ფოტოებზე წვდომას შეძლებს"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> ერთეული}other{<xliff:g id="COUNT_1">^1</xliff:g> ერთეული}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"დამატება (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"კამერა"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"ჩამოტვირთვები"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"რჩეულები"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"ეკრანის ანაბეჭდები"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"მოძრავი ფოტო"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> გადაღებულია <xliff:g id="TIME">%2$s</xliff:g>-ზე"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"ვიდეოს გადაღების დრო და ხანგრძლივობა: <xliff:g id="TIME">%1$s</xliff:g>, <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"ფოტო"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"მოძრავი ფოტო"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"ვიდეოს დადუმება"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"ვიდეოს დადუმების მოხსნა"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"ვიდეოს დაკვრა"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"ვიდეოს დაპაუზება"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"ღრუბლოვანი მედია უკვე ხელმისაწვდომია <xliff:g id="PKG_NAME">%1$s</xliff:g>-ისგან"</string>
+ <string name="not_selected" msgid="2244008151669896758">"არ არის არჩეული"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, შეცვალოს ეს აუდიოფაილი?}other{აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, შეცვალოს <xliff:g id="COUNT">^2</xliff:g> აუდიოფაილი?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{მიმდინარეობს აუდიოფაილის მოდიფიკაცია…}other{მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> აუდიოფაილის მოდიფიკაცია…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, შეცვალოს ეს ვიდეო?}other{აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, შეცვალოს <xliff:g id="COUNT">^2</xliff:g> ვიდეო?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{მიმდინარეობს ვიდეოს მოდიფიკაცია…}other{მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> ვიდეოს მოდიფიკაცია…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, შეცვალოს ეს ფოტო?}other{აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, შეცვალოს <xliff:g id="COUNT">^2</xliff:g> ფოტო?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{მიმდინარეობს ფოტოს მოდიფიკაცია…}other{მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> ფოტოს მოდიფიკაცია…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, შეცვალოს ეს ერთეული?}other{აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, შეცვალოს <xliff:g id="COUNT">^2</xliff:g> ერთეული?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{მიმდინარეობს ერთეულის მოდიფიკაცია…}other{მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> ერთეულის მოდიფიკაცია…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, გადაიტანოს ეს აუდიოფაილი წაშლილებში?}other{აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, გადაიტანოს <xliff:g id="COUNT">^2</xliff:g> აუდიოფაილი წაშლილებში?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{მიმდინარეობს აუდიოფაილის წაშლილებში გადატანა…}other{მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> აუდიოფაილის წაშლილებში გადატანა…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, გადაიტანოს ეს ვიდეო წაშლილებში?}other{აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, გადაიტანოს <xliff:g id="COUNT">^2</xliff:g> ვიდეო წაშლილებში?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{მიმდინარეობს ვიდეოს წაშლილებში გადატანა…}other{მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> ვიდეოს წაშლილებში გადატანა…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, გადაიტანოს ეს ფოტო წაშლილებში?}other{აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, გადაიტანოს <xliff:g id="COUNT">^2</xliff:g> ფოტო წაშლილებში?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{მიმდინარეობს ფოტოს წაშლილებში გადატანა…}other{მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> ფოტოს წაშლილებში გადატანა…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, გადაიტანოს ეს ერთეული წაშლილებში?}other{აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, გადაიტანოს <xliff:g id="COUNT">^2</xliff:g> ერთეული წაშლილებში?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{მიმდინარეობს ერთეულის წაშლილებში გადატანა…}other{მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> ერთეულის წაშლილებში გადატანა…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, გადმოიტანოს ეს აუდიოფაილი წაშლილებიდან?}other{აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, გადმოიტანოს <xliff:g id="COUNT">^2</xliff:g> აუდიოფაილი წაშლილებიდან?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{მიმდინარეობს აუდიოფაილის წაშლილებიდან გადმოტანა…}other{მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> აუდიოფაილის წაშლილებიდან გადმოტანა…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, გადმოიტანოს ეს ვიდეო წაშლილებიდან?}other{აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, გადმოიტანოს <xliff:g id="COUNT">^2</xliff:g> ვიდეო წაშლილებიდან?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{მიმდინარეობს ვიდეოს წაშლილებიდან გადმოტანა…}other{მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> ვიდეოს წაშლილებიდან გადმოტანა…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, გადმოიტანოს ეს ფოტო წაშლილებიდან?}other{აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, გადმოიტანოს <xliff:g id="COUNT">^2</xliff:g> ფოტო წაშლილებიდან?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{მიმდინარეობს ფოტოს წაშლილებიდან გადმოტანა…}other{მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> ფოტოს წაშლილებიდან გადმოტანა…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, გადმოიტანოს ეს ერთეული წაშლილებიდან?}other{აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, გადმოიტანოს <xliff:g id="COUNT">^2</xliff:g> ერთეული წაშლილებიდან?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{მიმდინარეობს ერთეულის წაშლილებიდან გადმოტანა…}other{მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> ერთეულის წაშლილებიდან გადმოტანა…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, წაშალოს ეს აუდიოფაილი?}other{აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, წაშალოს <xliff:g id="COUNT">^2</xliff:g> აუდიოფაილი?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{მიმდინარეობს აუდიოფაილის წაშლა…}other{მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> აუდიოფაილის წაშლა…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, წაშალოს ეს ვიდეო?}other{აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, წაშალოს <xliff:g id="COUNT">^2</xliff:g> ვიდეო?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{მიმდინარეობს ვიდეოს წაშლა…}other{მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> ვიდეოს წაშლა…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, წაშალოს ეს ფოტო?}other{აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, წაშალოს <xliff:g id="COUNT">^2</xliff:g> ფოტო?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{მიმდინარეობს ფოტოს წაშლა…}other{მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> ფოტოს წაშლა…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{აძლევთ უფლებას <xliff:g id="APP_NAME_0">^1</xliff:g>-ს, წაშალოს ეს ერთეული?}other{აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, წაშალოს <xliff:g id="COUNT">^2</xliff:g> ერთეული?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{მიმდინარეობს ერთეულის წაშლა…}other{მიმდინარეობს <xliff:g id="COUNT">^1</xliff:g> ერთეულის წაშლა…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> ვერ ამუშავებს მედია ფაილებს"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"მედიის დამუშავება გაუქმდა"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"მედიის დამუშავებისას შეცდომა მოხდა"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index f6a9252..4b6698c 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Мультимeдиа"</string>
<string name="storage_description" msgid="4081716890357580107">"Жергілікті жад"</string>
<string name="app_label" msgid="9035307001052716210">"Мультимедиа қоймасы"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"Сурет таңдағыш"</string>
<string name="artist_label" msgid="8105600993099120273">"Орындаушы"</string>
<string name="unknown" msgid="2059049215682829375">"Белгісіз"</string>
<string name="root_images" msgid="5861633549189045666">"Кескіндер"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Жалғастыру"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Рұқсат беру"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Тыйым салу"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Тағы <xliff:g id="COUNT_1">^1</xliff:g> элемент</item>
- <item quantity="one">Тағы <xliff:g id="COUNT_0">^1</xliff:g> элемент</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{Тағы <xliff:g id="COUNT_0">^1</xliff:g>}other{Тағы <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Тағы <xliff:g id="COUNT_0">^1</xliff:g> элемент}other{Тағы <xliff:g id="COUNT_1">^1</xliff:g> элемент}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Қолданбаның уақытша файлдарын өшіру"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> кейбір уақытша файлдарды өшіргісі келеді. Соған байланысты батарея тез отыруы немесе мобильдік интернет трафигі көп кетуі мүмкін."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Қолданбаның уақытша файлдары өшірілуде…"</string>
<string name="clear" msgid="5524638938415865915">"Өшіру"</string>
<string name="allow" msgid="8885707816848569619">"Рұқсат ету"</string>
<string name="deny" msgid="6040983710442068936">"Тыйым салу"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> аудиофайлды өзгертуге рұқсат етесіз бе?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы аудиофайлды өзгертуге рұқсат етесіз бе?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> аудиофайл өзгертілуде…</item>
- <item quantity="one">Аудиофайл өзгертілуде…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> бейнені өзгертуге рұқсат етесіз бе?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы бейнені өзгертуге рұқсат етесіз бе?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> бейне өзгертілуде…</item>
- <item quantity="one">Бейне өзгертілуде…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> фотосуретті өзгертуге рұқсат етесіз бе?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы фотосуретті өзгертуге рұқсат етесіз бе?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> фотосурет өзгертілуде…</item>
- <item quantity="one">Фотосурет өзгертілуде…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> элементті өзгертуге рұқсат етесіз бе?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы элементті өзгертуге рұқсат етесіз бе?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> элемент өзгертілуде…</item>
- <item quantity="one">Элемент өзгертілуде…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> аудиофайлды себетке жіберуге рұқсат етесіз бе?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы аудиофайлды себетке жіберуге рұқсат етесіз бе?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> аудиофайл себетке жіберілуде…</item>
- <item quantity="one">Аудиофайл себетке жіберілуде…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> бейнені себетке жіберуге рұқсат етесіз бе?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы бейнені себетке жіберуге рұқсат етесіз бе?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> бейне себетке жіберілуде…</item>
- <item quantity="one">Бейне себетке жіберілуде…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> фотосуретті себетке жіберуге рұқсат етесіз бе?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы фотосуретті себетке жіберуге рұқсат етесіз бе?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> фотосурет себетке жіберілуде…</item>
- <item quantity="one">Фотосурет себетке жіберілуде…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> элементті себетке жіберуге рұқсат етесіз бе?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы элементті себетке жіберуге рұқсат етесіз бе?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> элемент себетке жіберілуде…</item>
- <item quantity="one">Элемент себетке жіберілуде…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> аудиофайлды себеттен шығаруға рұқсат етесіз бе?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына бұл аудиофайлды себеттен шығаруға рұқсат етесіз бе?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> аудиофайл себеттен шығарылуда…</item>
- <item quantity="one">Аудиофайл себеттен шығарылуда…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> бейнені себеттен шығаруға рұқсат етесіз бе?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы бейнені себеттен шығаруға рұқсат етесіз бе?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> бейне себеттен шығарылуда…</item>
- <item quantity="one">Бейне себеттен шығарылуда…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> фотосуретті себеттен шығаруға рұқсат етесіз бе?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы фотосуретті себеттен шығаруға рұқсат етесіз бе?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> фотосурет себеттен шығарылуда…</item>
- <item quantity="one">Фотосурет себеттен шығарылуда…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> элементті себеттен шығаруға рұқсат етесіз бе?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы элементті себеттен шығаруға рұқсат етесіз бе?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> элемент себеттен шығарылуда…</item>
- <item quantity="one">Элемент себеттен шығарылуда…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> аудиофайлды жоюға рұқсат етесіз бе?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы аудиофайлды жоюға рұқсат етесіз бе?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> аудиофайл жойылуда…</item>
- <item quantity="one">Аудиофайл жойылуда…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> бейнені жоюға рұқсат етесіз бе?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы бейнені жоюға рұқсат етесіз бе?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> бейне жойылуда…</item>
- <item quantity="one">Бейне жойылуда…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> фотосуретті жоюға рұқсат етесіз бе?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы фотосуретті жоюға рұқсат етесіз бе?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> фотосурет жойылуда…</item>
- <item quantity="one">Фотосурет жойылуда…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> элементті жоюға рұқсат етесіз бе?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы элементті жоюға рұқсат етесіз бе?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> элемент жойылуда…</item>
- <item quantity="one">Элемент жойылуда…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Қосу"</string>
+ <string name="deselect" msgid="4297825044827769490">"Таңдамау"</string>
+ <string name="deselected" msgid="8488133193326208475">"Таңдау алынған"</string>
+ <string name="select" msgid="2704765470563027689">"Таңдау"</string>
+ <string name="selected" msgid="9151797369975828124">"Таңдалған"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> элементке дейін таңдаңыз.}other{<xliff:g id="COUNT_1">^1</xliff:g> элементке дейін таңдаңыз.}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Соңғы"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Фотосуреттер немесе бейнелер жоқ."</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Альбомдар жоқ."</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Таңдалғанды көру"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Фотосуреттер"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Aльбомдар"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Алдын ала көру"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Жұмыс профиліне ауысу"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Жеке профильге ауысу"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Әкімшіңіз бөгеген"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Жұмыс дерегіне жеке қолданбадан кіруге рұқсат жоқ."</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Жеке дерекке жұмыс қолданбасынан кіруге рұқсат жоқ."</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Жұмыс қолданбалары кідіртілді"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Жұмыс аккаунтындағы фотосуреттерді ашу үшін жұмыс қолданбаларын қосып, содан кейін қайталап көріңіз."</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Бұл қолданба өзіңіз таңдаған фотосуреттерді ғана пайдалана алады."</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> элемент}other{<xliff:g id="COUNT_1">^1</xliff:g> элемент}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Қосу (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Камера"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Жүктеп алынғандар"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Таңдаулылар"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Скриншоттар"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Қимылды сурет"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> түсірілген күні: <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Бейне түсіру уақыты: <xliff:g id="TIME">%1$s</xliff:g>, ұзақтығы: <xliff:g id="DURATION">%2$s</xliff:g>."</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Фотосурет"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Қимылды сурет"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Бейненің дыбысын өшіру"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Бейненің дыбысын қосу"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Бейнені ойнату"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Бейнені кідірту"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Бұлтқа сақталған медиафайл енді <xliff:g id="PKG_NAME">%1$s</xliff:g> қолданбасында қолжетімді."</string>
+ <string name="not_selected" msgid="2244008151669896758">"таңдалмаған"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы аудиофайлды өзгертуге рұқсат етілсін бе?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> аудиофайлды өзгертуге рұқсат етілсін бе?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Аудиофайл өзгертілуде…}other{<xliff:g id="COUNT">^1</xliff:g> аудиофайл өзгертілуде…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы бейнені өзгертуге рұқсат етілсін бе?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> бейнені өзгертуге рұқсат етілсін бе?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Бейне өзгертілуде…}other{<xliff:g id="COUNT">^1</xliff:g> бейне өзгертілуде…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы фотосуретті өзгертуге рұқсат етілсін бе?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> фотосуретті өзгертуге рұқсат етілсін бе?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Фотосурет өзгертілуде…}other{<xliff:g id="COUNT">^1</xliff:g> фотосурет өзгертілуде…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы элементті өзгертуге рұқсат етілсін бе?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> элементті өзгертуге рұқсат етілсін бе?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Элемент өзгертілуде…}other{<xliff:g id="COUNT">^1</xliff:g> элемент өзгертілуде…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы аудиофайлды себетке жіберуге рұқсат етілсін бе?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> аудиофайлды себетке жіберуге рұқсат етілсін бе?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Аудиофайл себетке жіберілуде…}other{<xliff:g id="COUNT">^1</xliff:g> аудиофайл себетке жіберілуде…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы бейнені себетке жіберуге рұқсат етілсін бе?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> бейнені себетке жіберуге рұқсат етілсін бе?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Бейне себетке жіберілуде…}other{<xliff:g id="COUNT">^1</xliff:g> бейне себетке жіберілуде…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы фотосуретті себетке жіберуге рұқсат етілсін бе?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> фотосуретті себетке жіберуге рұқсат етілсін бе?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Фотосурет себетке жіберілуде…}other{<xliff:g id="COUNT">^1</xliff:g> фотосурет себетке жіберілуде…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы элементті себетке жіберуге рұқсат етілсін бе?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> элементті себетке жіберуге рұқсат етілсін бе?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Элемент себетке жіберілуде…}other{<xliff:g id="COUNT">^1</xliff:g> элемент себетке жіберілуде…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына бұл аудиофайлды себеттен шығаруға рұқсат етілсін бе?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> аудиофайлды себеттен шығаруға рұқсат етілсін бе?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Аудиофайл себеттен шығарылуда…}other{<xliff:g id="COUNT">^1</xliff:g> аудиофайл себеттен шығарылуда…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы бейнені себеттен шығаруға рұқсат етілсін бе?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> бейнені себеттен шығаруға рұқсат етілсін бе?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Бейне себеттен шығарылуда…}other{<xliff:g id="COUNT">^1</xliff:g> бейне себеттен шығарылуда…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы фотосуретті себеттен шығаруға рұқсат етілсін бе?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> фотосуретті себеттен шығаруға рұқсат етілсін бе?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Фотосурет себеттен шығарылуда…}other{<xliff:g id="COUNT">^1</xliff:g> фотосурет себеттен шығарылуда…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы элементті себеттен шығаруға рұқсат етілсін бе?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> элементті себеттен шығаруға рұқсат етілсін бе?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Элемент себеттен шығарылуда…}other{<xliff:g id="COUNT">^1</xliff:g> элемент себеттен шығарылуда…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы аудиофайлды жоюға рұқсат етілсін бе?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> аудиофайлды жоюға рұқсат етілсін бе?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Аудиофайл жойылуда…}other{<xliff:g id="COUNT">^1</xliff:g> аудиофайл жойылуда…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы бейнені жоюға рұқсат етілсін бе?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> бейнені жоюға рұқсат етілсін бе?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Бейне жойылуда…}other{<xliff:g id="COUNT">^1</xliff:g> бейне жойылуда…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы фотосуретті жоюға рұқсат етілсін бе?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> фотосуретті жоюға рұқсат етілсін бе?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Фотосурет жойылуда…}other{<xliff:g id="COUNT">^1</xliff:g> фотосурет жойылуда…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> қолданбасына осы элементті жоюға рұқсат етілсін бе?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g id="COUNT">^2</xliff:g> элементті жоюға рұқсат етілсін бе?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Элемент жойылуда…}other{<xliff:g id="COUNT">^1</xliff:g> элемент жойылуда…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> қолданбасы медиа файлдарды өңдей алмайды."</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Медиафайлды өңдеу тоқтатылды."</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Медиафайлды өңдеуде қате пайда болды."</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index d7662d6..f479d9a 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"មេឌៀ"</string>
<string name="storage_description" msgid="4081716890357580107">"ទំហំផ្ទុកមូលដ្ឋាន"</string>
<string name="app_label" msgid="9035307001052716210">"ទំហំផ្ទុកមេឌៀ"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"ផ្ទាំងជ្រើសយករូបថត"</string>
<string name="artist_label" msgid="8105600993099120273">"សិល្បករ"</string>
<string name="unknown" msgid="2059049215682829375">"មិនស្គាល់"</string>
<string name="root_images" msgid="5861633549189045666">"រូបភាព"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"បន្ត"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"អនុញ្ញាត"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"បដិសេធ"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">បូករួមទាំងធាតុ <xliff:g id="COUNT_1">^1</xliff:g> បន្ថែមទៀត</item>
- <item quantity="one">បូករួមទាំងធាតុ <xliff:g id="COUNT_0">^1</xliff:g> បន្ថែមទៀត</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{បូករួមទាំងធាតុ <xliff:g id="COUNT_0">^1</xliff:g> បន្ថែមទៀត}other{បូករួមទាំងធាតុ <xliff:g id="COUNT_1">^1</xliff:g> បន្ថែមទៀត}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"សម្អាតឯកសារកម្មវិធីបណ្ដោះអាសន្ន"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ចង់សម្អាតឯកសារបណ្ដោះអាសន្នមួយចំនួន។ សកម្មភាពនេះអាចបណ្ដាលឱ្យការប្រើប្រាស់ថ្ម ឬទិន្នន័យទូរសព្ទចល័តមានការកើនឡើង។"</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"កំពុងសម្អាតឯកសារកម្មវិធីបណ្ដោះអាសន្ន…"</string>
<string name="clear" msgid="5524638938415865915">"សម្អាត"</string>
<string name="allow" msgid="8885707816848569619">"អនុញ្ញាត"</string>
<string name="deny" msgid="6040983710442068936">"បដិសេធ"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> កែឯកសារសំឡេង <xliff:g id="COUNT">^2</xliff:g> ឬ?</item>
- <item quantity="one">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> កែឯកសារសំឡេងនេះឬ?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">កំពុងកែឯកសារសំឡេង <xliff:g id="COUNT">^1</xliff:g>…</item>
- <item quantity="one">កំពុងកែឯកសារសំឡេង…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> កែវីដេអូ <xliff:g id="COUNT">^2</xliff:g> ឬ?</item>
- <item quantity="one">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> កែវីដេអូនេះឬ?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">កំពុងកែវីដេអូ <xliff:g id="COUNT">^1</xliff:g>…</item>
- <item quantity="one">កំពុងកែវីដេអូ…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> កែរូបថត <xliff:g id="COUNT">^2</xliff:g> សន្លឹកឬ?</item>
- <item quantity="one">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> កែរូបថតនេះឬ?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">កំពុងកែរូបថត <xliff:g id="COUNT">^1</xliff:g> សន្លឹក…</item>
- <item quantity="one">កំពុងកែរូបថត…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> កែធាតុ <xliff:g id="COUNT">^2</xliff:g> ឬ?</item>
- <item quantity="one">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> កែធាតុនេះឬ?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">កំពុងកែធាតុ <xliff:g id="COUNT">^1</xliff:g>…</item>
- <item quantity="one">កំពុងកែធាតុ…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> ផ្លាស់ទីឯកសារសំឡេង <xliff:g id="COUNT">^2</xliff:g> ទៅធុងសំរាមឬ?</item>
- <item quantity="one">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> ផ្លាស់ទីឯកសារសំឡេងនេះទៅធុងសំរាមឬ?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">កំពុងផ្លាស់ទីឯកសារសំឡេង <xliff:g id="COUNT">^1</xliff:g> ទៅធុងសំរាម…</item>
- <item quantity="one">កំពុងផ្លាស់ទីឯកសារសំឡេងទៅធុងសំរាម…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> ផ្លាស់ទីវីដេអូ <xliff:g id="COUNT">^2</xliff:g> ទៅធុងសំរាមឬ?</item>
- <item quantity="one">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> ផ្លាស់ទីវីដេអូនេះទៅធុងសំរាមឬ?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">កំពុងផ្លាស់ទីវីដេអូ <xliff:g id="COUNT">^1</xliff:g> ទៅធុងសំរាម…</item>
- <item quantity="one">កំពុងផ្លាស់ទីវីដេអូទៅធុងសំរាម…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> ផ្លាស់ទីរូបថត <xliff:g id="COUNT">^2</xliff:g> សន្លឹកទៅធុងសំរាមឬ?</item>
- <item quantity="one">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> ផ្លាស់ទីរូបថតនេះទៅធុងសំរាមឬ?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">កំពុងផ្លាស់ទីរូបថត <xliff:g id="COUNT">^1</xliff:g> សន្លឹកទៅធុងសំរាម…</item>
- <item quantity="one">កំពុងផ្លាស់ទីរូបថតទៅធុងសំរាម…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> ផ្លាស់ទីធាតុ <xliff:g id="COUNT">^2</xliff:g> ទៅធុងសំរាមឬ?</item>
- <item quantity="one">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> ផ្លាស់ទីធាតុនេះទៅធុងសំរាមឬ?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">កំពុងផ្លាស់ទីធាតុ <xliff:g id="COUNT">^1</xliff:g> ទៅធុងសំរាម…</item>
- <item quantity="one">កំពុងផ្លាស់ទីធាតុទៅធុងសំរាម…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> ផ្លាស់ទីឯកសារសំឡេង <xliff:g id="COUNT">^2</xliff:g> ចេញពីធុងសំរាមឬ?</item>
- <item quantity="one">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> ផ្លាស់ទីឯកសារសំឡេងនេះចេញពីធុងសំរាមឬ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">កំពុងផ្លាស់ទីឯកសារសំឡេង <xliff:g id="COUNT">^1</xliff:g> ចេញពីធុងសំរាម…</item>
- <item quantity="one">កំពុងផ្លាស់ទីឯកសារសំឡេងចេញពីធុងសំរាម…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> ផ្លាស់ទីវីដេអូ <xliff:g id="COUNT">^2</xliff:g> ចេញពីធុងសំរាមឬ?</item>
- <item quantity="one">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> ផ្លាស់ទីវីដេអូនេះចេញពីធុងសំរាមឬ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">កំពុងផ្លាស់ទីវីដេអូ <xliff:g id="COUNT">^1</xliff:g> ចេញពីធុងសំរាម…</item>
- <item quantity="one">កំពុងផ្លាស់ទីវីដេអូចេញពីធុងសំរាម…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> ផ្លាស់ទីរូបថត <xliff:g id="COUNT">^2</xliff:g> សន្លឹកចេញពីធុងសំរាមឬ?</item>
- <item quantity="one">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> ផ្លាស់ទីរូបថតនេះចេញពីធុងសំរាមឬ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">កំពុងផ្លាស់ទីរូបថត <xliff:g id="COUNT">^1</xliff:g> សន្លឹកចេញពីធុងសំរាម…</item>
- <item quantity="one">កំពុងផ្លាស់ទីរូបថតចេញពីធុងសំរាម…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> ផ្លាស់ទីធាតុ <xliff:g id="COUNT">^2</xliff:g> ចេញពីធុងសំរាមឬ?</item>
- <item quantity="one">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> ផ្លាស់ទីធាតុនេះចេញពីធុងសំរាមឬ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">កំពុងផ្លាស់ទីធាតុ <xliff:g id="COUNT">^1</xliff:g> ចេញពីធុងសំរាម…</item>
- <item quantity="one">កំពុងផ្លាស់ទីធាតុចេញពីធុងសំរាម…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> លុបឯកសារសំឡេង <xliff:g id="COUNT">^2</xliff:g> ឬ?</item>
- <item quantity="one">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> លុបឯកសារសំឡេងនេះឬ?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">កំពុងលុបឯកសារសំឡេង <xliff:g id="COUNT">^1</xliff:g>…</item>
- <item quantity="one">កំពុងលុបឯកសារសំឡេង…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> លុបវីដេអូ <xliff:g id="COUNT">^2</xliff:g> ឬ?</item>
- <item quantity="one">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> លុបវីដេអូនេះឬ?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">កំពុងលុបវីដេអូ <xliff:g id="COUNT">^1</xliff:g>…</item>
- <item quantity="one">កំពុងលុបវីដេអូ…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> លុបរូបថត <xliff:g id="COUNT">^2</xliff:g> សន្លឹកឬ?</item>
- <item quantity="one">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> លុបរូបថតនេះឬ?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">កំពុងលុបរូបថត <xliff:g id="COUNT">^1</xliff:g>…</item>
- <item quantity="one">កំពុងលុបរូបថត…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> លុបធាតុ <xliff:g id="COUNT">^2</xliff:g> ឬ?</item>
- <item quantity="one">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> លុបធាតុនេះឬ?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">កំពុងលុបធាតុ <xliff:g id="COUNT">^1</xliff:g>…</item>
- <item quantity="one">កំពុងលុបធាតុ…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"បញ្ចូល"</string>
+ <string name="deselect" msgid="4297825044827769490">"ដកការជ្រើសរើស"</string>
+ <string name="deselected" msgid="8488133193326208475">"បានដកការជ្រើសរើស"</string>
+ <string name="select" msgid="2704765470563027689">"ជ្រើសរើស"</string>
+ <string name="selected" msgid="9151797369975828124">"បានជ្រើសរើស"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{ជ្រើសរើសធាតុរហូតដល់ <xliff:g id="COUNT_0">^1</xliff:g>}other{ជ្រើសរើសធាតុរហូតដល់ <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="recent" msgid="6694613584743207874">"ថ្មីៗ"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"គ្មានរូបថត ឬវីដេអូទេ"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"គ្មានអាល់ប៊ុមទេ"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"មើលអ្វីដែលបានជ្រើសរើស"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"រូបថត"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"អាល់ប៊ុម"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"មើលសាកល្បង"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"ប្ដូរទៅកម្រងព័ត៌មានការងារ"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"ប្ដូរទៅកម្រងព័ត៌មានផ្ទាល់ខ្លួន"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"បានទប់ស្កាត់ដោយអ្នកគ្រប់គ្រងរបស់អ្នក"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"ការចូលប្រើទិន្នន័យការងារពីកម្មវិធីផ្ទាល់ខ្លួនមិនត្រូវបានអនុញ្ញាតទេ"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"ការចូលប្រើទិន្នន័យផ្ទាល់ខ្លួនពីកម្មវិធីការងារមិនត្រូវបានអនុញ្ញាតទេ"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"កម្មវិធីការងារត្រូវបានផ្អាក"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"ដើម្បីបើករូបថតការងារ សូមបើកកម្មវិធីការងាររបស់អ្នក រួចព្យាយាមម្ដងទៀត"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"កម្មវិធីនេះអាចចូលប្រើបានតែរូបថតដែលអ្នកជ្រើសរើសប៉ុណ្ណោះ"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{ធាតុ <xliff:g id="COUNT_0">^1</xliff:g>}other{ធាតុ <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"បញ្ចូល (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"កាមេរ៉ា"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"ការទាញយក"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"សំណព្វ"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"រូបថតអេក្រង់"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"រូបថតមានចលនា"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> ដែលបានថតនៅថ្ងៃទី <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"វីដេអូបានថតនៅថ្ងៃទី <xliff:g id="TIME">%1$s</xliff:g> ដែលមានរយៈពេល <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"រូបថត"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"រូបថតមានចលនា"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"បិទសំឡេងវីដេអូ"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"បើកសំឡេងវីដេអូ"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"ចាក់វីដេអូ"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"ផ្អាកវីដេអូ"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"ឥឡូវនេះមានមេឌៀក្នុងប្រព័ន្ធពពកពី <xliff:g id="PKG_NAME">%1$s</xliff:g> ហើយ"</string>
+ <string name="not_selected" msgid="2244008151669896758">"មិនបានជ្រើសរើសទេ"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> កែឯកសារសំឡេងនេះឬ?}other{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> កែឯកសារសំឡេង <xliff:g id="COUNT">^2</xliff:g> ឬ?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{កំពុងកែឯកសារសំឡេង…}other{កំពុងកែឯកសារសំឡេង <xliff:g id="COUNT">^1</xliff:g>…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> កែវីដេអូនេះឬ?}other{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> កែវីដេអូ <xliff:g id="COUNT">^2</xliff:g> ឬ?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{កំពុងកែវីដេអូ…}other{កំពុងកែវីដេអូ <xliff:g id="COUNT">^1</xliff:g>…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> កែរូបថតនេះឬ?}other{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> កែរូបថត <xliff:g id="COUNT">^2</xliff:g> សន្លឹកឬ?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{កំពុងកែរូបថត…}other{កំពុងកែរូបថត <xliff:g id="COUNT">^1</xliff:g> សន្លឹក…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> កែធាតុនេះឬ?}other{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> កែធាតុ <xliff:g id="COUNT">^2</xliff:g> ឬ?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{កំពុងកែធាតុ…}other{កំពុងកែធាតុ <xliff:g id="COUNT">^1</xliff:g>…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> ផ្លាស់ទីឯកសារសំឡេងនេះទៅធុងសំរាមឬ?}other{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> ផ្លាស់ទីឯកសារសំឡេង <xliff:g id="COUNT">^2</xliff:g> ទៅធុងសំរាមឬ?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{កំពុងផ្លាស់ទីឯកសារសំឡេងទៅធុងសំរាម…}other{កំពុងផ្លាស់ទីឯកសារសំឡេង <xliff:g id="COUNT">^1</xliff:g> ទៅធុងសំរាម…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> ផ្លាស់ទីវីដេអូនេះទៅធុងសំរាមឬ?}other{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> ផ្លាស់ទីវីដេអូ <xliff:g id="COUNT">^2</xliff:g> ទៅធុងសំរាមឬ?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{កំពុងផ្លាស់ទីវីដេអូទៅធុងសំរាម…}other{កំពុងផ្លាស់ទីវីដេអូ <xliff:g id="COUNT">^1</xliff:g> ទៅធុងសំរាម…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> ផ្លាស់ទីរូបថតនេះទៅធុងសំរាមឬ?}other{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> ផ្លាស់ទីរូបថត <xliff:g id="COUNT">^2</xliff:g> សន្លឹកទៅធុងសំរាមឬ?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{កំពុងផ្លាស់ទីរូបថតទៅធុងសំរាម…}other{កំពុងផ្លាស់ទីរូបថត <xliff:g id="COUNT">^1</xliff:g> សន្លឹកទៅធុងសំរាម…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> ផ្លាស់ទីធាតុនេះទៅធុងសំរាមឬ?}other{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> ផ្លាស់ទីធាតុ <xliff:g id="COUNT">^2</xliff:g> ទៅធុងសំរាមឬ?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{កំពុងផ្លាស់ទីធាតុទៅធុងសំរាម…}other{កំពុងផ្លាស់ទីធាតុ <xliff:g id="COUNT">^1</xliff:g> ទៅធុងសំរាម…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> ផ្លាស់ទីឯកសារសំឡេងនេះចេញពីធុងសំរាមឬ?}other{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> ផ្លាស់ទីឯកសារសំឡេង <xliff:g id="COUNT">^2</xliff:g> ចេញពីធុងសំរាមឬ?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{កំពុងផ្លាស់ទីឯកសារសំឡេងចេញពីធុងសំរាម…}other{កំពុងផ្លាស់ទីឯកសារសំឡេង <xliff:g id="COUNT">^1</xliff:g> ចេញពីធុងសំរាម…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> ផ្លាស់ទីវីដេអូនេះចេញពីធុងសំរាមឬ?}other{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> ផ្លាស់ទីវីដេអូ <xliff:g id="COUNT">^2</xliff:g> ចេញពីធុងសំរាមឬ?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{កំពុងផ្លាស់ទីវីដេអូចេញពីធុងសំរាម…}other{កំពុងផ្លាស់ទីវីដេអូ <xliff:g id="COUNT">^1</xliff:g> ចេញពីធុងសំរាម…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> ផ្លាស់ទីរូបថតនេះចេញពីធុងសំរាមឬ?}other{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> ផ្លាស់ទីរូបថត <xliff:g id="COUNT">^2</xliff:g> សន្លឹកចេញពីធុងសំរាមឬ?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{កំពុងផ្លាស់ទីរូបថតចេញពីធុងសំរាម…}other{កំពុងផ្លាស់ទីរូបថត <xliff:g id="COUNT">^1</xliff:g> សន្លឹកចេញពីធុងសំរាម…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> ផ្លាស់ទីធាតុនេះចេញពីធុងសំរាមឬ?}other{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> ផ្លាស់ទីធាតុ <xliff:g id="COUNT">^2</xliff:g> ចេញពីធុងសំរាមឬ?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{កំពុងផ្លាស់ទីធាតុចេញពីធុងសំរាម…}other{កំពុងផ្លាស់ទីធាតុ <xliff:g id="COUNT">^1</xliff:g> ចេញពីធុងសំរាម…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> លុបឯកសារសំឡេងនេះឬ?}other{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> លុបឯកសារសំឡេង <xliff:g id="COUNT">^2</xliff:g> ឬ?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{កំពុងលុបឯកសារសំឡេង…}other{កំពុងលុបឯកសារសំឡេង <xliff:g id="COUNT">^1</xliff:g>…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> លុបវីដេអូនេះឬ?}other{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> លុបវីដេអូ <xliff:g id="COUNT">^2</xliff:g> ឬ?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{កំពុងលុបវីដេអូ…}other{កំពុងលុបវីដេអូ <xliff:g id="COUNT">^1</xliff:g>…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> លុបរូបថតនេះឬ?}other{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> លុបរូបថត <xliff:g id="COUNT">^2</xliff:g> សន្លឹកឬ?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{កំពុងលុបរូបថត…}other{កំពុងលុបរូបថត <xliff:g id="COUNT">^1</xliff:g> សន្លឹក…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_0">^1</xliff:g> លុបធាតុនេះឬ?}other{អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> លុបធាតុ <xliff:g id="COUNT">^2</xliff:g> ឬ?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{កំពុងលុបធាតុ…}other{កំពុងលុបធាតុ <xliff:g id="COUNT">^1</xliff:g>…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> មិនអាចដំណើរការឯកសារមេឌៀបានទេ"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"បានបោះបង់ការដំណើរការមេឌៀ"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"មានបញ្ហាក្នុងការដំណើរការមេឌៀ"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 0d47e57..5d23792 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"ಮಾಧ್ಯಮ"</string>
<string name="storage_description" msgid="4081716890357580107">"ಸ್ಥಳೀಯ ಸಂಗ್ರಹಣೆ"</string>
<string name="app_label" msgid="9035307001052716210">"ಮಾಧ್ಯಮ ಸಂಗ್ರಹಣೆ"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"ಫೋಟೋ ಪಿಕರ್"</string>
<string name="artist_label" msgid="8105600993099120273">"ಕಲಾವಿದರು"</string>
<string name="unknown" msgid="2059049215682829375">"ಅಪರಿಚಿತ"</string>
<string name="root_images" msgid="5861633549189045666">"ಚಿತ್ರಗಳು"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"ಮುಂದುವರಿಸಿ"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"ಅನುಮತಿಸಿ"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"ನಿರಾಕರಿಸಿ"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">ಜೊತೆಗೆ <xliff:g id="COUNT_1">^1</xliff:g> ಹೆಚ್ಚುವರಿ ಐಟಂಗಳು</item>
- <item quantity="other">ಜೊತೆಗೆ <xliff:g id="COUNT_1">^1</xliff:g> ಹೆಚ್ಚುವರಿ ಐಟಂಗಳು</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}one{+<xliff:g id="COUNT_1">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{ಜೊತೆಗೆ <xliff:g id="COUNT_0">^1</xliff:g> ಹೆಚ್ಚುವರಿ ಐಟಂ}one{ಜೊತೆಗೆ <xliff:g id="COUNT_1">^1</xliff:g> ಹೆಚ್ಚುವರಿ ಐಟಂಗಳು}other{ಜೊತೆಗೆ <xliff:g id="COUNT_1">^1</xliff:g> ಹೆಚ್ಚುವರಿ ಐಟಂಗಳು}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"ತಾತ್ಕಾಲಿಕ ಆ್ಯಪ್ ಫೈಲ್ಗಳನ್ನು ತೆರವುಗೊಳಿಸಿ"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>, ಕೆಲವು ತಾತ್ಕಾಲಿಕ ಫೈಲ್ಗಳನ್ನು ತೆರವುಗೊಳಿಸಲು ಬಯಸುತ್ತಿದೆ. ಬ್ಯಾಟರಿ ಅಥವಾ ಸೆಲ್ಯುಲರ್ ಡೇಟಾ ಹೆಚ್ಚು ಬಳಕೆಯಾಗಲು ಇದು ಕಾರಣವಾಗಬಹುದು."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"ತಾತ್ಕಾಲಿಕ ಆ್ಯಪ್ ಫೈಲ್ಗಳನ್ನು ತೆರವುಗೊಳಿಸಲಾಗುತ್ತಿದೆ…"</string>
<string name="clear" msgid="5524638938415865915">"ತೆರವುಗೊಳಿಸಿ"</string>
<string name="allow" msgid="8885707816848569619">"ಅನುಮತಿಸಿ"</string>
<string name="deny" msgid="6040983710442068936">"ನಿರಾಕರಿಸಿ"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">ಈ <xliff:g id="COUNT">^2</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- <item quantity="other">ಈ <xliff:g id="COUNT">^2</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಮಾರ್ಪಡಿಸಲಾಗುತ್ತಿದೆ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಮಾರ್ಪಡಿಸಲಾಗುತ್ತಿದೆ…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one">ಈ <xliff:g id="COUNT">^2</xliff:g> ವೀಡಿಯೊಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- <item quantity="other">ಈ <xliff:g id="COUNT">^2</xliff:g> ವೀಡಿಯೊಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ವೀಡಿಯೋಗಳನ್ನು ಮಾರ್ಪಡಿಸಲಾಗುತ್ತಿದೆ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ವೀಡಿಯೋಗಳನ್ನು ಮಾರ್ಪಡಿಸಲಾಗುತ್ತಿದೆ…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one">ಈ <xliff:g id="COUNT">^2</xliff:g> ಫೋಟೋಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- <item quantity="other">ಈ <xliff:g id="COUNT">^2</xliff:g> ಫೋಟೋಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ಫೋಟೋಗಳನ್ನು ಮಾರ್ಪಡಿಸಲಾಗುತ್ತಿದೆ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ಫೋಟೋಗಳನ್ನು ಮಾರ್ಪಡಿಸಲಾಗುತ್ತಿದೆ…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one">ಈ <xliff:g id="COUNT">^2</xliff:g> ಐಟಂಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- <item quantity="other">ಈ <xliff:g id="COUNT">^2</xliff:g> ಐಟಂಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ಐಟಂಗಳನ್ನು ಮಾರ್ಪಡಿಸಲಾಗುತ್ತಿದೆ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ಐಟಂಗಳನ್ನು ಮಾರ್ಪಡಿಸಲಾಗುತ್ತಿದೆ…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one">ಈ <xliff:g id="COUNT">^2</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- <item quantity="other">ಈ <xliff:g id="COUNT">^2</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one">ಈ <xliff:g id="COUNT">^2</xliff:g> ವೀಡಿಯೊಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- <item quantity="other">ಈ <xliff:g id="COUNT">^2</xliff:g> ವೀಡಿಯೊಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ವೀಡಿಯೋವನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ವೀಡಿಯೋವನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one">ಈ <xliff:g id="COUNT">^2</xliff:g> ಫೋಟೋಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- <item quantity="other">ಈ <xliff:g id="COUNT">^2</xliff:g> ಫೋಟೋಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ಫೋಟೋಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ಫೋಟೋಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one">ಈ <xliff:g id="COUNT">^2</xliff:g> ಐಟಂಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- <item quantity="other">ಈ <xliff:g id="COUNT">^2</xliff:g> ಐಟಂಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ಐಟಂಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ಐಟಂಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one">ಈ <xliff:g id="COUNT">^2</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- <item quantity="other">ಈ <xliff:g id="COUNT">^2</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one">ಈ <xliff:g id="COUNT">^2</xliff:g> ವೀಡಿಯೊಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- <item quantity="other">ಈ <xliff:g id="COUNT">^2</xliff:g> ವೀಡಿಯೊಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ವೀಡಿಯೋಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ವೀಡಿಯೋಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one">ಈ <xliff:g id="COUNT">^2</xliff:g> ಫೋಟೋಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- <item quantity="other">ಈ <xliff:g id="COUNT">^2</xliff:g> ಫೋಟೋಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ಫೋಟೋಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ಫೋಟೋಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one">ಈ <xliff:g id="COUNT">^2</xliff:g> ಐಟಂಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- <item quantity="other">ಈ <xliff:g id="COUNT">^2</xliff:g> ಐಟಂಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ಐಟಂಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ಐಟಂಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one">ಈ <xliff:g id="COUNT">^2</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಅಳಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- <item quantity="other">ಈ <xliff:g id="COUNT">^2</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಅಳಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one">ಈ <xliff:g id="COUNT">^2</xliff:g> ವೀಡಿಯೊಗಳನ್ನು ಅಳಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- <item quantity="other">ಈ <xliff:g id="COUNT">^2</xliff:g> ವೀಡಿಯೊಗಳನ್ನು ಅಳಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ವೀಡಿಯೊಗಳನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ವೀಡಿಯೊಗಳನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one">ಈ <xliff:g id="COUNT">^2</xliff:g> ಫೋಟೋಗಳನ್ನು ಅಳಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- <item quantity="other">ಈ <xliff:g id="COUNT">^2</xliff:g> ಫೋಟೋಗಳನ್ನು ಅಳಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ಫೋಟೋಗಳನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ಫೋಟೋಗಳನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one">ಈ <xliff:g id="COUNT">^2</xliff:g> ಐಟಂಗಳನ್ನು ಅಳಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- <item quantity="other">ಈ <xliff:g id="COUNT">^2</xliff:g> ಐಟಂಗಳನ್ನು ಅಳಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ಐಟಂಗಳನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ಐಟಂಗಳನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"ಸೇರಿಸಿ"</string>
+ <string name="deselect" msgid="4297825044827769490">"ಆಯ್ಕೆ ರದ್ದುಮಾಡಿ"</string>
+ <string name="deselected" msgid="8488133193326208475">"ಆಯ್ಕೆ ರದ್ದುಮಾಡಲಾಗಿದೆ"</string>
+ <string name="select" msgid="2704765470563027689">"ಆಯ್ಕೆಮಾಡಿ"</string>
+ <string name="selected" msgid="9151797369975828124">"ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> ಐಟಂವರೆಗೆ ಆಯ್ಕೆಮಾಡಿ}one{<xliff:g id="COUNT_1">^1</xliff:g> ಐಟಂಗಳವರೆಗೆ ಆಯ್ಕೆಮಾಡಿ}other{<xliff:g id="COUNT_1">^1</xliff:g> ಐಟಂಗಳವರೆಗೆ ಆಯ್ಕೆಮಾಡಿ}}"</string>
+ <string name="recent" msgid="6694613584743207874">"ಇತ್ತೀಚಿನದು"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"ಯಾವುದೇ ಫೋಟೋಗಳು ಅಥವಾ ವೀಡಿಯೊಗಳಿಲ್ಲ"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"ಯಾವುದೇ ಆಲ್ಬಮ್ಗಳಿಲ್ಲ"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"ಆಯ್ಕೆಮಾಡಿರುವುದನ್ನು ವೀಕ್ಷಿಸಿ"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"ಫೋಟೋಗಳು"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"ಆಲ್ಬಮ್ಗಳು"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"ಪೂರ್ವವೀಕ್ಷಣೆ"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"ಕೆಲಸಕ್ಕೆ ಬದಲಿಸಿ"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"ವೈಯಕ್ತಿಕಕ್ಕೆ ಬದಲಿಸಿ"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ನಿರ್ಬಂಧಿಸಿದ್ದಾರೆ"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"ವೈಯಕ್ತಿಕ ಆ್ಯಪ್ ಮೂಲಕ ಅಧಿಕೃತ ಡೇಟಾವನ್ನು ಪ್ರವೇಶಿಸಲು ಅನುಮತಿಸಲಾಗುವುದಿಲ್ಲ"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"ಕೆಲಸಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯಪ್ ಮೂಲಕ ಅಧಿಕೃತ ಡೇಟಾವನ್ನು ಪ್ರವೇಶಿಸಲು ಅನುಮತಿಸಲಾಗುವುದಿಲ್ಲ"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"ಕೆಲಸಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯಪ್ಗಳನ್ನು ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"ಕೆಲಸದ ಫೋಟೋಗಳನ್ನು ತೆರೆಯಲು, ನಿಮ್ಮ ಕೆಲಸಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯಪ್ಗಳನ್ನು ಆನ್ ಮಾಡಿ ನಂತರ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"ಈ ಆ್ಯಪ್ ನೀವು ಆಯ್ಕೆಮಾಡಿದ ಫೋಟೋಗಳನ್ನು ಮಾತ್ರ ಪ್ರವೇಶಿಸಬಹುದು"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g>ಐಟಂ}one{<xliff:g id="COUNT_1">^1</xliff:g> ಐಟಂಗಳು}other{<xliff:g id="COUNT_1">^1</xliff:g> ಐಟಂಗಳು}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"ಸೇರಿಸಿ (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"ಕ್ಯಾಮರಾ"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"ಡೌನ್ಲೋಡ್ಗಳು"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"ಮೆಚ್ಚಿನವುಗಳು"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"ಸ್ಕ್ರೀನ್ಶಾಟ್ಗಳು"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"ಚಲನೆಯ ಫೋಟೋ"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> ಅನ್ನು <xliff:g id="TIME">%2$s</xliff:g> ರಂದು ತೆಗೆದುಕೊಳ್ಳಲಾಗಿದೆ"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"<xliff:g id="DURATION">%2$s</xliff:g> ಅವಧಿಯ ವೀಡಿಯೊವನ್ನು <xliff:g id="TIME">%1$s</xliff:g> ಸಮಯದಲ್ಲಿ ಸೆರೆಹಿಡಿಯಲಾಗಿದೆ"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"ಫೋಟೋ"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"ಚಲನೆಯ ಫೋಟೋ"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"ವೀಡಿಯೊ ಮ್ಯೂಟ್ ಮಾಡಿ"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"ವೀಡಿಯೊ ಅನ್ಮ್ಯೂಟ್ ಮಾಡಿ"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"ವೀಡಿಯೊವನ್ನು ಪ್ಲೇ ಮಾಡಿ"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"ವೀಡಿಯೊವನ್ನು ವಿರಾಮಗೊಳಿಸಿ"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"ಕ್ಲೌಡ್ ಮಾಧ್ಯಮವು ಈಗ <xliff:g id="PKG_NAME">%1$s</xliff:g> ನಿಂದ ಲಭ್ಯವಿದೆ"</string>
+ <string name="not_selected" msgid="2244008151669896758">"ಆಯ್ಕೆಮಾಡಲಾಗಿಲ್ಲ"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{ಈ ಆಡಿಯೋ ಫೈಲ್ ಅನ್ನು ಮಾರ್ಪಡಿಸಲು <xliff:g id="APP_NAME_0">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}one{ಈ <xliff:g id="COUNT">^2</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}other{ಈ <xliff:g id="COUNT">^2</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{ಆಡಿಯೋ ಫೈಲ್ ಅನ್ನು ಮಾರ್ಪಡಿಸಲಾಗುತ್ತಿದೆ…}one{<xliff:g id="COUNT">^1</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಮಾರ್ಪಡಿಸಲಾಗುತ್ತಿದೆ…}other{<xliff:g id="COUNT">^1</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಮಾರ್ಪಡಿಸಲಾಗುತ್ತಿದೆ…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{ಈ ವೀಡಿಯೊವನ್ನು ಮಾರ್ಪಡಿಸಲು <xliff:g id="APP_NAME_0">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}one{ಈ <xliff:g id="COUNT">^2</xliff:g> ವೀಡಿಯೊಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}other{ಈ <xliff:g id="COUNT">^2</xliff:g> ವೀಡಿಯೊಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{ವೀಡಿಯೋವನ್ನು ಮಾರ್ಪಡಿಸಲಾಗುತ್ತಿದೆ…}one{<xliff:g id="COUNT">^1</xliff:g> ವೀಡಿಯೋಗಳನ್ನು ಮಾರ್ಪಡಿಸಲಾಗುತ್ತಿದೆ…}other{<xliff:g id="COUNT">^1</xliff:g> ವೀಡಿಯೋಗಳನ್ನು ಮಾರ್ಪಡಿಸಲಾಗುತ್ತಿದೆ…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{ಈ ಫೋಟೋವನ್ನು ಮಾರ್ಪಡಿಸಲು <xliff:g id="APP_NAME_0">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}one{ಈ <xliff:g id="COUNT">^2</xliff:g> ಫೋಟೋಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}other{ಈ <xliff:g id="COUNT">^2</xliff:g> ಫೋಟೋಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{ಫೋಟೋವನ್ನು ಮಾರ್ಪಡಿಸಲಾಗುತ್ತಿದೆ…}one{<xliff:g id="COUNT">^1</xliff:g> ಫೋಟೋಗಳನ್ನು ಮಾರ್ಪಡಿಸಲಾಗುತ್ತಿದೆ…}other{<xliff:g id="COUNT">^1</xliff:g> ಫೋಟೋಗಳನ್ನು ಮಾರ್ಪಡಿಸಲಾಗುತ್ತಿದೆ…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{ಈ ಐಟಂ ಅನ್ನು ಮಾರ್ಪಡಿಸಲು <xliff:g id="APP_NAME_0">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}one{ಈ <xliff:g id="COUNT">^2</xliff:g> ಐಟಂಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}other{ಈ <xliff:g id="COUNT">^2</xliff:g> ಐಟಂಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{ಐಟಂ ಅನ್ನು ಮಾರ್ಪಡಿಸಲಾಗುತ್ತಿದೆ…}one{<xliff:g id="COUNT">^1</xliff:g> ಐಟಂಗಳನ್ನು ಮಾರ್ಪಡಿಸಲಾಗುತ್ತಿದೆ…}other{<xliff:g id="COUNT">^1</xliff:g> ಐಟಂಗಳನ್ನು ಮಾರ್ಪಡಿಸಲಾಗುತ್ತಿದೆ…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{ಈ ಆಡಿಯೋ ಫೈಲ್ ಅನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲು <xliff:g id="APP_NAME_0">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}one{ಈ <xliff:g id="COUNT">^2</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}other{ಈ <xliff:g id="COUNT">^2</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{ಆಡಿಯೋ ಫೈಲ್ ಅನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…}one{<xliff:g id="COUNT">^1</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…}other{<xliff:g id="COUNT">^1</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{ಈ ವೀಡಿಯೊವನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲು <xliff:g id="APP_NAME_0">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}one{ಈ <xliff:g id="COUNT">^2</xliff:g> ವೀಡಿಯೊಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}other{ಈ <xliff:g id="COUNT">^2</xliff:g> ವೀಡಿಯೊಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{ವೀಡಿಯೋವನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…}one{<xliff:g id="COUNT">^1</xliff:g> ವೀಡಿಯೋಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…}other{<xliff:g id="COUNT">^1</xliff:g> ವೀಡಿಯೋಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{ಈ ಫೋಟೋವನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲು <xliff:g id="APP_NAME_0">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}one{ಈ <xliff:g id="COUNT">^2</xliff:g> ಫೋಟೋಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}other{ಈ <xliff:g id="COUNT">^2</xliff:g> ಫೋಟೋಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{ಫೋಟೋವನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…}one{<xliff:g id="COUNT">^1</xliff:g> ಫೋಟೋಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…}other{<xliff:g id="COUNT">^1</xliff:g> ಫೋಟೋಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{ಈ ಐಟಂ ಅನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲು <xliff:g id="APP_NAME_0">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}one{ಈ <xliff:g id="COUNT">^2</xliff:g> ಐಟಂಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}other{ಈ <xliff:g id="COUNT">^2</xliff:g> ಐಟಂಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{ಐಟಂ ಅನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…}one{<xliff:g id="COUNT">^1</xliff:g> ಐಟಂಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…}other{<xliff:g id="COUNT">^1</xliff:g> ಐಟಂಗಳನ್ನು ಅನುಪಯುಕ್ತಕ್ಕೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{ಈ ಆಡಿಯೋ ಫೈಲ್ ಅನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲು <xliff:g id="APP_NAME_0">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}one{ಈ <xliff:g id="COUNT">^2</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}other{ಈ <xliff:g id="COUNT">^2</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{ಆಡಿಯೋ ಫೈಲ್ ಅನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…}one{<xliff:g id="COUNT">^1</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…}other{<xliff:g id="COUNT">^1</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{ಈ ವೀಡಿಯೊವನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲು <xliff:g id="APP_NAME_0">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}one{ಈ <xliff:g id="COUNT">^2</xliff:g> ವೀಡಿಯೊಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}other{ಈ <xliff:g id="COUNT">^2</xliff:g> ವೀಡಿಯೊಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{ವೀಡಿಯೋವನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…}one{<xliff:g id="COUNT">^1</xliff:g> ವೀಡಿಯೋಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…}other{<xliff:g id="COUNT">^1</xliff:g> ವೀಡಿಯೋಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{ಈ ಫೋಟೋವನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲು <xliff:g id="APP_NAME_0">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}one{ಈ <xliff:g id="COUNT">^2</xliff:g> ಫೋಟೋಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}other{ಈ <xliff:g id="COUNT">^2</xliff:g> ಫೋಟೋಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{ಫೋಟೋವನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…}one{<xliff:g id="COUNT">^1</xliff:g> ಫೋಟೋಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…}other{<xliff:g id="COUNT">^1</xliff:g> ಫೋಟೋಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{ಈ ಐಟಂ ಅನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲು <xliff:g id="APP_NAME_0">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}one{ಈ <xliff:g id="COUNT">^2</xliff:g> ಐಟಂಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}other{ಈ <xliff:g id="COUNT">^2</xliff:g> ಐಟಂಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{ಐಟಂ ಅನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…}one{<xliff:g id="COUNT">^1</xliff:g> ಐಟಂಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…}other{<xliff:g id="COUNT">^1</xliff:g> ಐಟಂಗಳನ್ನು ಅನುಪಯುಕ್ತದಿಂದ ಹೊರಗೆ ಸರಿಸಲಾಗುತ್ತಿದೆ…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{ಈ ಆಡಿಯೋ ಫೈಲ್ ಅನ್ನು ಅಳಿಸಲು <xliff:g id="APP_NAME_0">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}one{ಈ <xliff:g id="COUNT">^2</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಅಳಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}other{ಈ <xliff:g id="COUNT">^2</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಅಳಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{ಆಡಿಯೋ ಫೈಲ್ ಅನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ…}one{<xliff:g id="COUNT">^1</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ…}other{<xliff:g id="COUNT">^1</xliff:g> ಆಡಿಯೋ ಫೈಲ್ಗಳನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{ಈ ವೀಡಿಯೊವನ್ನು ಅಳಿಸಲು <xliff:g id="APP_NAME_0">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}one{ಈ <xliff:g id="COUNT">^2</xliff:g> ವೀಡಿಯೊಗಳನ್ನು ಅಳಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}other{ಈ <xliff:g id="COUNT">^2</xliff:g> ವೀಡಿಯೊಗಳನ್ನು ಅಳಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{ವೀಡಿಯೊವನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ…}one{<xliff:g id="COUNT">^1</xliff:g> ವೀಡಿಯೊಗಳನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ…}other{<xliff:g id="COUNT">^1</xliff:g> ವೀಡಿಯೊಗಳನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{ಈ ಫೋಟೋವನ್ನು ಅಳಿಸಲು <xliff:g id="APP_NAME_0">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}one{ಈ <xliff:g id="COUNT">^2</xliff:g> ಫೋಟೋಗಳನ್ನು ಅಳಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}other{ಈ <xliff:g id="COUNT">^2</xliff:g> ಫೋಟೋಗಳನ್ನು ಅಳಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{ಫೋಟೋವನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ…}one{<xliff:g id="COUNT">^1</xliff:g> ಫೋಟೋಗಳನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ…}other{<xliff:g id="COUNT">^1</xliff:g> ಫೋಟೋಗಳನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{ಈ ಐಟಂ ಅನ್ನು ಅಳಿಸಲು <xliff:g id="APP_NAME_0">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}one{ಈ <xliff:g id="COUNT">^2</xliff:g> ಐಟಂಗಳನ್ನು ಅಳಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}other{ಈ <xliff:g id="COUNT">^2</xliff:g> ಐಟಂಗಳನ್ನು ಅಳಿಸಲು <xliff:g id="APP_NAME_1">^1</xliff:g> ಗೆ ಅನುಮತಿ ನೀಡಬೇಕೇ?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{ಐಟಂ ಅನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ…}one{<xliff:g id="COUNT">^1</xliff:g> ಐಟಂಗಳನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ…}other{<xliff:g id="COUNT">^1</xliff:g> ಐಟಂಗಳನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> ಗೆ ಮಾಧ್ಯಮ ಫೈಲ್ಗಳನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಧ್ಯವಾಗದು"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"ಮಾಧ್ಯಮ ಪ್ರಕ್ರಿಯೆಗೊಳಿಸುವಿಕೆ ರದ್ದುಗೊಂಡಿದೆ"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"ಮಾಧ್ಯಮ ಪ್ರಕ್ರಿಯೆಗೊಳಿಸುವಿಕೆ ದೋಷ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index db82767..c2bc09f 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"미디어"</string>
<string name="storage_description" msgid="4081716890357580107">"로컬 저장소"</string>
<string name="app_label" msgid="9035307001052716210">"미디어 저장소"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"사진 선택 도구"</string>
<string name="artist_label" msgid="8105600993099120273">"아티스트"</string>
<string name="unknown" msgid="2059049215682829375">"알 수 없음"</string>
<string name="root_images" msgid="5861633549189045666">"이미지"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"계속"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"허용"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"거부"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">그 외 <xliff:g id="COUNT_1">^1</xliff:g>개</item>
- <item quantity="one">그 외 <xliff:g id="COUNT_0">^1</xliff:g>개</item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">그 외 항목 <xliff:g id="COUNT_1">^1</xliff:g>개</item>
- <item quantity="one">그 외 항목 <xliff:g id="COUNT_0">^1</xliff:g>개</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{그 외 <xliff:g id="COUNT_0">^1</xliff:g>개}other{그 외 <xliff:g id="COUNT_1">^1</xliff:g>개}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{그 외 항목 <xliff:g id="COUNT_0">^1</xliff:g>개}other{그 외 항목 <xliff:g id="COUNT_1">^1</xliff:g>개}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"임시 앱 파일 삭제"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>에서 일부 임시 파일을 삭제하려고 합니다. 이로 인해 배터리 또는 모바일 데이터 사용량이 늘어날 수 있습니다."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"임시 앱 파일 삭제 중…"</string>
<string name="clear" msgid="5524638938415865915">"삭제"</string>
<string name="allow" msgid="8885707816848569619">"허용"</string>
<string name="deny" msgid="6040983710442068936">"거부"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>에서 오디오 파일 <xliff:g id="COUNT">^2</xliff:g>개를 수정하도록 허용하시겠습니까?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 오디오 파일을 수정하도록 허용하시겠습니까?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">오디오 파일 <xliff:g id="COUNT">^1</xliff:g>개 수정 중…</item>
- <item quantity="one">오디오 파일 수정 중…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>에서 동영상 <xliff:g id="COUNT">^2</xliff:g>개를 수정하도록 허용하시겠습니까?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 동영상을 수정하도록 허용하시겠습니까?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">동영상 <xliff:g id="COUNT">^1</xliff:g>개 수정 중…</item>
- <item quantity="one">동영상 수정 중…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>에서 사진 <xliff:g id="COUNT">^2</xliff:g>개를 수정하도록 허용하시겠습니까?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 사진을 수정하도록 허용하시겠습니까?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">사진 <xliff:g id="COUNT">^1</xliff:g>개 수정 중…</item>
- <item quantity="one">사진 수정 중…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>에서 항목 <xliff:g id="COUNT">^2</xliff:g>개를 수정하도록 허용하시겠습니까?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 항목을 수정하도록 허용하시겠습니까?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">항목 <xliff:g id="COUNT">^1</xliff:g>개 수정 중…</item>
- <item quantity="one">항목 수정 중…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>에서 오디오 파일 <xliff:g id="COUNT">^2</xliff:g>개를 휴지통으로 이동하도록 허용하시겠습니까?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 오디오 파일을 휴지통으로 이동하도록 허용하시겠습니까?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">오디오 파일 <xliff:g id="COUNT">^1</xliff:g>개를 휴지통으로 이동하는 중…</item>
- <item quantity="one">오디오 파일을 휴지통으로 이동하는 중…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>에서 동영상 <xliff:g id="COUNT">^2</xliff:g>개를 휴지통으로 이동하도록 허용하시겠습니까?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 동영상을 휴지통으로 이동하도록 허용하시겠습니까?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">동영상 <xliff:g id="COUNT">^1</xliff:g>개를 휴지통으로 이동하는 중…</item>
- <item quantity="one">동영상을 휴지통으로 이동하는 중…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>에서 사진 <xliff:g id="COUNT">^2</xliff:g>개를 휴지통으로 이동하도록 허용하시겠습니까?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 사진을 휴지통으로 이동하도록 허용하시겠습니까?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">사진 <xliff:g id="COUNT">^1</xliff:g>개를 휴지통으로 이동하는 중…</item>
- <item quantity="one">사진을 휴지통으로 이동하는 중…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>에서 항목 <xliff:g id="COUNT">^2</xliff:g>개를 휴지통으로 이동하도록 허용하시겠습니까?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 항목을 휴지통으로 이동하도록 허용하시겠습니까?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">항목 <xliff:g id="COUNT">^1</xliff:g>개를 휴지통으로 이동하는 중…</item>
- <item quantity="one">항목을 휴지통으로 이동하는 중…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>에서 오디오 파일 <xliff:g id="COUNT">^2</xliff:g>개를 휴지통에서 꺼내도록 허용하시겠습니까?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 오디오 파일을 휴지통에서 꺼내도록 허용하시겠습니까?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">오디오 파일 <xliff:g id="COUNT">^1</xliff:g>개를 휴지통에서 꺼내는 중…</item>
- <item quantity="one">오디오 파일을 휴지통에서 꺼내는 중…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>에서 동영상 <xliff:g id="COUNT">^2</xliff:g>개를 휴지통에서 꺼내도록 허용하시겠습니까?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 동영상을 휴지통에서 꺼내도록 허용하시겠습니까?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">동영상 <xliff:g id="COUNT">^1</xliff:g>개를 휴지통에서 꺼내는 중…</item>
- <item quantity="one">동영상을 휴지통에서 꺼내는 중…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>에서 사진 <xliff:g id="COUNT">^2</xliff:g>개를 휴지통에서 꺼내도록 허용하시겠습니까?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 사진을 휴지통에서 꺼내도록 허용하시겠습니까?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">사진 <xliff:g id="COUNT">^1</xliff:g>개를 휴지통에서 꺼내는 중…</item>
- <item quantity="one">사진을 휴지통에서 꺼내는 중…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>에서 항목 <xliff:g id="COUNT">^2</xliff:g>개를 휴지통에서 꺼내도록 허용하시겠습니까?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 항목을 휴지통에서 꺼내도록 허용하시겠습니까?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">항목 <xliff:g id="COUNT">^1</xliff:g>개를 휴지통에서 꺼내는 중…</item>
- <item quantity="one">항목을 휴지통에서 꺼내는 중…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>에서 오디오 파일 <xliff:g id="COUNT">^2</xliff:g>개를 삭제하도록 허용하시겠습니까?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 오디오 파일을 삭제하도록 허용하시겠습니까?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">오디오 파일 <xliff:g id="COUNT">^1</xliff:g>개 삭제 중…</item>
- <item quantity="one">오디오 파일 삭제 중…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>에서 동영상 <xliff:g id="COUNT">^2</xliff:g>개를 삭제하도록 허용하시겠습니까?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 동영상을 삭제하도록 허용하시겠습니까?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">동영상 <xliff:g id="COUNT">^1</xliff:g>개 삭제 중…</item>
- <item quantity="one">동영상 삭제 중…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>에서 사진 <xliff:g id="COUNT">^2</xliff:g>개를 삭제하도록 허용하시겠습니까?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 사진을 삭제하도록 허용하시겠습니까?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">사진 <xliff:g id="COUNT">^1</xliff:g>개 삭제 중…</item>
- <item quantity="one">사진 삭제 중…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>에서 항목 <xliff:g id="COUNT">^2</xliff:g>개를 삭제하도록 허용하시겠습니까?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 항목을 삭제하도록 허용하시겠습니까?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">항목 <xliff:g id="COUNT">^1</xliff:g>개 삭제 중…</item>
- <item quantity="one">항목 삭제 중…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"추가"</string>
+ <string name="deselect" msgid="4297825044827769490">"선택 해제"</string>
+ <string name="deselected" msgid="8488133193326208475">"선택 해제됨"</string>
+ <string name="select" msgid="2704765470563027689">"선택"</string>
+ <string name="selected" msgid="9151797369975828124">"선택됨"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{최대 <xliff:g id="COUNT_0">^1</xliff:g>개 항목을 선택하세요}other{최대 <xliff:g id="COUNT_1">^1</xliff:g>개 항목을 선택하세요}}"</string>
+ <string name="recent" msgid="6694613584743207874">"최근"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"사진 또는 동영상 없음"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"앨범 없음"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"선택 항목 보기"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"사진"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"앨범"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"미리보기"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"직장으로 전환"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"개인으로 전환"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"관리자가 차단함"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"개인 앱에서는 업무 데이터에 액세스할 수 없습니다."</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"직장 앱에서는 개인 데이터에 액세스할 수 없습니다."</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"직장 앱이 일시중지됨"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"직장 사진을 열려면 직장 앱을 사용 설정한 후 다시 시도하세요."</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"앱은 사용자가 선택한 사진에만 액세스할 수 있습니다."</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{항목 <xliff:g id="COUNT_0">^1</xliff:g>개}other{항목 <xliff:g id="COUNT_1">^1</xliff:g>개}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"추가(<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"카메라"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"다운로드"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"즐겨찾기"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"스크린샷"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"모션 사진"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="TIME">%2$s</xliff:g>에 촬영한 <xliff:g id="ITEM_NAME">%1$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"<xliff:g id="TIME">%1$s</xliff:g>에 <xliff:g id="DURATION">%2$s</xliff:g> 동안 촬영된 동영상"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"사진"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"모션 사진"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"동영상 음소거"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"동영상 음소거 해제"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"동영상 재생"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"동영상 일시중지"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"이제 <xliff:g id="PKG_NAME">%1$s</xliff:g>에서 클라우드 미디어를 사용할 수 있습니다."</string>
+ <string name="not_selected" msgid="2244008151669896758">"선택되지 않음"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 오디오 파일을 수정하도록 허용하시겠습니까?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>에서 오디오 파일 <xliff:g id="COUNT">^2</xliff:g>개를 수정하도록 허용하시겠습니까?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{오디오 파일 수정 중…}other{오디오 파일 <xliff:g id="COUNT">^1</xliff:g>개 수정 중…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 동영상을 수정하도록 허용하시겠습니까?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>에서 동영상 <xliff:g id="COUNT">^2</xliff:g>개를 수정하도록 허용하시겠습니까?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{동영상 수정 중…}other{동영상 <xliff:g id="COUNT">^1</xliff:g>개 수정 중…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 사진을 수정하도록 허용하시겠습니까?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>에서 사진 <xliff:g id="COUNT">^2</xliff:g>개를 수정하도록 허용하시겠습니까?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{사진 수정 중…}other{사진 <xliff:g id="COUNT">^1</xliff:g>개 수정 중…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 항목을 수정하도록 허용하시겠습니까?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>에서 항목 <xliff:g id="COUNT">^2</xliff:g>개를 수정하도록 허용하시겠습니까?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{항목 수정 중…}other{항목 <xliff:g id="COUNT">^1</xliff:g>개 수정 중…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 오디오 파일을 휴지통으로 이동하도록 허용하시겠습니까?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>에서 오디오 파일 <xliff:g id="COUNT">^2</xliff:g>개를 휴지통으로 이동하도록 허용하시겠습니까?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{오디오 파일을 휴지통으로 이동하는 중…}other{오디오 파일 <xliff:g id="COUNT">^1</xliff:g>개를 휴지통으로 이동하는 중…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 동영상을 휴지통으로 이동하도록 허용하시겠습니까?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>에서 동영상 <xliff:g id="COUNT">^2</xliff:g>개를 휴지통으로 이동하도록 허용하시겠습니까?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{동영상을 휴지통으로 이동하는 중…}other{동영상 <xliff:g id="COUNT">^1</xliff:g>개를 휴지통으로 이동하는 중…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 사진을 휴지통으로 이동하도록 허용하시겠습니까?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>에서 사진 <xliff:g id="COUNT">^2</xliff:g>개를 휴지통으로 이동하도록 허용하시겠습니까?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{사진을 휴지통으로 이동하는 중…}other{사진 <xliff:g id="COUNT">^1</xliff:g>개를 휴지통으로 이동하는 중…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 항목을 휴지통으로 이동하도록 허용하시겠습니까?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>에서 항목 <xliff:g id="COUNT">^2</xliff:g>개를 휴지통으로 이동하도록 허용하시겠습니까?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{항목을 휴지통으로 이동하는 중…}other{항목 <xliff:g id="COUNT">^1</xliff:g>개를 휴지통으로 이동하는 중…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 오디오 파일을 휴지통에서 꺼내도록 허용하시겠습니까?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>에서 오디오 파일 <xliff:g id="COUNT">^2</xliff:g>개를 휴지통에서 꺼내도록 허용하시겠습니까?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{오디오 파일을 휴지통에서 꺼내는 중…}other{오디오 파일 <xliff:g id="COUNT">^1</xliff:g>개를 휴지통에서 꺼내는 중…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 동영상을 휴지통에서 꺼내도록 허용하시겠습니까?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>에서 동영상 <xliff:g id="COUNT">^2</xliff:g>개를 휴지통에서 꺼내도록 허용하시겠습니까?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{동영상을 휴지통에서 꺼내는 중…}other{동영상 <xliff:g id="COUNT">^1</xliff:g>개를 휴지통에서 꺼내는 중…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 사진을 휴지통에서 꺼내도록 허용하시겠습니까?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>에서 사진 <xliff:g id="COUNT">^2</xliff:g>개를 휴지통에서 꺼내도록 허용하시겠습니까?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{사진을 휴지통에서 꺼내는 중…}other{사진 <xliff:g id="COUNT">^1</xliff:g>개를 휴지통에서 꺼내는 중…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 항목을 휴지통에서 꺼내도록 허용하시겠습니까?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>에서 항목 <xliff:g id="COUNT">^2</xliff:g>개를 휴지통에서 꺼내도록 허용하시겠습니까?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{항목을 휴지통에서 꺼내는 중…}other{항목 <xliff:g id="COUNT">^1</xliff:g>개를 휴지통에서 꺼내는 중…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 오디오 파일을 삭제하도록 허용하시겠습니까?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>에서 오디오 파일 <xliff:g id="COUNT">^2</xliff:g>개를 삭제하도록 허용하시겠습니까?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{오디오 파일 삭제 중…}other{오디오 파일 <xliff:g id="COUNT">^1</xliff:g>개 삭제 중…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 동영상을 삭제하도록 허용하시겠습니까?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>에서 동영상 <xliff:g id="COUNT">^2</xliff:g>개를 삭제하도록 허용하시겠습니까?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{동영상 삭제 중…}other{동영상 <xliff:g id="COUNT">^1</xliff:g>개 삭제 중…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 사진을 삭제하도록 허용하시겠습니까?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>에서 사진 <xliff:g id="COUNT">^2</xliff:g>개를 삭제하도록 허용하시겠습니까?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{사진 삭제 중…}other{사진 <xliff:g id="COUNT">^1</xliff:g>개 삭제 중…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>에서 이 항목을 삭제하도록 허용하시겠습니까?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>에서 항목 <xliff:g id="COUNT">^2</xliff:g>개를 삭제하도록 허용하시겠습니까?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{항목 삭제 중…}other{항목 <xliff:g id="COUNT">^1</xliff:g>개 삭제 중…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g>에서 미디어 파일을 처리할 수 없습니다."</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"미디어 처리 취소됨"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"미디어 처리 오류"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index bb44a4c..40237a1 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Мультимедия"</string>
<string name="storage_description" msgid="4081716890357580107">"Жергиликтүү сактагыч"</string>
<string name="app_label" msgid="9035307001052716210">"Медиа сактагыч"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"Сүрөт тандагыч"</string>
<string name="artist_label" msgid="8105600993099120273">"Аткаруучу"</string>
<string name="unknown" msgid="2059049215682829375">"Белгисиз"</string>
<string name="root_images" msgid="5861633549189045666">"Сүрөттөр"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Улантуу"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Ооба"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Тыюу салынат"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">дагы <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">дагы <xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Дагы <xliff:g id="COUNT_1">^1</xliff:g> нерсе</item>
- <item quantity="one">Дагы <xliff:g id="COUNT_0">^1</xliff:g> нерсе</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{дагы <xliff:g id="COUNT_0">^1</xliff:g>}other{дагы <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Дагы <xliff:g id="COUNT_0">^1</xliff:g> нерсе}other{Дагы <xliff:g id="COUNT_1">^1</xliff:g> нерсе}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Колдонмодогу убактылуу файлдарды өчүрүү"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> айрым убактылуу файлдарды өчүргөнү жатат. Батарея же мобилдик Интернет өтө көп колдонулушу мүмкүн."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Колдонмодогу убактылуу файлдар өчүрүлүүдө…"</string>
<string name="clear" msgid="5524638938415865915">"Тазалоо"</string>
<string name="allow" msgid="8885707816848569619">"Ооба"</string>
<string name="deny" msgid="6040983710442068936">"Жок"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> аудио файлды өзгөртсүнбү?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул аудио файлды өзгөртсүнбү?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> аудио файл өзгөртүлүүдө…</item>
- <item quantity="one">Аудио файл өзгөртүлүүдө…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> видеону өзгөртсүнбү?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул видеону өзгөртсүнбү?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> видео өзгөртүлүүдө…</item>
- <item quantity="one">Видео өзгөртүлүүдө…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> бул <xliff:g id="COUNT">^2</xliff:g> сүрөттү өзгөртсүнбү?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> бул сүрөттү өзгөртсүнбү?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> сүрөт өзгөртүлүүдө…</item>
- <item quantity="one">Сүрөт өзгөртүлүүдө…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> нерсени өзгөртсүнбү?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул нерсени өзгөртсүнбү?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> элемент өзгөртүлүүдө…</item>
- <item quantity="one">Элемент өзгөртүлүүдө…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> бул <xliff:g id="COUNT">^2</xliff:g> аудио файлды таштандыга ыргытсынбы?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> бул аудио файлды таштандыга ыргытсынбы?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> аудио файл таштандыга ыргытылууда…</item>
- <item quantity="one">Аудио файл таштандыга ыргытылууда…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> бул <xliff:g id="COUNT">^2</xliff:g> видеону таштандыга ыргытсынбы?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> бул видеону таштандыга ыргытсынбы?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> видео таштандыга ыргытылууда…</item>
- <item quantity="one">Видео таштандыга ыргытылууда…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> сүрөттү таштандыга ыргытсынбы?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> бул сүрөттү таштандыга ыргытсынбы?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> сүрөт таштандыга ыргытылууда…</item>
- <item quantity="one">Сүрөт таштандыга ыргытылууда…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> бул <xliff:g id="COUNT">^2</xliff:g> нерсени таштандыга ыргытсынбы?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> бул нерсени таштандыга ыргытсынбы?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> элемент таштандыга ыргытылууда…</item>
- <item quantity="one">Элемент таштандыга ыргытылууда…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> аудио файлды калыбына келтирсинби?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул аудио файлды калыбына келтирсинби?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> аудио файл калыбына келтирилүүдө…</item>
- <item quantity="one">Аудио файл калыбына келтирилүүдө…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> видеону калыбына келтирсинби?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул видеону калыбына келтирсинби?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> видео калыбына келтирилүүдө…</item>
- <item quantity="one">Видео калыбына келтирилүүдө…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> бул <xliff:g id="COUNT">^2</xliff:g> сүрөттү калыбына келтирсинби?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> бул сүрөттү калыбына келтирсинби?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> сүрөт калыбына келтирилүүдө…</item>
- <item quantity="one">Сүрөт калыбына келтирилүүдө…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> нерсени калыбына келтирсинби?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул нерсени калыбына келтирсинби?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> элемент калыбына келтирилүүдө…</item>
- <item quantity="one">Элемент калыбына келтирилүүдө…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> аудио файлды өчүрсүнбү?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул аудио файлды өчүрсүнбү?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> аудио файл өчүрүлүүдө…</item>
- <item quantity="one">Аудио файл өчүрүлүүдө…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> видеону өчүрсүнбү?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул видеону өчүрсүнбү?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> видео жок кылынууда…</item>
- <item quantity="one">Видео жок кылынууда…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> бул <xliff:g id="COUNT">^2</xliff:g> сүрөттү өчүрсүнбү?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> бул сүрөттү өчүрсүнбү?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> сүрөт жок кылынууда…</item>
- <item quantity="one">Сүрөт жок кылынууда…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> нерсени өчүрсүнбү?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул нерсени өчүрсүнбү?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> элемент жок кылынууда…</item>
- <item quantity="one">Элемент жок кылынууда…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Кошуу"</string>
+ <string name="deselect" msgid="4297825044827769490">"Тандоодон чыгаруу"</string>
+ <string name="deselected" msgid="8488133193326208475">"Тандоодон чыгарылды"</string>
+ <string name="select" msgid="2704765470563027689">"Тандоо"</string>
+ <string name="selected" msgid="9151797369975828124">"Тандалды"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> объектке чейин тандаңыз}other{<xliff:g id="COUNT_1">^1</xliff:g> объектке чейин тандаңыз}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Акыркы"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Сүрөттөр же видеолор жок"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Альбомдор жок"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Тандалганды карап көрүү"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Сүрөттөр"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Альбомдор"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Алдын ала көрүү"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Жумуш профилине которулуу"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Жеке профилге которулуу"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Администраторуңуз бөгөттөп койгон"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Жумушка байланыштуу маалыматка жеке колдонмодон кирүүгө тыюу салынат"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Жеке маалыматка жумуш колдонмосунан кирүүгө тыюу салынат"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Жумуш колдонмолору тындырылды"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Жумуш сүрөттөрүн ачуу үчүн жумуш колдонмолорун иштетип, кайра аракет кылыңыз"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Бул колдонмо сиз тандаган сүрөттөргө гана кире алат"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> нерсе}other{<xliff:g id="COUNT_1">^1</xliff:g> нерсе}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Кошуу (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Камера"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Жүктөлүп алынгандар"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Тандалмалар"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Скриншоттор"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Кыймылды сүрөткө тартуу"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="TIME">%2$s</xliff:g> тартылган <xliff:g id="ITEM_NAME">%1$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Видео <xliff:g id="TIME">%1$s</xliff:g> узактыгы <xliff:g id="DURATION">%2$s</xliff:g> болуп тартылган"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Сүрөт"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Кыймылды сүрөткө тартуу"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Видеонун үнүн басуу"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Видеонун үнүн чыгаруу"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Видеону ойнотуу"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Видеону тындыруу"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Булуттагы медиа эми <xliff:g id="PKG_NAME">%1$s</xliff:g> кызматында жеткиликтүү"</string>
+ <string name="not_selected" msgid="2244008151669896758">"тандалган жок"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул аудио файлды өзгөртсүнбү?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> аудио файлды өзгөртсүнбү?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Аудио файл өзгөртүлүүдө…}other{<xliff:g id="COUNT">^1</xliff:g> аудио файл өзгөртүлүүдө…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул видеону өзгөртсүнбү?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> видеону өзгөртсүнбү?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Видео өзгөртүлүүдө…}other{<xliff:g id="COUNT">^1</xliff:g> видео өзгөртүлүүдө…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул сүрөттү өзгөртсүнбү?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> сүрөттү өзгөртсүнбү?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Сүрөт өзгөртүлүүдө…}other{<xliff:g id="COUNT">^1</xliff:g> сүрөт өзгөртүлүүдө…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул нерсени өзгөртсүнбү?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> нерсени өзгөртсүнбү?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Элемент өзгөртүлүүдө…}other{<xliff:g id="COUNT">^1</xliff:g> элемент өзгөртүлүүдө…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул аудио файлды таштандыга салсынбы?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> аудио файлды таштандыга салсынбы?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Аудио файл таштандыга ыргытылууда…}other{<xliff:g id="COUNT">^1</xliff:g> аудио файл таштандыга ыргытылууда…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул видеону таштандыга салсынбы?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> видеону таштандыга салсынбы?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Видео таштандыга ыргытылууда…}other{<xliff:g id="COUNT">^1</xliff:g> видео таштандыга ыргытылууда…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул сүрөттү таштандыга салсынбы?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> сүрөттү таштандыга салсынбы?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Сүрөт таштандыга ыргытылууда…}other{<xliff:g id="COUNT">^1</xliff:g> сүрөт таштандыга ыргытылууда…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул нерсени таштандыга салсынбы?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> нерсени таштандыга салсынбы?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Элемент таштандыга ыргытылууда…}other{<xliff:g id="COUNT">^1</xliff:g> элемент таштандыга ыргытылууда…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул аудио файлды калыбына келтирсинби?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> аудио файлды калыбына келтирсинби?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Аудио файл калыбына келтирилүүдө…}other{<xliff:g id="COUNT">^1</xliff:g> аудио файл калыбына келтирилүүдө…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул видеону калыбына келтирсинби?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> видеону калыбына келтирсинби?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Видео калыбына келтирилүүдө…}other{<xliff:g id="COUNT">^1</xliff:g> видео калыбына келтирилүүдө…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул сүрөттү калыбына келтирсинби?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> сүрөттү калыбына келтирсинби?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Сүрөт калыбына келтирилүүдө…}other{<xliff:g id="COUNT">^1</xliff:g> сүрөт калыбына келтирилүүдө…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул нерсени калыбына келтирсинби?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> нерсени калыбына келтирсинби?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Элемент калыбына келтирилүүдө…}other{<xliff:g id="COUNT">^1</xliff:g> элемент калыбына келтирилүүдө…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул аудио файлды өчүрсүнбү?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> аудио файлды өчүрсүнбү?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Аудио файл өчүрүлүүдө…}other{<xliff:g id="COUNT">^1</xliff:g> аудио файл өчүрүлүүдө…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул видеону өчүрсүнбү?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> видеону өчүрсүнбү?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Видео жок кылынууда…}other{<xliff:g id="COUNT">^1</xliff:g> видео жок кылынууда…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул сүрөттү өчүрсүнбү?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> сүрөттү өчүрсүнбү?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Сүрөт жок кылынууда…}other{<xliff:g id="COUNT">^1</xliff:g> сүрөт жок кылынууда…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> колдонмосу бул нерсени өчүрсүнбү?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g id="COUNT">^2</xliff:g> нерсени өчүрсүнбү?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Элемент жок кылынууда…}other{<xliff:g id="COUNT">^1</xliff:g> элемент жок кылынууда…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> медиа файлдарды иштете албайт"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Медианы иштетүү жокко чыгарылды"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Медианы иштетүү катасы"</string>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index c21aec4..5f1de6e 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"ມີເດຍ"</string>
<string name="storage_description" msgid="4081716890357580107">"ບ່ອນຈັດເກັບຂໍ້ມູນໃນເຄື່ອງ"</string>
<string name="app_label" msgid="9035307001052716210">"ພື້ນທີ່ຈັດເກັບຂໍ້ມູນມີເດຍ"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"ຕົວເລືອກຮູບພາບ"</string>
<string name="artist_label" msgid="8105600993099120273">"ສິນລະປິນ"</string>
<string name="unknown" msgid="2059049215682829375">"ບໍ່ຮູ້ຈັກ"</string>
<string name="root_images" msgid="5861633549189045666">"ຮູບພາບ"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"ດຳເນີນການຕໍ່"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"ອະນຸຍາດ"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"ປະຕິເສດ"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">ຮວມ <xliff:g id="COUNT_1">^1</xliff:g> ລາຍການເພີ່ມເຕີມ</item>
- <item quantity="one">ຮວມ <xliff:g id="COUNT_0">^1</xliff:g> ລາຍການເພີ່ມເຕີມ</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{ຮວມ <xliff:g id="COUNT_0">^1</xliff:g> ລາຍການເພີ່ມເຕີມ}other{ຮວມ <xliff:g id="COUNT_1">^1</xliff:g> ລາຍການເພີ່ມເຕີມ}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"ລຶບລ້າງໄຟລ໌ແອັບຊົ່ວຄາວ"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ຕ້ອງການລ້າງໄຟລ໌ຊົ່ວຄາວບາງຢ່າງອອກ. ນີ້ອາດເຮັດໃຫ້ມີການໃຊ້ແບັດເຕີຣີ ຫຼື ອິນເຕີເນັດມືຖືຫຼາຍຂຶ້ນ."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"ກຳລັງລຶບລ້າງໄຟລ໌ແອັບຊົ່ວຄາວ…"</string>
<string name="clear" msgid="5524638938415865915">"ລຶບລ້າງ"</string>
<string name="allow" msgid="8885707816848569619">"ອະນຸຍາດ"</string>
<string name="deny" msgid="6040983710442068936">"ປະຕິເສດ"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ແກ້ໄຂໄຟລ໌ສຽງ <xliff:g id="COUNT">^2</xliff:g> ໄຟລ໌ບໍ?</item>
- <item quantity="one">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ແກ້ໄຂໄຟລ໌ສຽງນີ້ບໍ?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">ກຳລັງແກ້ໄຂໄຟລ໌ສຽງ <xliff:g id="COUNT">^1</xliff:g> ໄຟລ໌…</item>
- <item quantity="one">ກຳລັງແກ້ໄຂໄຟລ໌ສຽງ…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ແກ້ໄຂ <xliff:g id="COUNT">^2</xliff:g> ວິດີໂອບໍ?</item>
- <item quantity="one">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ແກ້ໄຂວິດີໂອນີ້ບໍ?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">ກຳລັງແກ້ໄຂວິດີໂອ <xliff:g id="COUNT">^1</xliff:g> ອັນ…</item>
- <item quantity="one">ກຳລັງແກ້ໄຂວິດີໂອ…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ແກ້ໄຂ <xliff:g id="COUNT">^2</xliff:g> ຮູບບໍ?</item>
- <item quantity="one">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ແກ້ໄຂຮູບນີ້ບໍ?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">ກຳລັງແກ້ໄຂຮູບພາບ <xliff:g id="COUNT">^1</xliff:g> ຮູບ…</item>
- <item quantity="one">ກຳລັງແກ້ໄຂຮູບພາບ…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ແກ້ໄຂ <xliff:g id="COUNT">^2</xliff:g> ລາຍການບໍ?</item>
- <item quantity="one">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ແກ້ໄຂລາຍການນີ້ບໍ?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">ກຳລັງແກ້ໄຂລາຍການ <xliff:g id="COUNT">^1</xliff:g> ລາຍການ…</item>
- <item quantity="one">ກຳລັງແກ້ໄຂລາຍການ…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ຍ້າຍໄຟລ໌ສຽງ <xliff:g id="COUNT">^2</xliff:g> ໄຟລ໌ໄປໃສ່ຖັງຂີ້ເຫຍື້ອບໍ?</item>
- <item quantity="one">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ຍ້າຍໄຟລ໌ສຽງນີ້ໄປໃສ່ຖັງຂີ້ເຫຍື້ອບໍ?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">ກຳລັງຍ້າຍໄຟລ໌ສຽງ <xliff:g id="COUNT">^1</xliff:g> ໄຟລ໌ໄປໃສ່ຖັງຂີ້ເຫຍື້ອ…</item>
- <item quantity="one">ກຳລັງຍ້າຍໄຟລ໌ສຽງໄປໃສ່ຖັງຂີ້ເຫຍື້ອ…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ຍ້າຍ <xliff:g id="COUNT">^2</xliff:g> ວິດີໂອໄປໃສ່ຖັງຂີ້ເຫຍື້ອບໍ?</item>
- <item quantity="one">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ຍ້າຍວິດີໂອນີ້ໄປໃສ່ຖັງຂີ້ເຫຍື້ອບໍ?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">ກຳລັງຍ້າຍວິດີໂອ <xliff:g id="COUNT">^1</xliff:g> ອັນໄປໃສ່ຖັງຂີ້ເຫຍື້ອ…</item>
- <item quantity="one">ກຳລັງຍ້າຍວິດີໂອໄປໃສ່ຖັງຂີ້ເຫຍື້ອ…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ຍ້າຍ <xliff:g id="COUNT">^2</xliff:g> ຮູບໄປໃສ່ຖັງຂີ້ເຫຍື້ອບໍ?</item>
- <item quantity="one">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ຍ້າຍຮູບນີ້ໄປໃສ່ຖັງຂີ້ເຫຍື້ອບໍ?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">ກຳລັງຍ້າຍຮູບພາບ <xliff:g id="COUNT">^1</xliff:g> ຮູບໄປໃສ່ຖັງຂີ້ເຫຍື້ອ…</item>
- <item quantity="one">ກຳລັງຍ້າຍຮູບພາບໄປໃສ່ຖັງຂີ້ເຫຍື້ອ…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ຍ້າຍ <xliff:g id="COUNT">^2</xliff:g> ລາຍການໄປໃສ່ຖັງຂີ້ເຫຍື້ອບໍ?</item>
- <item quantity="one">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ຍ້າຍລາຍການນີ້ໄປໃສ່ຖັງຂີ້ເຫຍື້ອບໍ?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">ກຳລັງຍ້າຍລາຍການ <xliff:g id="COUNT">^1</xliff:g> ລາຍການໄປໃສ່ຖັງຂີ້ເຫຍື້ອ…</item>
- <item quantity="one">ກຳລັງຍ້າຍລາຍການໄປໃສ່ຖັງຂີ້ເຫຍື້ອ…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ຍ້າຍ <xliff:g id="COUNT">^2</xliff:g> ໄຟລ໌ສຽງອອກຈາກຖັງຂີ້ເຫຍື້ອບໍ?</item>
- <item quantity="one">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ຍ້າຍໄຟລ໌ສຽງນີ້ອອກຈາກຖັງຂີ້ເຫຍື້ອບໍ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">ກຳລັງຍ້າຍໄຟລ໌ສຽງ <xliff:g id="COUNT">^1</xliff:g> ໄຟລ໌ອອກຈາກຖັງຂີ້ເຫຍື້ອ…</item>
- <item quantity="one">ກຳລັງຍ້າຍໄຟລ໌ສຽງອອກຈາກຖັງຂີ້ເຫຍື້ອ…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ຍ້າຍ <xliff:g id="COUNT">^2</xliff:g> ວິດີໂອອອກຈາກຖັງຂີ້ເຫຍື້ອບໍ?</item>
- <item quantity="one">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ຍ້າຍວິດີໂອນີ້ອອກຈາກຖັງຂີ້ເຫຍື້ອບໍ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">ກຳລັງຍ້າຍວິດີໂອ <xliff:g id="COUNT">^1</xliff:g> ອັນອອກຈາກຖັງຂີ້ເຫຍື້ອ…</item>
- <item quantity="one">ກຳລັງຍ້າຍວິດີໂອອອກຈາກຖັງຂີ້ເຫຍື້ອ…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ຍ້າຍ <xliff:g id="COUNT">^2</xliff:g> ຮູບອອກຈາກຖັງຂີ້ເຫຍື້ອບໍ?</item>
- <item quantity="one">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ຍ້າຍຮູບນີ້ອອກຈາກຖັງຂີ້ເຫຍື້ອບໍ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">ກຳລັງຍ້າຍຮູບພາບ <xliff:g id="COUNT">^1</xliff:g> ຮູບອອກຈາກຖັງຂີ້ເຫຍື້ອ…</item>
- <item quantity="one">ກຳລັງຍ້າຍຮູບພາບອອກຈາກຖັງຂີ້ເຫຍື້ອ…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ຍ້າຍ <xliff:g id="COUNT">^2</xliff:g> ລາຍການອອກຈາກຖັງຂີ້ເຫຍື້ອບໍ?</item>
- <item quantity="one">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ຍ້າຍລາຍການນີ້ອອກຈາກຖັງຂີ້ເຫຍື້ອບໍ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">ກຳລັງຍ້າຍລາຍການ <xliff:g id="COUNT">^1</xliff:g> ລາຍການອອກຈາກຖັງຂີ້ເຫຍື້ອ…</item>
- <item quantity="one">ກຳລັງຍ້າຍລາຍການອອກຈາກຖັງຂີ້ເຫຍື້ອ…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ລຶບໄຟລ໌ສຽງ <xliff:g id="COUNT">^2</xliff:g> ໄຟລ໌ບໍ?</item>
- <item quantity="one">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ລຶບໄຟລ໌ສຽງນີ້ບໍ?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">ກຳລັງລຶບໄຟລ໌ສຽງ <xliff:g id="COUNT">^1</xliff:g> ໄຟລ໌ອອກ…</item>
- <item quantity="one">ກຳລັງລຶບໄຟລ໌ສຽງອອກ…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ລຶບ <xliff:g id="COUNT">^2</xliff:g> ວິດີໂອບໍ?</item>
- <item quantity="one">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ລຶບວິດີໂອນີ້ບໍ?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">ກຳລັງລຶບວິດີໂອ <xliff:g id="COUNT">^1</xliff:g> ອັນອອກ…</item>
- <item quantity="one">ກຳລັງລຶບວິດີໂອ…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ລຶບ <xliff:g id="COUNT">^2</xliff:g> ຮູບບໍ?</item>
- <item quantity="one">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ລຶບຮູບນີ້ບໍ?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">ກຳລັງລຶບຮູບພາບ <xliff:g id="COUNT">^1</xliff:g> ຮູບອອກ…</item>
- <item quantity="one">ກຳລັງລຶບຮູບພາບ…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ລຶບ <xliff:g id="COUNT">^2</xliff:g> ລາຍການບໍ?</item>
- <item quantity="one">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ລຶບລາຍການນີ້ບໍ?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">ກຳລັງລຶບລາຍການ <xliff:g id="COUNT">^1</xliff:g> ລາຍການອອກ…</item>
- <item quantity="one">ກຳລັງລຶບລາຍການອອກ…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"ເພີ່ມ"</string>
+ <string name="deselect" msgid="4297825044827769490">"ເຊົາເລືອກ"</string>
+ <string name="deselected" msgid="8488133193326208475">"ເຊົາເລືອກແລ້ວ"</string>
+ <string name="select" msgid="2704765470563027689">"ເລືອກ"</string>
+ <string name="selected" msgid="9151797369975828124">"ເລືອກແລ້ວ"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{ເລືອກບໍ່ເກີນ <xliff:g id="COUNT_0">^1</xliff:g> ລາຍການ}other{ເລືອກບໍ່ເກີນ <xliff:g id="COUNT_1">^1</xliff:g> ລາຍການ}}"</string>
+ <string name="recent" msgid="6694613584743207874">"ຫຼ້າສຸດ"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"ບໍ່ມີຮູບພາບ ຫຼື ວິດີໂອ"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"ບໍ່ມີອະລະບ້ຳ"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"ເບິ່ງອັນທີ່ເລືອກໄວ້"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"ຮູບພາບ"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"ອະລະບ້ຳ"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"ຕົວຢ່າງ"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"ສະຫຼັບໄປໂປຣໄຟລ໌ວຽກ"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"ສະຫຼັບໄປໂປຣໄຟລ໌ສ່ວນຕົວ"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"ຖືກບລັອກໄວ້ໂດຍຜູ້ເບິ່ງແຍງຂອງທ່ານ"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"ບໍ່ອະນຸຍາດການເຂົ້າເຖິງຂໍ້ມູນບ່ອນເຮັດວຽກຈາກແອັບສ່ວນຕົວ"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"ບໍ່ອະນຸຍາດການເຂົ້າເຖິງຂໍ້ມູນສ່ວນຕົວຈາກແອັບບ່ອນເຮັດວຽກ"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"ຢຸດແອັບບ່ອນເຮັດວຽກຊົ່ວຄາວແລ້ວ"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"ເພື່ອເປີດຮູບພາບບ່ອນເຮັດວຽກ, ໃຫ້ເປີດໃຊ້ແອັບບ່ອນເຮັດວຽກຂອງທ່ານແລ້ວລອງໃໝ່"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"ແອັບນີ້ສາມາດເຂົ້າເຖິງຮູບພາບທີ່ທ່ານເລືອກເທົ່ານັ້ນ"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> ລາຍການ}other{<xliff:g id="COUNT_1">^1</xliff:g> ລາຍການ}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"ເພີ່ມ (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"ກ້ອງຖ່າຍຮູບ"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"ດາວໂຫຼດ"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"ລາຍການທີ່ມັກ"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"ຮູບໜ້າຈໍ"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"ຮູບພາບໂມຊັນ"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> ຖ່າຍເມື່ອ <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"ຖ່າຍວິດີໂອເມື່ອ <xliff:g id="TIME">%1$s</xliff:g> ໂດຍມີໄລຍະເວລາ <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"ຮູບພາບ"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"ຮູບພາບໂມຊັນ"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"ປິດສຽງວິດີໂອ"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"ເຊົາປິດສຽງວິດີໂອ"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"ຫຼິ້ນວິດີໂອ"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"ຢຸດວິດີໂອຊົ່ວຄາວ"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"ຕອນນີ້ສາມາດໃຊ້ມີເດຍຄລາວຈາກ <xliff:g id="PKG_NAME">%1$s</xliff:g> ໄດ້ແລ້ວ"</string>
+ <string name="not_selected" msgid="2244008151669896758">"ບໍ່ໄດ້ເລືອກ"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ແກ້ໄຂໄຟລ໌ສຽງນີ້ບໍ?}other{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ແກ້ໄຂໄຟລ໌ສຽງ <xliff:g id="COUNT">^2</xliff:g> ໄຟລ໌ບໍ?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{ກຳລັງແກ້ໄຂໄຟລ໌ສຽງ…}other{ກຳລັງແກ້ໄຂໄຟລ໌ສຽງ <xliff:g id="COUNT">^1</xliff:g> ໄຟລ໌…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ແກ້ໄຂວິດີໂອນີ້ບໍ?}other{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ແກ້ໄຂວິດີໂອ <xliff:g id="COUNT">^2</xliff:g> ລາຍການບໍ?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{ກຳລັງແກ້ໄຂວິດີໂອ…}other{ກຳລັງແກ້ໄຂວິດີໂອ <xliff:g id="COUNT">^1</xliff:g> ລາຍການ…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ແກ້ໄຂຮູບພາບນີ້ບໍ?}other{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ແກ້ໄຂຮູບພາບ <xliff:g id="COUNT">^2</xliff:g> ລາຍການບໍ?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{ກຳລັງແກ້ໄຂຮູບພາບ…}other{ກຳລັງແກ້ໄຂຮູບພາບ <xliff:g id="COUNT">^1</xliff:g> ລາຍການ…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ແກ້ໄຂລາຍການນີ້ບໍ?}other{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ແກ້ໄຂ <xliff:g id="COUNT">^2</xliff:g> ລາຍການບໍ?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{ກຳລັງແກ້ໄຂລາຍການ…}other{ກຳລັງແກ້ໄຂລາຍການ <xliff:g id="COUNT">^1</xliff:g> ລາຍການ…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ຍ້າຍໄຟລ໌ສຽງນີ້ໄປໃສ່ຖັງຂີ້ເຫຍື້ອບໍ?}other{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ຍ້າຍໄຟລ໌ສຽງ <xliff:g id="COUNT">^2</xliff:g> ໄຟລ໌ໄປໃສ່ຖັງຂີ້ເຫຍື້ອບໍ?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{ກຳລັງຍ້າຍໄຟລ໌ສຽງໄປໃສ່ຖັງຂີ້ເຫຍື້ອ…}other{ກຳລັງຍ້າຍໄຟລ໌ສຽງ <xliff:g id="COUNT">^1</xliff:g> ໄຟລ໌ໄປໃສ່ຖັງຂີ້ເຫຍື້ອ…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ຍ້າຍວິດີໂອນີ້ໄປໃສ່ຖັງຂີ້ເຫຍື້ອບໍ?}other{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ຍ້າຍວິດີໂອ <xliff:g id="COUNT">^2</xliff:g> ລາຍການໄປໃສ່ຖັງຂີ້ເຫຍື້ອບໍ?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{ກຳລັງຍ້າຍວິດີໂອໄປໃສ່ຖັງຂີ້ເຫຍື້ອ…}other{ກຳລັງຍ້າຍວິດີໂອ <xliff:g id="COUNT">^1</xliff:g> ລາຍການໄປໃສ່ຖັງຂີ້ເຫຍື້ອ…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ຍ້າຍຮູບນີ້ໄປໃສ່ຖັງຂີ້ເຫຍື້ອບໍ?}other{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ຍ້າຍຮູບພາບ <xliff:g id="COUNT">^2</xliff:g> ລາຍການໄປໃສ່ຖັງຂີ້ເຫຍື້ອບໍ?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{ກຳລັງຍ້າຍຮູບພາບໄປໃສ່ຖັງຂີ້ເຫຍື້ອ…}other{ກຳລັງຍ້າຍຮູບພາບ <xliff:g id="COUNT">^1</xliff:g> ລາຍການໄປໃສ່ຖັງຂີ້ເຫຍື້ອ…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ຍ້າຍລາຍການນີ້ໄປໃສ່ຖັງຂີ້ເຫຍື້ອບໍ?}other{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ຍ້າຍ <xliff:g id="COUNT">^2</xliff:g> ລາຍການໄປໃສ່ຖັງຂີ້ເຫຍື້ອບໍ?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{ກຳລັງຍ້າຍລາຍການໄປໃສ່ຖັງຂີ້ເຫຍື້ອ…}other{ກຳລັງຍ້າຍລາຍການ <xliff:g id="COUNT">^1</xliff:g> ລາຍການໄປໃສ່ຖັງຂີ້ເຫຍື້ອ…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ຍ້າຍໄຟລ໌ສຽງນີ້ອອກຈາກຖັງຂີ້ເຫຍື້ອບໍ?}other{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ຍ້າຍໄຟລ໌ສຽງ <xliff:g id="COUNT">^2</xliff:g> ສຽງອອກຈາກຖັງຂີ້ເຫຍື້ອບໍ?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{ກຳລັງຍ້າຍໄຟລ໌ສຽງອອກຈາກຖັງຂີ້ເຫຍື້ອ…}other{ກຳລັງຍ້າຍໄຟລ໌ສຽງ <xliff:g id="COUNT">^1</xliff:g> ໄຟລ໌ອອກຈາກຖັງຂີ້ເຫຍື້ອ…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ຍ້າຍວິດີໂອນີ້ອອກຈາກຖັງຂີ້ເຫຍື້ອບໍ?}other{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ຍ້າຍວິດີໂອ <xliff:g id="COUNT">^2</xliff:g> ລາຍການອອກຈາກຖັງຂີ້ເຫຍື້ອບໍ?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{ກຳລັງຍ້າຍວິດີໂອອອກຈາກຖັງຂີ້ເຫຍື້ອ…}other{ກຳລັງຍ້າຍວິດີໂອ <xliff:g id="COUNT">^1</xliff:g> ລາຍການອອກຈາກຖັງຂີ້ເຫຍື້ອ…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ຍ້າຍຮູບພາບນີ້ອອກຈາກຖັງຂີ້ເຫຍື້ອບໍ?}other{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ຍ້າຍຮູບພາບ <xliff:g id="COUNT">^2</xliff:g> ລາຍການອອກຈາກຖັງຂີ້ເຫຍື້ອບໍ?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{ກຳລັງຍ້າຍຮູບພາບອອກຈາກຖັງຂີ້ເຫຍື້ອ…}other{ກຳລັງຍ້າຍຮູບພາບ <xliff:g id="COUNT">^1</xliff:g> ລາຍການອອກຈາກຖັງຂີ້ເຫຍື້ອ…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ຍ້າຍລາຍການນີ້ອອກຈາກຖັງຂີ້ເຫຍື້ອບໍ?}other{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ຍ້າຍ <xliff:g id="COUNT">^2</xliff:g> ລາຍການອອກຈາກຖັງຂີ້ເຫຍື້ອບໍ?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{ກຳລັງຍ້າຍລາຍການອອກຈາກຖັງຂີ້ເຫຍື້ອ…}other{ກຳລັງຍ້າຍລາຍການ <xliff:g id="COUNT">^1</xliff:g> ລາຍການອອກຈາກຖັງຂີ້ເຫຍື້ອ…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ລຶບໄຟລ໌ສຽງນີ້ອອກບໍ?}other{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ລຶບໄຟລ໌ສຽງ <xliff:g id="COUNT">^2</xliff:g> ໄຟລ໌ອອກບໍ?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{ກຳລັງລຶບໄຟລ໌ສຽງອອກ…}other{ກຳລັງລຶບໄຟລ໌ສຽງ <xliff:g id="COUNT">^1</xliff:g> ໄຟລ໌ອອກ…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ລຶບວິດີໂອນີ້ອອກບໍ?}other{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ລຶບວິດີໂອ <xliff:g id="COUNT">^2</xliff:g> ລາຍການອອກບໍ?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{ກຳລັງລຶບວິດີໂອ…}other{ກຳລັງລຶບວິດີໂອ <xliff:g id="COUNT">^1</xliff:g> ລາຍການອອກ…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ລຶບຮູບນີ້ອອກບໍ?}other{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ລຶບຮູບພາບ <xliff:g id="COUNT">^2</xliff:g> ລາຍການອອກບໍ?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{ກຳລັງລຶບຮູບພາບ…}other{ກຳລັງລຶບຮູບພາບ <xliff:g id="COUNT">^1</xliff:g> ລາຍການອອກ…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_0">^1</xliff:g> ລຶບລາຍການນີ້ອອກບໍ?}other{ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ລຶບ <xliff:g id="COUNT">^2</xliff:g> ລາຍການອອກບໍ?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{ກຳລັງລຶບລາຍການອອກ…}other{ກຳລັງລຶບລາຍການ <xliff:g id="COUNT">^1</xliff:g> ລາຍການອອກ…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> ບໍ່ສາມາດປະມວນຜົນໄຟລ໌ມີເດຍໄດ້"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"ຍົກເລີກການປະມວນຜົນມີເດຍແລ້ວ"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"ການປະມວນຜົນມີເດຍຜິດພາດ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index c4f30d9..87ac100 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Nuotraukų rinkiklis"</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>
@@ -29,216 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Tęsti"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Leisti"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Atmesti"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">Dar <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="few">Dar <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="many">Dar <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">Dar <xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">Dar <xliff:g id="COUNT_1">^1</xliff:g> papildomas elementas</item>
- <item quantity="few">Dar <xliff:g id="COUNT_1">^1</xliff:g> papildomi elementai</item>
- <item quantity="many">Dar <xliff:g id="COUNT_1">^1</xliff:g> papildomo elemento</item>
- <item quantity="other">Dar <xliff:g id="COUNT_1">^1</xliff:g> papildomų elementų</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{Dar <xliff:g id="COUNT_0">^1</xliff:g>}one{Dar <xliff:g id="COUNT_1">^1</xliff:g>}few{Dar <xliff:g id="COUNT_1">^1</xliff:g>}many{Dar <xliff:g id="COUNT_1">^1</xliff:g>}other{Dar <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Dar <xliff:g id="COUNT_0">^1</xliff:g> papildomas elementas}one{Dar <xliff:g id="COUNT_1">^1</xliff:g> papildomas elementas}few{Dar <xliff:g id="COUNT_1">^1</xliff:g> papildomi elementai}many{Dar <xliff:g id="COUNT_1">^1</xliff:g> papildomo elemento}other{Dar <xliff:g id="COUNT_1">^1</xliff:g> papildomų elementų}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Laikinų programų failų išvalymas"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"Programa „<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“ nori išvalyti kai kuriuos laikinus failus. Dėl to gali būti suvartojama daugiau akumuliatoriaus energijos ar mobiliojo ryšio duomenų."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Išvalomi laikini programos failai…"</string>
<string name="clear" msgid="5524638938415865915">"Išvalyti"</string>
<string name="allow" msgid="8885707816848569619">"Leisti"</string>
<string name="deny" msgid="6040983710442068936">"Atmesti"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> garso failą?</item>
- <item quantity="few">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> garso failus?</item>
- <item quantity="many">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> garso failo?</item>
- <item quantity="other">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> garso failų?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one">Keičiamas <xliff:g id="COUNT">^1</xliff:g> garso failas…</item>
- <item quantity="few">Keičiami <xliff:g id="COUNT">^1</xliff:g> garso failai…</item>
- <item quantity="many">Keičiama <xliff:g id="COUNT">^1</xliff:g> garso failo…</item>
- <item quantity="other">Keičiama <xliff:g id="COUNT">^1</xliff:g> garso failų…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašą?</item>
- <item quantity="few">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašus?</item>
- <item quantity="many">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašo?</item>
- <item quantity="other">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašų?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one">Keičiamas <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašas…</item>
- <item quantity="few">Keičiami <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašai…</item>
- <item quantity="many">Keičiama <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašo…</item>
- <item quantity="other">Keičiama <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašų…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> nuotrauką?</item>
- <item quantity="few">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> nuotraukas?</item>
- <item quantity="many">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> nuotraukos?</item>
- <item quantity="other">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> nuotraukų?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one">Keičiama <xliff:g id="COUNT">^1</xliff:g> nuotrauka…</item>
- <item quantity="few">Keičiamos <xliff:g id="COUNT">^1</xliff:g> nuotraukos…</item>
- <item quantity="many">Keičiama <xliff:g id="COUNT">^1</xliff:g> nuotraukos…</item>
- <item quantity="other">Keičiama <xliff:g id="COUNT">^1</xliff:g> nuotraukų…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> elementą?</item>
- <item quantity="few">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> elementus?</item>
- <item quantity="many">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> elemento?</item>
- <item quantity="other">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> elementų?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one">Keičiamas <xliff:g id="COUNT">^1</xliff:g> elementas…</item>
- <item quantity="few">Keičiami <xliff:g id="COUNT">^1</xliff:g> elementai…</item>
- <item quantity="many">Keičiama <xliff:g id="COUNT">^1</xliff:g> elemento…</item>
- <item quantity="other">Keičiama <xliff:g id="COUNT">^1</xliff:g> elementų…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> garso failą į šiukšliadėžę?</item>
- <item quantity="few">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> garso failus į šiukšliadėžę?</item>
- <item quantity="many">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> garso failo į šiukšliadėžę?</item>
- <item quantity="other">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> garso failų į šiukšliadėžę?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one">Perkeliamas <xliff:g id="COUNT">^1</xliff:g> garso failas į šiukšliadėžę…</item>
- <item quantity="few">Perkeliami <xliff:g id="COUNT">^1</xliff:g> garso failai į šiukšliadėžę…</item>
- <item quantity="many">Perkeliama <xliff:g id="COUNT">^1</xliff:g> garso failo į šiukšliadėžę…</item>
- <item quantity="other">Perkeliama <xliff:g id="COUNT">^1</xliff:g> garso failų į šiukšliadėžę…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašą į šiukšliadėžę?</item>
- <item quantity="few">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašus į šiukšliadėžę?</item>
- <item quantity="many">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašo į šiukšliadėžę?</item>
- <item quantity="other">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašų į šiukšliadėžę?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one">Perkeliamas <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašas į šiukšliadėžę…</item>
- <item quantity="few">Perkeliami <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašai į šiukšliadėžę…</item>
- <item quantity="many">Perkeliama <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašo į šiukšliadėžę…</item>
- <item quantity="other">Perkeliama <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašų į šiukšliadėžę…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> nuotrauką į šiukšliadėžę?</item>
- <item quantity="few">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> nuotraukas į šiukšliadėžę?</item>
- <item quantity="many">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> nuotraukos į šiukšliadėžę?</item>
- <item quantity="other">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> nuotraukų į šiukšliadėžę?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one">Perkeliama <xliff:g id="COUNT">^1</xliff:g> nuotrauka į šiukšliadėžę…</item>
- <item quantity="few">Perkeliamos <xliff:g id="COUNT">^1</xliff:g> nuotraukos į šiukšliadėžę…</item>
- <item quantity="many">Perkeliama <xliff:g id="COUNT">^1</xliff:g> nuotraukos į šiukšliadėžę…</item>
- <item quantity="other">Perkeliama <xliff:g id="COUNT">^1</xliff:g> nuotraukų į šiukšliadėžę…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> elementą į šiukšliadėžę?</item>
- <item quantity="few">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> elementus į šiukšliadėžę?</item>
- <item quantity="many">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> elemento į šiukšliadėžę?</item>
- <item quantity="other">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> elementų į šiukšliadėžę?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one">Perkeliamas <xliff:g id="COUNT">^1</xliff:g> elementas į šiukšliadėžę…</item>
- <item quantity="few">Perkeliami <xliff:g id="COUNT">^1</xliff:g> elementai į šiukšliadėžę…</item>
- <item quantity="many">Perkeliama <xliff:g id="COUNT">^1</xliff:g> elemento į šiukšliadėžę…</item>
- <item quantity="other">Perkeliama <xliff:g id="COUNT">^1</xliff:g> elementų į šiukšliadėžę…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> garso failą iš šiukšliadėžės?</item>
- <item quantity="few">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> garso failus iš šiukšliadėžės?</item>
- <item quantity="many">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> garso failo iš šiukšliadėžės?</item>
- <item quantity="other">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> garso failų iš šiukšliadėžės?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one">Perkeliamas <xliff:g id="COUNT">^1</xliff:g> garso failas iš šiukšliadėžės…</item>
- <item quantity="few">Perkeliami <xliff:g id="COUNT">^1</xliff:g> garso failai iš šiukšliadėžės…</item>
- <item quantity="many">Perkeliama <xliff:g id="COUNT">^1</xliff:g> garso failo iš šiukšliadėžės…</item>
- <item quantity="other">Perkeliama <xliff:g id="COUNT">^1</xliff:g> garso failų iš šiukšliadėžės…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašą iš šiukšliadėžės?</item>
- <item quantity="few">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašus iš šiukšliadėžės?</item>
- <item quantity="many">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašo iš šiukšliadėžės?</item>
- <item quantity="other">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašų iš šiukšliadėžės?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one">Perkeliamas <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašas iš šiukšliadėžės…</item>
- <item quantity="few">Perkeliami <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašai iš šiukšliadėžės…</item>
- <item quantity="many">Perkeliama <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašo iš šiukšliadėžės…</item>
- <item quantity="other">Perkeliama <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašų iš šiukšliadėžės…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> nuotrauką iš šiukšliadėžės?</item>
- <item quantity="few">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> nuotraukas iš šiukšliadėžės?</item>
- <item quantity="many">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> nuotraukos iš šiukšliadėžės?</item>
- <item quantity="other">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> nuotraukų iš šiukšliadėžės?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one">Perkeliama <xliff:g id="COUNT">^1</xliff:g> nuotrauka iš šiukšliadėžės…</item>
- <item quantity="few">Perkeliamos <xliff:g id="COUNT">^1</xliff:g> nuotraukos iš šiukšliadėžės…</item>
- <item quantity="many">Perkeliama <xliff:g id="COUNT">^1</xliff:g> nuotraukos iš šiukšliadėžės…</item>
- <item quantity="other">Perkeliama <xliff:g id="COUNT">^1</xliff:g> nuotraukų iš šiukšliadėžės…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> elementą iš šiukšliadėžės?</item>
- <item quantity="few">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> elementus iš šiukšliadėžės?</item>
- <item quantity="many">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> elemento iš šiukšliadėžės?</item>
- <item quantity="other">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> elementų iš šiukšliadėžės?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one">Perkeliamas <xliff:g id="COUNT">^1</xliff:g> elementas iš šiukšliadėžės…</item>
- <item quantity="few">Perkeliami <xliff:g id="COUNT">^1</xliff:g> elementai iš šiukšliadėžės…</item>
- <item quantity="many">Perkeliama <xliff:g id="COUNT">^1</xliff:g> elemento iš šiukšliadėžės…</item>
- <item quantity="other">Perkeliama <xliff:g id="COUNT">^1</xliff:g> elementų iš šiukšliadėžės…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one">Leisti „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> garso failą?</item>
- <item quantity="few">Leisti „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> garso failus?</item>
- <item quantity="many">Leisti „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> garso failo?</item>
- <item quantity="other">Leisti „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> garso failų?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one">Ištrinamas <xliff:g id="COUNT">^1</xliff:g> garso failas…</item>
- <item quantity="few">Ištrinami <xliff:g id="COUNT">^1</xliff:g> garso failai…</item>
- <item quantity="many">Ištrinama <xliff:g id="COUNT">^1</xliff:g> garso failo…</item>
- <item quantity="other">Ištrinama <xliff:g id="COUNT">^1</xliff:g> garso failų…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašą?</item>
- <item quantity="few">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašus?</item>
- <item quantity="many">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašo?</item>
- <item quantity="other">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašų?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one">Ištrinamas <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašas…</item>
- <item quantity="few">Ištrinami <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašai…</item>
- <item quantity="many">Ištrinama <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašo…</item>
- <item quantity="other">Ištrinama <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašų…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> nuotrauką?</item>
- <item quantity="few">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> nuotraukas?</item>
- <item quantity="many">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> nuotraukos?</item>
- <item quantity="other">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> nuotraukų?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one">Ištrinama <xliff:g id="COUNT">^1</xliff:g> nuotrauka…</item>
- <item quantity="few">Ištrinamos <xliff:g id="COUNT">^1</xliff:g> nuotraukos…</item>
- <item quantity="many">Ištrinama <xliff:g id="COUNT">^1</xliff:g> nuotraukos…</item>
- <item quantity="other">Ištrinama <xliff:g id="COUNT">^1</xliff:g> nuotraukų…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> elementą?</item>
- <item quantity="few">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> elementus?</item>
- <item quantity="many">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> elemento?</item>
- <item quantity="other">Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> elementų?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one">Ištrinamas <xliff:g id="COUNT">^1</xliff:g> elementas…</item>
- <item quantity="few">Ištrinami <xliff:g id="COUNT">^1</xliff:g> elementai…</item>
- <item quantity="many">Ištrinama <xliff:g id="COUNT">^1</xliff:g> elemento…</item>
- <item quantity="other">Ištrinama <xliff:g id="COUNT">^1</xliff:g> elementų…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Pridėti"</string>
+ <string name="deselect" msgid="4297825044827769490">"Panaikinti pasirinkimą"</string>
+ <string name="deselected" msgid="8488133193326208475">"Pasirinkimas panaikintas"</string>
+ <string name="select" msgid="2704765470563027689">"Pasirinkti"</string>
+ <string name="selected" msgid="9151797369975828124">"Pasirinkta"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Pasirinkite iki <xliff:g id="COUNT_0">^1</xliff:g> elemento}one{Pasirinkite iki <xliff:g id="COUNT_1">^1</xliff:g> elemento}few{Pasirinkite iki <xliff:g id="COUNT_1">^1</xliff:g> elementų}many{Pasirinkite iki <xliff:g id="COUNT_1">^1</xliff:g> elemento}other{Pasirinkite iki <xliff:g id="COUNT_1">^1</xliff:g> elementų}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Naujausios"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Nėra jokių nuotraukų ar vaizdo įrašų"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Nėra jokių albumų"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Žiūrėti pasirinktus"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Nuotraukos"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albumai"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Peržiūra"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Perjungti į darbo profilį"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Perjungti į asmeninį profilį"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Užblokavo jūsų administratorius"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Neleidžiama pasiekti darbo duomenų iš asmeninės programos"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Neleidžiama pasiekti asmens duomenų iš darbo programos"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Darbo programos pristabdytos"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Norėdami atidaryti darbo nuotraukas, įjunkite darbo programas ir bandykite dar kartą"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Ši programa gali pasiekti tik jūsų pasirinktas nuotraukas"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> elementas}one{<xliff:g id="COUNT_1">^1</xliff:g> elementas}few{<xliff:g id="COUNT_1">^1</xliff:g> elementai}many{<xliff:g id="COUNT_1">^1</xliff:g> elemento}other{<xliff:g id="COUNT_1">^1</xliff:g> elementų}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Pridėti (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Vaizdo kamera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Atsisiuntimai"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Mėgstamiausi"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Ekrano kopijos"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Judančioji nuotrauka"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> užfiksuota <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Vaizdo įrašas sukurtas <xliff:g id="TIME">%1$s</xliff:g>, trukmė: <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Nuotrauka"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Judančioji nuotrauka"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Nutildyti vaizdo įrašą"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Įjungti vaizdo įrašo garsą"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Leisti vaizdo įrašą"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Pristabdyti vaizdo įrašą"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Debesyje esanti medija dabar pasiekiama iš „<xliff:g id="PKG_NAME">%1$s</xliff:g>“"</string>
+ <string name="not_selected" msgid="2244008151669896758">"nepasirinkta"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Leisti programai „<xliff:g id="APP_NAME_0">^1</xliff:g>“ keisti šį garso failą?}one{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> garso failą?}few{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> garso failus?}many{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> garso failo?}other{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> garso failų?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Keičiamas garso failas…}one{Keičiamas <xliff:g id="COUNT">^1</xliff:g> garso failas…}few{Keičiami <xliff:g id="COUNT">^1</xliff:g> garso failai…}many{Keičiama <xliff:g id="COUNT">^1</xliff:g> garso failo…}other{Keičiama <xliff:g id="COUNT">^1</xliff:g> garso failų…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Leisti programai „<xliff:g id="APP_NAME_0">^1</xliff:g>“ keisti šį vaizdo įrašą?}one{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašą?}few{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašus?}many{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašo?}other{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašų?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Keičiamas vaizdo įrašas…}one{Keičiamas <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašas…}few{Keičiami <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašai…}many{Keičiama <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašo…}other{Keičiama <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašų…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Leisti programai „<xliff:g id="APP_NAME_0">^1</xliff:g>“ keisti šią nuotrauką?}one{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> nuotrauką?}few{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> nuotraukas?}many{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> nuotraukos?}other{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> nuotraukų?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Keičiama nuotrauka…}one{Keičiama <xliff:g id="COUNT">^1</xliff:g> nuotrauka…}few{Keičiamos <xliff:g id="COUNT">^1</xliff:g> nuotraukos…}many{Keičiama <xliff:g id="COUNT">^1</xliff:g> nuotraukos…}other{Keičiama <xliff:g id="COUNT">^1</xliff:g> nuotraukų…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Leisti programai „<xliff:g id="APP_NAME_0">^1</xliff:g>“ keisti šį elementą?}one{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> elementą?}few{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> elementus?}many{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> elemento?}other{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ keisti <xliff:g id="COUNT">^2</xliff:g> elementų?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Keičiamas elementas…}one{Keičiamas <xliff:g id="COUNT">^1</xliff:g> elementas…}few{Keičiami <xliff:g id="COUNT">^1</xliff:g> elementai…}many{Keičiama <xliff:g id="COUNT">^1</xliff:g> elemento…}other{Keičiama <xliff:g id="COUNT">^1</xliff:g> elementų…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Leisti programai „<xliff:g id="APP_NAME_0">^1</xliff:g>“ perkelti šį garso failą į šiukšliadėžę?}one{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> garso failą į šiukšliadėžę?}few{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> garso failus į šiukšliadėžę?}many{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> garso failo į šiukšliadėžę?}other{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> garso failų į šiukšliadėžę?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Perkeliamas garso failas į šiukšliadėžę…}one{Perkeliamas <xliff:g id="COUNT">^1</xliff:g> garso failas į šiukšliadėžę…}few{Perkeliami <xliff:g id="COUNT">^1</xliff:g> garso failai į šiukšliadėžę…}many{Perkeliama <xliff:g id="COUNT">^1</xliff:g> garso failo į šiukšliadėžę…}other{Perkeliama <xliff:g id="COUNT">^1</xliff:g> garso failų į šiukšliadėžę…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Leisti programai „<xliff:g id="APP_NAME_0">^1</xliff:g>“ perkelti šį vaizdo įrašą į šiukšliadėžę?}one{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašą į šiukšliadėžę?}few{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašus į šiukšliadėžę?}many{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašo į šiukšliadėžę?}other{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašų į šiukšliadėžę?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Perkeliamas vaizdo įrašas į šiukšliadėžę…}one{Perkeliamas <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašas į šiukšliadėžę…}few{Perkeliami <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašai į šiukšliadėžę…}many{Perkeliama <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašo į šiukšliadėžę…}other{Perkeliama <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašų į šiukšliadėžę…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Leisti programai „<xliff:g id="APP_NAME_0">^1</xliff:g>“ perkelti šią nuotrauką į šiukšliadėžę?}one{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> nuotrauką į šiukšliadėžę?}few{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> nuotraukas į šiukšliadėžę?}many{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> nuotraukos į šiukšliadėžę?}other{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> nuotraukų į šiukšliadėžę?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Perkeliama nuotrauka į šiukšliadėžę…}one{Perkeliama <xliff:g id="COUNT">^1</xliff:g> nuotrauka į šiukšliadėžę…}few{Perkeliamos <xliff:g id="COUNT">^1</xliff:g> nuotraukos į šiukšliadėžę…}many{Perkeliama <xliff:g id="COUNT">^1</xliff:g> nuotraukos į šiukšliadėžę…}other{Perkeliama <xliff:g id="COUNT">^1</xliff:g> nuotraukų į šiukšliadėžę…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Leisti programai „<xliff:g id="APP_NAME_0">^1</xliff:g>“ perkelti šį elementą į šiukšliadėžę?}one{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> elementą į šiukšliadėžę?}few{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> elementus į šiukšliadėžę?}many{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> elemento į šiukšliadėžę?}other{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> elementų į šiukšliadėžę?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Perkeliama elementas į šiukšliadėžę…}one{Perkeliamas <xliff:g id="COUNT">^1</xliff:g> elementas į šiukšliadėžę…}few{Perkeliami <xliff:g id="COUNT">^1</xliff:g> elementai į šiukšliadėžę…}many{Perkeliama <xliff:g id="COUNT">^1</xliff:g> elemento į šiukšliadėžę…}other{Perkeliama <xliff:g id="COUNT">^1</xliff:g> elementų į šiukšliadėžę…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Leisti programai „<xliff:g id="APP_NAME_0">^1</xliff:g>“ perkelti šį garso failą iš šiukšliadėžės?}one{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> garso failą iš šiukšliadėžės?}few{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> garso failus iš šiukšliadėžės?}many{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> garso failo iš šiukšliadėžės?}other{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> garso failų iš šiukšliadėžės?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Perkeliamas garso failas iš šiukšliadėžės…}one{Perkeliamas <xliff:g id="COUNT">^1</xliff:g> garso failas iš šiukšliadėžės…}few{Perkeliami <xliff:g id="COUNT">^1</xliff:g> garso failai iš šiukšliadėžės…}many{Perkeliama <xliff:g id="COUNT">^1</xliff:g> garso failo iš šiukšliadėžės…}other{Perkeliama <xliff:g id="COUNT">^1</xliff:g> garso failų iš šiukšliadėžės…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Leisti programai „<xliff:g id="APP_NAME_0">^1</xliff:g>“ perkelti šį vaizdo įrašą iš šiukšliadėžės?}one{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašą iš šiukšliadėžės?}few{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašus iš šiukšliadėžės?}many{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašo iš šiukšliadėžės?}other{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašų iš šiukšliadėžės?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Perkeliamas vaizdo įrašas iš šiukšliadėžės…}one{Perkeliamas <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašas iš šiukšliadėžės…}few{Perkeliami <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašai iš šiukšliadėžės…}many{Perkeliama <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašo iš šiukšliadėžės…}other{Perkeliama <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašų iš šiukšliadėžės…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Leisti programai „<xliff:g id="APP_NAME_0">^1</xliff:g>“ perkelti šią nuotrauką iš šiukšliadėžės?}one{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> nuotrauką iš šiukšliadėžės?}few{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> nuotraukas iš šiukšliadėžės?}many{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> nuotraukos iš šiukšliadėžės?}other{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> nuotraukų iš šiukšliadėžės?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Perkeliama nuotrauka iš šiukšliadėžės…}one{Perkeliama <xliff:g id="COUNT">^1</xliff:g> nuotrauka iš šiukšliadėžės…}few{Perkeliamos <xliff:g id="COUNT">^1</xliff:g> nuotraukos iš šiukšliadėžės…}many{Perkeliama <xliff:g id="COUNT">^1</xliff:g> nuotraukos iš šiukšliadėžės…}other{Perkeliama <xliff:g id="COUNT">^1</xliff:g> nuotraukų iš šiukšliadėžės…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Leisti programai „<xliff:g id="APP_NAME_0">^1</xliff:g>“ perkelti šį elementą iš šiukšliadėžės?}one{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> elementą iš šiukšliadėžės?}few{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> elementus iš šiukšliadėžės?}many{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> elemento iš šiukšliadėžės?}other{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ perkelti <xliff:g id="COUNT">^2</xliff:g> elementų iš šiukšliadėžės?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Perkeliamas elementas iš šiukšliadėžės…}one{Perkeliamas <xliff:g id="COUNT">^1</xliff:g> elementas iš šiukšliadėžės…}few{Perkeliami <xliff:g id="COUNT">^1</xliff:g> elementai iš šiukšliadėžės…}many{Perkeliama <xliff:g id="COUNT">^1</xliff:g> elemento iš šiukšliadėžės…}other{Perkeliama <xliff:g id="COUNT">^1</xliff:g> elementų iš šiukšliadėžės…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Leisti „<xliff:g id="APP_NAME_0">^1</xliff:g>“ ištrinti šį garso failą?}one{Leisti „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> garso failą?}few{Leisti „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> garso failus?}many{Leisti „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> garso failo?}other{Leisti „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> garso failų?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Ištrinamas garso failas…}one{Ištrinamas <xliff:g id="COUNT">^1</xliff:g> garso failas…}few{Ištrinami <xliff:g id="COUNT">^1</xliff:g> garso failai…}many{Ištrinama <xliff:g id="COUNT">^1</xliff:g> garso failo…}other{Ištrinama <xliff:g id="COUNT">^1</xliff:g> garso failų…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Leisti programai „<xliff:g id="APP_NAME_0">^1</xliff:g>“ ištrinti šį vaizdo įrašą?}one{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašą?}few{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašus?}many{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašo?}other{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> vaizdo įrašų?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Ištrinamas vaizdo įrašas…}one{Ištrinamas <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašas…}few{Ištrinami <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašai…}many{Ištrinama <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašo…}other{Ištrinama <xliff:g id="COUNT">^1</xliff:g> vaizdo įrašų…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Leisti programai „<xliff:g id="APP_NAME_0">^1</xliff:g>“ ištrinti šią nuotrauką?}one{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> nuotrauką?}few{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> nuotraukas?}many{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> nuotraukos?}other{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> nuotraukų?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Ištrinama nuotrauka…}one{Ištrinama <xliff:g id="COUNT">^1</xliff:g> nuotrauka…}few{Ištrinamos <xliff:g id="COUNT">^1</xliff:g> nuotraukos…}many{Ištrinama <xliff:g id="COUNT">^1</xliff:g> nuotraukos…}other{Ištrinama <xliff:g id="COUNT">^1</xliff:g> nuotraukų…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Leisti programai „<xliff:g id="APP_NAME_0">^1</xliff:g>“ ištrinti šį elementą?}one{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> elementą?}few{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> elementus?}many{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> elemento?}other{Leisti programai „<xliff:g id="APP_NAME_1">^1</xliff:g>“ ištrinti <xliff:g id="COUNT">^2</xliff:g> elementų?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Ištrinamas elementas…}one{Ištrinamas <xliff:g id="COUNT">^1</xliff:g> elementas…}few{Ištrinami <xliff:g id="COUNT">^1</xliff:g> elementai…}many{Ištrinama <xliff:g id="COUNT">^1</xliff:g> elemento…}other{Ištrinama <xliff:g id="COUNT">^1</xliff:g> elementų…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"Programa „<xliff:g id="APP_NAME">%s</xliff:g>“ negali apdoroti medijos failų"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Medija apdorojimas atšauktas"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Medija apdorojimo klaida"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index fbd2cba..da84a60 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Fotoattēlu paņēmējs"</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>
@@ -29,182 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Turpināt"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Atļaut"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Noraidīt"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="zero">vēl <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">vēl <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">vēl <xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="zero">Un vēl <xliff:g id="COUNT_1">^1</xliff:g> vienumu</item>
- <item quantity="one">Un vēl <xliff:g id="COUNT_1">^1</xliff:g> vienums</item>
- <item quantity="other">Un vēl <xliff:g id="COUNT_1">^1</xliff:g> vienumi</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{Vēl <xliff:g id="COUNT_0">^1</xliff:g>}zero{Vēl <xliff:g id="COUNT_1">^1</xliff:g>}one{Vēl <xliff:g id="COUNT_1">^1</xliff:g>}other{Vēl <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Un vēl <xliff:g id="COUNT_0">^1</xliff:g> vienums}zero{Un vēl <xliff:g id="COUNT_1">^1</xliff:g> vienumu}one{Un vēl <xliff:g id="COUNT_1">^1</xliff:g> vienums}other{Un vēl <xliff:g id="COUNT_1">^1</xliff:g> vienumi}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Lietotņu pagaidu failu notīrīšana"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"Lietotne <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vēlas iegūt atļauju notīrīt dažus pagaidu failus. Tas var palielināt akumulatora izmantošanas ilgumu vai mobilo datu lietojumu."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Notiek lietotņu pagaidu failu notīrīšana…"</string>
<string name="clear" msgid="5524638938415865915">"Notīrīt"</string>
<string name="allow" msgid="8885707816848569619">"Atļaut"</string>
<string name="deny" msgid="6040983710442068936">"Neatļaut"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="zero">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> modificēt <xliff:g id="COUNT">^2</xliff:g> audio failus?</item>
- <item quantity="one">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> modificēt <xliff:g id="COUNT">^2</xliff:g> audio failu?</item>
- <item quantity="other">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> modificēt <xliff:g id="COUNT">^2</xliff:g> audio failus?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="zero">Notiek <xliff:g id="COUNT">^1</xliff:g> audio failu pārveidošana…</item>
- <item quantity="one">Notiek <xliff:g id="COUNT">^1</xliff:g> audio faila pārveidošana…</item>
- <item quantity="other">Notiek <xliff:g id="COUNT">^1</xliff:g> audio failu pārveidošana…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="zero">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> modificēt <xliff:g id="COUNT">^2</xliff:g> videoklipus?</item>
- <item quantity="one">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> modificēt <xliff:g id="COUNT">^2</xliff:g> videoklipu?</item>
- <item quantity="other">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> modificēt <xliff:g id="COUNT">^2</xliff:g> videoklipus?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="zero">Notiek <xliff:g id="COUNT">^1</xliff:g> videoklipu pārveidošana…</item>
- <item quantity="one">Notiek <xliff:g id="COUNT">^1</xliff:g> videoklipa pārveidošana…</item>
- <item quantity="other">Notiek <xliff:g id="COUNT">^1</xliff:g> videoklipu pārveidošana…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="zero">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> modificēt <xliff:g id="COUNT">^2</xliff:g> fotoattēlus?</item>
- <item quantity="one">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> modificēt <xliff:g id="COUNT">^2</xliff:g> fotoattēlu?</item>
- <item quantity="other">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> modificēt <xliff:g id="COUNT">^2</xliff:g> fotoattēlus?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="zero">Notiek <xliff:g id="COUNT">^1</xliff:g> fotoattēlu pārveidošana…</item>
- <item quantity="one">Notiek <xliff:g id="COUNT">^1</xliff:g> fotoattēla pārveidošana…</item>
- <item quantity="other">Notiek <xliff:g id="COUNT">^1</xliff:g> fotoattēlu pārveidošana…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="zero">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> modificēt <xliff:g id="COUNT">^2</xliff:g> vienumus?</item>
- <item quantity="one">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> modificēt <xliff:g id="COUNT">^2</xliff:g> vienumu?</item>
- <item quantity="other">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> modificēt <xliff:g id="COUNT">^2</xliff:g> vienumus?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="zero">Notiek <xliff:g id="COUNT">^1</xliff:g> vienumu pārveidošana…</item>
- <item quantity="one">Notiek <xliff:g id="COUNT">^1</xliff:g> vienuma pārveidošana…</item>
- <item quantity="other">Notiek <xliff:g id="COUNT">^1</xliff:g> vienumu pārveidošana…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="zero">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārvietot <xliff:g id="COUNT">^2</xliff:g> audio failus uz atkritni?</item>
- <item quantity="one">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārvietot <xliff:g id="COUNT">^2</xliff:g> audio failu uz atkritni?</item>
- <item quantity="other">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārvietot <xliff:g id="COUNT">^2</xliff:g> audio failus uz atkritni?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="zero">Notiek <xliff:g id="COUNT">^1</xliff:g> audio failu pārvietošana uz atkritni…</item>
- <item quantity="one">Notiek <xliff:g id="COUNT">^1</xliff:g> audio faila pārvietošana uz atkritni…</item>
- <item quantity="other">Notiek <xliff:g id="COUNT">^1</xliff:g> audio failu pārvietošana uz atkritni…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="zero">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārvietot <xliff:g id="COUNT">^2</xliff:g> videoklipus uz atkritni?</item>
- <item quantity="one">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārvietot <xliff:g id="COUNT">^2</xliff:g> videoklipu uz atkritni?</item>
- <item quantity="other">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārvietot <xliff:g id="COUNT">^2</xliff:g> videoklipus uz atkritni?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="zero">Notiek <xliff:g id="COUNT">^1</xliff:g> videoklipu pārvietošana uz atkritni…</item>
- <item quantity="one">Notiek <xliff:g id="COUNT">^1</xliff:g> videoklipa pārvietošana uz atkritni…</item>
- <item quantity="other">Notiek <xliff:g id="COUNT">^1</xliff:g> videoklipu pārvietošana uz atkritni…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="zero">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārvietot <xliff:g id="COUNT">^2</xliff:g> fotoattēlus uz atkritni?</item>
- <item quantity="one">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārvietot <xliff:g id="COUNT">^2</xliff:g> fotoattēlu uz atkritni?</item>
- <item quantity="other">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārvietot <xliff:g id="COUNT">^2</xliff:g> fotoattēlus uz atkritni?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="zero">Notiek <xliff:g id="COUNT">^1</xliff:g> fotoattēlu pārvietošana uz atkritni…</item>
- <item quantity="one">Notiek <xliff:g id="COUNT">^1</xliff:g> fotoattēla pārvietošana uz atkritni…</item>
- <item quantity="other">Notiek <xliff:g id="COUNT">^1</xliff:g> fotoattēlu pārvietošana uz atkritni…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="zero">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārvietot <xliff:g id="COUNT">^2</xliff:g> vienumus uz atkritni?</item>
- <item quantity="one">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārvietot <xliff:g id="COUNT">^2</xliff:g> vienumu uz atkritni?</item>
- <item quantity="other">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārvietot <xliff:g id="COUNT">^2</xliff:g> vienumus uz atkritni?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="zero">Notiek <xliff:g id="COUNT">^1</xliff:g> vienumu pārvietošana uz atkritni…</item>
- <item quantity="one">Notiek <xliff:g id="COUNT">^1</xliff:g> vienuma pārvietošana uz atkritni…</item>
- <item quantity="other">Notiek <xliff:g id="COUNT">^1</xliff:g> vienumu pārvietošana uz atkritni…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="zero">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> izņemt <xliff:g id="COUNT">^2</xliff:g> audio failus no atkritnes?</item>
- <item quantity="one">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> izņemt <xliff:g id="COUNT">^2</xliff:g> audio failu no atkritnes?</item>
- <item quantity="other">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> izņemt <xliff:g id="COUNT">^2</xliff:g> audio failus no atkritnes?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="zero">Notiek <xliff:g id="COUNT">^1</xliff:g> audio failu izņemšana no atkritnes…</item>
- <item quantity="one">Notiek <xliff:g id="COUNT">^1</xliff:g> audio faila izņemšana no atkritnes…</item>
- <item quantity="other">Notiek <xliff:g id="COUNT">^1</xliff:g> audio failu izņemšana no atkritnes…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="zero">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> izņemt <xliff:g id="COUNT">^2</xliff:g> videoklipus no atkritnes?</item>
- <item quantity="one">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> izņemt <xliff:g id="COUNT">^2</xliff:g> videoklipu no atkritnes?</item>
- <item quantity="other">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> izņemt <xliff:g id="COUNT">^2</xliff:g> videoklipus no atkritnes?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="zero">Notiek <xliff:g id="COUNT">^1</xliff:g> videoklipu izņemšana no atkritnes…</item>
- <item quantity="one">Notiek <xliff:g id="COUNT">^1</xliff:g> videoklipa izņemšana no atkritnes…</item>
- <item quantity="other">Notiek <xliff:g id="COUNT">^1</xliff:g> videoklipu izņemšana no atkritnes…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="zero">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> izņemt <xliff:g id="COUNT">^2</xliff:g> fotoattēlus no atkritnes?</item>
- <item quantity="one">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> izņemt <xliff:g id="COUNT">^2</xliff:g> fotoattēlu no atkritnes?</item>
- <item quantity="other">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> izņemt <xliff:g id="COUNT">^2</xliff:g> fotoattēlus no atkritnes?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="zero">Notiek <xliff:g id="COUNT">^1</xliff:g> fotoattēlu izņemšana no atkritnes…</item>
- <item quantity="one">Notiek <xliff:g id="COUNT">^1</xliff:g> fotoattēla izņemšana no atkritnes…</item>
- <item quantity="other">Notiek <xliff:g id="COUNT">^1</xliff:g> fotoattēlu izņemšana no atkritnes…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="zero">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> izņemt <xliff:g id="COUNT">^2</xliff:g> vienumus no atkritnes?</item>
- <item quantity="one">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> izņemt <xliff:g id="COUNT">^2</xliff:g> vienumu no atkritnes?</item>
- <item quantity="other">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> izņemt <xliff:g id="COUNT">^2</xliff:g> vienumus no atkritnes?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="zero">Notiek <xliff:g id="COUNT">^1</xliff:g> vienumu izņemšana no atkritnes…</item>
- <item quantity="one">Notiek <xliff:g id="COUNT">^1</xliff:g> vienuma izņemšana no atkritnes…</item>
- <item quantity="other">Notiek <xliff:g id="COUNT">^1</xliff:g> vienumu izņemšana no atkritnes…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="zero">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> dzēst <xliff:g id="COUNT">^2</xliff:g> audio failus?</item>
- <item quantity="one">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> dzēst <xliff:g id="COUNT">^2</xliff:g> audio failu?</item>
- <item quantity="other">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> dzēst <xliff:g id="COUNT">^2</xliff:g> audio failus?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="zero">Notiek <xliff:g id="COUNT">^1</xliff:g> audio failu dzēšana…</item>
- <item quantity="one">Notiek <xliff:g id="COUNT">^1</xliff:g> audio faila dzēšana…</item>
- <item quantity="other">Notiek <xliff:g id="COUNT">^1</xliff:g> audio failu dzēšana…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="zero">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> dzēst <xliff:g id="COUNT">^2</xliff:g> videoklipus?</item>
- <item quantity="one">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> dzēst <xliff:g id="COUNT">^2</xliff:g> videoklipu?</item>
- <item quantity="other">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> dzēst <xliff:g id="COUNT">^2</xliff:g> videoklipus?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="zero">Notiek <xliff:g id="COUNT">^1</xliff:g> videoklipu dzēšana…</item>
- <item quantity="one">Notiek <xliff:g id="COUNT">^1</xliff:g> videoklipa dzēšana…</item>
- <item quantity="other">Notiek <xliff:g id="COUNT">^1</xliff:g> videoklipu dzēšana…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="zero">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> dzēst <xliff:g id="COUNT">^2</xliff:g> fotoattēlus?</item>
- <item quantity="one">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> dzēst <xliff:g id="COUNT">^2</xliff:g> fotoattēlu?</item>
- <item quantity="other">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> dzēst <xliff:g id="COUNT">^2</xliff:g> fotoattēlus?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="zero">Notiek <xliff:g id="COUNT">^1</xliff:g> fotoattēlu dzēšana…</item>
- <item quantity="one">Notiek <xliff:g id="COUNT">^1</xliff:g> fotoattēla dzēšana…</item>
- <item quantity="other">Notiek <xliff:g id="COUNT">^1</xliff:g> fotoattēlu dzēšana…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="zero">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> dzēst <xliff:g id="COUNT">^2</xliff:g> vienumus?</item>
- <item quantity="one">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> dzēst <xliff:g id="COUNT">^2</xliff:g> vienumu?</item>
- <item quantity="other">Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> dzēst <xliff:g id="COUNT">^2</xliff:g> vienumus?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="zero">Notiek <xliff:g id="COUNT">^1</xliff:g> vienumu dzēšana…</item>
- <item quantity="one">Notiek <xliff:g id="COUNT">^1</xliff:g> vienuma dzēšana…</item>
- <item quantity="other">Notiek <xliff:g id="COUNT">^1</xliff:g> vienumu dzēšana…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Pievienot"</string>
+ <string name="deselect" msgid="4297825044827769490">"Noņemt atlasi"</string>
+ <string name="deselected" msgid="8488133193326208475">"Atlase noņemta"</string>
+ <string name="select" msgid="2704765470563027689">"Atlasīt"</string>
+ <string name="selected" msgid="9151797369975828124">"Atlasīts"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Atlasiet līdz pat <xliff:g id="COUNT_0">^1</xliff:g> vienumam}zero{Atlasiet līdz pat <xliff:g id="COUNT_1">^1</xliff:g> vienumiem}one{Atlasiet līdz pat <xliff:g id="COUNT_1">^1</xliff:g> vienumam}other{Atlasiet līdz pat <xliff:g id="COUNT_1">^1</xliff:g> vienumiem}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Jaunākie"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Nav fotoattēlu vai videoklipu"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Nav albumu"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Skatīt atlasīto"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Fotoattēli"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albumi"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Priekšskatījums"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Pārslēgties uz darba profilu"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Pārslēgties uz personīgo profilu"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Bloķējis jūsu administrators"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Piekļuve darba datiem no personīgās lietotnes nav atļauta."</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Piekļuve personīgajiem datiem no darba lietotnes nav atļauta."</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Darba lietotnes ir apturētas"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Lai atvērtu darba fotoattēlus, ieslēdziet savas darba lietotnes un pēc tam mēģiniet vēlreiz."</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Šī lietotne var piekļūt tikai jūsu atlasītajiem fotoattēliem."</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> vienums}zero{<xliff:g id="COUNT_1">^1</xliff:g> vienumu}one{<xliff:g id="COUNT_1">^1</xliff:g> vienums}other{<xliff:g id="COUNT_1">^1</xliff:g> vienumi}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Pievienot <xliff:g id="COUNT">^1</xliff:g>"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Lejupielādes"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Izlase"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Ekrānuzņēmumi"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Kustības fotoattēls"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g>; uzņemšanas laiks: <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Videoklipa uzņemšanas datums: <xliff:g id="TIME">%1$s</xliff:g>, videoklipa ilgums: <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Fotoattēls"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Kustības fotoattēls"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Izslēgt video skaņu"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Ieslēgt video skaņu"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Atskaņot videoklipu"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Apturēt videoklipa atskaņošanu"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Tagad mākoņa multivides saturs ir pieejams, izmantojot lietotni <xliff:g id="PKG_NAME">%1$s</xliff:g>."</string>
+ <string name="not_selected" msgid="2244008151669896758">"nav atlasīts"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Vai atļaut lietotnei <xliff:g id="APP_NAME_0">^1</xliff:g> pārveidot šo audio failu?}zero{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārveidot <xliff:g id="COUNT">^2</xliff:g> audio failus?}one{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārveidot <xliff:g id="COUNT">^2</xliff:g> audio failu?}other{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārveidot <xliff:g id="COUNT">^2</xliff:g> audio failus?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Notiek audio faila pārveidošana…}zero{Notiek <xliff:g id="COUNT">^1</xliff:g> audio failu pārveidošana…}one{Notiek <xliff:g id="COUNT">^1</xliff:g> audio faila pārveidošana…}other{Notiek <xliff:g id="COUNT">^1</xliff:g> audio failu pārveidošana…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Vai atļaut lietotnei <xliff:g id="APP_NAME_0">^1</xliff:g> pārveidot šo videoklipu?}zero{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārveidot <xliff:g id="COUNT">^2</xliff:g> videoklipus?}one{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārveidot <xliff:g id="COUNT">^2</xliff:g> videoklipu?}other{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārveidot <xliff:g id="COUNT">^2</xliff:g> videoklipus?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Notiek videoklipa pārveidošana…}zero{Notiek <xliff:g id="COUNT">^1</xliff:g> videoklipu pārveidošana…}one{Notiek <xliff:g id="COUNT">^1</xliff:g> videoklipa pārveidošana…}other{Notiek <xliff:g id="COUNT">^1</xliff:g> videoklipu pārveidošana…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Vai atļaut lietotnei <xliff:g id="APP_NAME_0">^1</xliff:g> pārveidot šo fotoattēlu?}zero{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārveidot <xliff:g id="COUNT">^2</xliff:g> fotoattēlus?}one{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārveidot <xliff:g id="COUNT">^2</xliff:g> fotoattēlu?}other{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārveidot <xliff:g id="COUNT">^2</xliff:g> fotoattēlus?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Notiek fotoattēla pārveidošana…}zero{Notiek <xliff:g id="COUNT">^1</xliff:g> fotoattēlu pārveidošana…}one{Notiek <xliff:g id="COUNT">^1</xliff:g> fotoattēla pārveidošana…}other{Notiek <xliff:g id="COUNT">^1</xliff:g> fotoattēlu pārveidošana…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Vai atļaut lietotnei <xliff:g id="APP_NAME_0">^1</xliff:g> pārveidot šo vienumu?}zero{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārveidot <xliff:g id="COUNT">^2</xliff:g> vienumus?}one{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārveidot <xliff:g id="COUNT">^2</xliff:g> vienumu?}other{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārveidot <xliff:g id="COUNT">^2</xliff:g> vienumus?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Notiek vienuma pārveidošana…}zero{Notiek <xliff:g id="COUNT">^1</xliff:g> vienumu pārveidošana…}one{Notiek <xliff:g id="COUNT">^1</xliff:g> vienuma pārveidošana…}other{Notiek <xliff:g id="COUNT">^1</xliff:g> vienumu pārveidošana…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Vai atļaut lietotnei <xliff:g id="APP_NAME_0">^1</xliff:g> pārvietot šo audio failu uz atkritni?}zero{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārvietot <xliff:g id="COUNT">^2</xliff:g> audio failus uz atkritni?}one{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārvietot <xliff:g id="COUNT">^2</xliff:g> audio failu uz atkritni?}other{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārvietot <xliff:g id="COUNT">^2</xliff:g> audio failus uz atkritni?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Notiek audio faila pārvietošana uz atkritni…}zero{Notiek <xliff:g id="COUNT">^1</xliff:g> audio failu pārvietošana uz atkritni…}one{Notiek <xliff:g id="COUNT">^1</xliff:g> audio faila pārvietošana uz atkritni…}other{Notiek <xliff:g id="COUNT">^1</xliff:g> audio failu pārvietošana uz atkritni…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Vai atļaut lietotnei <xliff:g id="APP_NAME_0">^1</xliff:g> pārvietot šo videoklipu uz atkritni?}zero{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārvietot <xliff:g id="COUNT">^2</xliff:g> videoklipus uz atkritni?}one{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārvietot <xliff:g id="COUNT">^2</xliff:g> videoklipu uz atkritni?}other{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārvietot <xliff:g id="COUNT">^2</xliff:g> videoklipus uz atkritni?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Notiek videoklipa pārvietošana uz atkritni…}zero{Notiek <xliff:g id="COUNT">^1</xliff:g> videoklipu pārvietošana uz atkritni…}one{Notiek <xliff:g id="COUNT">^1</xliff:g> videoklipa pārvietošana uz atkritni…}other{Notiek <xliff:g id="COUNT">^1</xliff:g> videoklipu pārvietošana uz atkritni…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Vai atļaut lietotnei <xliff:g id="APP_NAME_0">^1</xliff:g> pārvietot šo fotoattēlu uz atkritni?}zero{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārvietot <xliff:g id="COUNT">^2</xliff:g> fotoattēlus uz atkritni?}one{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārvietot <xliff:g id="COUNT">^2</xliff:g> fotoattēlu uz atkritni?}other{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārvietot <xliff:g id="COUNT">^2</xliff:g> fotoattēlus uz atkritni?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Notiek fotoattēla pārvietošana uz atkritni…}zero{Notiek <xliff:g id="COUNT">^1</xliff:g> fotoattēlu pārvietošana uz atkritni…}one{Notiek <xliff:g id="COUNT">^1</xliff:g> fotoattēla pārvietošana uz atkritni…}other{Notiek <xliff:g id="COUNT">^1</xliff:g> fotoattēlu pārvietošana uz atkritni…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Vai atļaut lietotnei <xliff:g id="APP_NAME_0">^1</xliff:g> pārvietot šo vienumu uz atkritni?}zero{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārvietot <xliff:g id="COUNT">^2</xliff:g> vienumus uz atkritni?}one{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārvietot <xliff:g id="COUNT">^2</xliff:g> vienumu uz atkritni?}other{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> pārvietot <xliff:g id="COUNT">^2</xliff:g> vienumus uz atkritni?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Notiek vienuma pārvietošana uz atkritni…}zero{Notiek <xliff:g id="COUNT">^1</xliff:g> vienumu pārvietošana uz atkritni…}one{Notiek <xliff:g id="COUNT">^1</xliff:g> vienuma pārvietošana uz atkritni…}other{Notiek <xliff:g id="COUNT">^1</xliff:g> vienumu pārvietošana uz atkritni…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Vai atļaut lietotnei <xliff:g id="APP_NAME_0">^1</xliff:g> izņemt šo audio failu no atkritnes?}zero{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> izņemt <xliff:g id="COUNT">^2</xliff:g> audio failus no atkritnes?}one{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> izņemt <xliff:g id="COUNT">^2</xliff:g> audio failu no atkritnes?}other{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> izņemt <xliff:g id="COUNT">^2</xliff:g> audio failus no atkritnes?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Notiek audio faila izņemšana no atkritnes…}zero{Notiek <xliff:g id="COUNT">^1</xliff:g> audio failu izņemšana no atkritnes…}one{Notiek <xliff:g id="COUNT">^1</xliff:g> audio faila izņemšana no atkritnes…}other{Notiek <xliff:g id="COUNT">^1</xliff:g> audio failu izņemšana no atkritnes…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Vai atļaut lietotnei <xliff:g id="APP_NAME_0">^1</xliff:g> izņemt šo videoklipu no atkritnes?}zero{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> izņemt <xliff:g id="COUNT">^2</xliff:g> videoklipus no atkritnes?}one{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> izņemt <xliff:g id="COUNT">^2</xliff:g> videoklipu no atkritnes?}other{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> izņemt <xliff:g id="COUNT">^2</xliff:g> videoklipus no atkritnes?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Notiek videoklipa izņemšana no atkritnes…}zero{Notiek <xliff:g id="COUNT">^1</xliff:g> videoklipu izņemšana no atkritnes…}one{Notiek <xliff:g id="COUNT">^1</xliff:g> videoklipa izņemšana no atkritnes…}other{Notiek <xliff:g id="COUNT">^1</xliff:g> videoklipu izņemšana no atkritnes…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Vai atļaut lietotnei <xliff:g id="APP_NAME_0">^1</xliff:g> izņemt šo fotoattēlu no atkritnes?}zero{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> izņemt <xliff:g id="COUNT">^2</xliff:g> fotoattēlus no atkritnes?}one{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> izņemt <xliff:g id="COUNT">^2</xliff:g> fotoattēlu no atkritnes?}other{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> izņemt <xliff:g id="COUNT">^2</xliff:g> fotoattēlus no atkritnes?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Notiek fotoattēla izņemšana no atkritnes…}zero{Notiek <xliff:g id="COUNT">^1</xliff:g> fotoattēlu izņemšana no atkritnes…}one{Notiek <xliff:g id="COUNT">^1</xliff:g> fotoattēla izņemšana no atkritnes…}other{Notiek <xliff:g id="COUNT">^1</xliff:g> fotoattēlu izņemšana no atkritnes…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Vai atļaut lietotnei <xliff:g id="APP_NAME_0">^1</xliff:g> izņemt šo vienumu no atkritnes?}zero{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> izņemt <xliff:g id="COUNT">^2</xliff:g> vienumus no atkritnes?}one{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> izņemt <xliff:g id="COUNT">^2</xliff:g> vienumu no atkritnes?}other{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> izņemt <xliff:g id="COUNT">^2</xliff:g> vienumus no atkritnes?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Notiek vienuma izņemšana no atkritnes…}zero{Notiek <xliff:g id="COUNT">^1</xliff:g> vienumu izņemšana no atkritnes…}one{Notiek <xliff:g id="COUNT">^1</xliff:g> vienuma izņemšana no atkritnes…}other{Notiek <xliff:g id="COUNT">^1</xliff:g> vienumu izņemšana no atkritnes…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Vai atļaut lietotnei <xliff:g id="APP_NAME_0">^1</xliff:g> dzēst šo audio failu?}zero{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> dzēst <xliff:g id="COUNT">^2</xliff:g> audio failus?}one{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> dzēst <xliff:g id="COUNT">^2</xliff:g> audio failu?}other{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> dzēst <xliff:g id="COUNT">^2</xliff:g> audio failus?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Notiek audio faila dzēšana…}zero{Notiek <xliff:g id="COUNT">^1</xliff:g> audio failu dzēšana…}one{Notiek <xliff:g id="COUNT">^1</xliff:g> audio faila dzēšana…}other{Notiek <xliff:g id="COUNT">^1</xliff:g> audio failu dzēšana…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Vai atļaut lietotnei <xliff:g id="APP_NAME_0">^1</xliff:g> dzēst šo videoklipu?}zero{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> dzēst <xliff:g id="COUNT">^2</xliff:g> videoklipus?}one{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> dzēst <xliff:g id="COUNT">^2</xliff:g> videoklipu?}other{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> dzēst <xliff:g id="COUNT">^2</xliff:g> videoklipus?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Notiek videoklipa dzēšana…}zero{Notiek <xliff:g id="COUNT">^1</xliff:g> videoklipu dzēšana…}one{Notiek <xliff:g id="COUNT">^1</xliff:g> videoklipa dzēšana…}other{Notiek <xliff:g id="COUNT">^1</xliff:g> videoklipu dzēšana…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Vai atļaut lietotnei <xliff:g id="APP_NAME_0">^1</xliff:g> dzēst šo fotoattēlu?}zero{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> dzēst <xliff:g id="COUNT">^2</xliff:g> fotoattēlus?}one{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> dzēst <xliff:g id="COUNT">^2</xliff:g> fotoattēlu?}other{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> dzēst <xliff:g id="COUNT">^2</xliff:g> fotoattēlus?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Notiek fotoattēla dzēšana…}zero{Notiek <xliff:g id="COUNT">^1</xliff:g> fotoattēlu dzēšana…}one{Notiek <xliff:g id="COUNT">^1</xliff:g> fotoattēla dzēšana…}other{Notiek <xliff:g id="COUNT">^1</xliff:g> fotoattēlu dzēšana…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Vai atļaut lietotnei <xliff:g id="APP_NAME_0">^1</xliff:g> dzēst šo vienumu?}zero{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> dzēst <xliff:g id="COUNT">^2</xliff:g> vienumus?}one{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> dzēst <xliff:g id="COUNT">^2</xliff:g> vienumu?}other{Vai atļaut lietotnei <xliff:g id="APP_NAME_1">^1</xliff:g> dzēst <xliff:g id="COUNT">^2</xliff:g> vienumus?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Notiek vienuma dzēšana…}zero{Notiek <xliff:g id="COUNT">^1</xliff:g> vienumu dzēšana…}one{Notiek <xliff:g id="COUNT">^1</xliff:g> vienuma dzēšana…}other{Notiek <xliff:g id="COUNT">^1</xliff:g> vienumu dzēšana…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> nevar apstrādāt multivides failus"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Multivides apstrāde ir atcelta."</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Radās multivides apstrādes kļūda."</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 3fad90a..ce83f1b 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Аудио-визуелни содржини"</string>
<string name="storage_description" msgid="4081716890357580107">"Локална меморија"</string>
<string name="app_label" msgid="9035307001052716210">"Капацитет за аудиовизуелни содржини"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"Избирач на фотографии"</string>
<string name="artist_label" msgid="8105600993099120273">"Изведувач"</string>
<string name="unknown" msgid="2059049215682829375">"Непознат"</string>
<string name="root_images" msgid="5861633549189045666">"Слики"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Продолжи"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Дозволи"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Одбиј"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">+ <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+ <xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">Плус <xliff:g id="COUNT_1">^1</xliff:g> дополнителна ставка</item>
- <item quantity="other">Плус <xliff:g id="COUNT_1">^1</xliff:g> дополнителни ставки</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+ <xliff:g id="COUNT_0">^1</xliff:g>}one{+ <xliff:g id="COUNT_1">^1</xliff:g>}other{+ <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Плус <xliff:g id="COUNT_0">^1</xliff:g> дополнителна ставка}one{Плус <xliff:g id="COUNT_1">^1</xliff:g> дополнителна ставка}other{Плус <xliff:g id="COUNT_1">^1</xliff:g> дополнителни ставки}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Избриши ги привремените датотеки на апликацијата"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> сака да избрише некои привремени датотеки. Поради тоа може да дојде до зголемено користење на батеријата или мобилниот интернет."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Се бришат привремените датотеки на апликацијата…"</string>
<string name="clear" msgid="5524638938415865915">"Избриши"</string>
<string name="allow" msgid="8885707816848569619">"Дозволи"</string>
<string name="deny" msgid="6040983710442068936">"Одбиј"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да измени <xliff:g id="COUNT">^2</xliff:g> аудиодатотека?</item>
- <item quantity="other">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да измени <xliff:g id="COUNT">^2</xliff:g> аудиодатотеки?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one">Се изменува <xliff:g id="COUNT">^1</xliff:g> аудиодатотека…</item>
- <item quantity="other">Се изменуваат <xliff:g id="COUNT">^1</xliff:g> аудиодатотеки…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да измени <xliff:g id="COUNT">^2</xliff:g> видео?</item>
- <item quantity="other">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да измени <xliff:g id="COUNT">^2</xliff:g> видеа?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one">Се изменува <xliff:g id="COUNT">^1</xliff:g> видео…</item>
- <item quantity="other">Се изменуваат <xliff:g id="COUNT">^1</xliff:g> видеа…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да измени <xliff:g id="COUNT">^2</xliff:g> фотографија?</item>
- <item quantity="other">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да измени <xliff:g id="COUNT">^2</xliff:g> фотографии?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one">Се изменува <xliff:g id="COUNT">^1</xliff:g> фотографија…</item>
- <item quantity="other">Се изменуваат <xliff:g id="COUNT">^1</xliff:g> фотографии…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да измени <xliff:g id="COUNT">^2</xliff:g> ставка?</item>
- <item quantity="other">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да измени <xliff:g id="COUNT">^2</xliff:g> ставки?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one">Се изменува <xliff:g id="COUNT">^1</xliff:g> ставка…</item>
- <item quantity="other">Се изменуваат <xliff:g id="COUNT">^1</xliff:g> ставки…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> аудиодатотека во корпата?</item>
- <item quantity="other">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> аудиодатотеки во корпата?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one">Се преместува <xliff:g id="COUNT">^1</xliff:g> аудиодатотека во корпата…</item>
- <item quantity="other">Се преместуваат <xliff:g id="COUNT">^1</xliff:g> аудиодатотеки во корпата…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> видео во корпата?</item>
- <item quantity="other">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> видеа во корпата?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one">Се преместува <xliff:g id="COUNT">^1</xliff:g> видео во корпата…</item>
- <item quantity="other">Се преместуваат <xliff:g id="COUNT">^1</xliff:g> видеа во корпата…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> фотографија во корпата?</item>
- <item quantity="other">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> фотографии во корпата?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one">Се преместува <xliff:g id="COUNT">^1</xliff:g> фотографија во корпата…</item>
- <item quantity="other">Се преместуваат <xliff:g id="COUNT">^1</xliff:g> фотографии во корпата…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> ставка во корпата?</item>
- <item quantity="other">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> ставки во корпата?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one">Се преместува <xliff:g id="COUNT">^1</xliff:g> во корпата…</item>
- <item quantity="other">Се преместуваат <xliff:g id="COUNT">^1</xliff:g> ставки во корпата…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да извади <xliff:g id="COUNT">^2</xliff:g> аудиодатотека од корпата?</item>
- <item quantity="other">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да извади <xliff:g id="COUNT">^2</xliff:g> аудиодатотеки од корпата?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one">Се вади <xliff:g id="COUNT">^1</xliff:g> аудиодатотека од корпата…</item>
- <item quantity="other">Се вадат <xliff:g id="COUNT">^1</xliff:g> аудиодатотеки од корпата…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да извади <xliff:g id="COUNT">^2</xliff:g> видео од корпата?</item>
- <item quantity="other">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да извади <xliff:g id="COUNT">^2</xliff:g> видеа од корпата?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one">Се вади <xliff:g id="COUNT">^1</xliff:g> видео од корпата…</item>
- <item quantity="other">Се вадат <xliff:g id="COUNT">^1</xliff:g> видеа од корпата…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да извади <xliff:g id="COUNT">^2</xliff:g> фотографија од корпата?</item>
- <item quantity="other">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да извади <xliff:g id="COUNT">^2</xliff:g> фотографии од корпата?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one">Се вади <xliff:g id="COUNT">^1</xliff:g> фотографија од корпата…</item>
- <item quantity="other">Се вадат <xliff:g id="COUNT">^1</xliff:g> фотографии од корпата…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да извади <xliff:g id="COUNT">^2</xliff:g> ставка од корпата?</item>
- <item quantity="other">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да извади <xliff:g id="COUNT">^2</xliff:g> ставки од корпата?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one">Се вади <xliff:g id="COUNT">^1</xliff:g> ставка од корпата…</item>
- <item quantity="other">Се вадат <xliff:g id="COUNT">^1</xliff:g> ставки од корпата…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да избрише <xliff:g id="COUNT">^2</xliff:g> аудиодатотека?</item>
- <item quantity="other">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да избрише <xliff:g id="COUNT">^2</xliff:g> аудиодатотеки?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one">Се брише <xliff:g id="COUNT">^1</xliff:g> аудиодатотека…</item>
- <item quantity="other">Се бришат <xliff:g id="COUNT">^1</xliff:g> аудиодатотеки…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да избрише <xliff:g id="COUNT">^2</xliff:g> видео?</item>
- <item quantity="other">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да избрише <xliff:g id="COUNT">^2</xliff:g> видеа?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one">Се брише <xliff:g id="COUNT">^1</xliff:g> видео…</item>
- <item quantity="other">Се бришат <xliff:g id="COUNT">^1</xliff:g> видеа…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да избрише <xliff:g id="COUNT">^2</xliff:g> фотографија?</item>
- <item quantity="other">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да избрише <xliff:g id="COUNT">^2</xliff:g> фотографии?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one">Се брише <xliff:g id="COUNT">^1</xliff:g> фотографија…</item>
- <item quantity="other">Се бришат <xliff:g id="COUNT">^1</xliff:g> фотографии…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да избрише <xliff:g id="COUNT">^2</xliff:g> ставка?</item>
- <item quantity="other">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да избрише <xliff:g id="COUNT">^2</xliff:g> ставки?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one">Се брише <xliff:g id="COUNT">^1</xliff:g> ставка…</item>
- <item quantity="other">Се бришат <xliff:g id="COUNT">^1</xliff:g> ставки…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Додај"</string>
+ <string name="deselect" msgid="4297825044827769490">"Поништи го изборот"</string>
+ <string name="deselected" msgid="8488133193326208475">"Изборот е поништен"</string>
+ <string name="select" msgid="2704765470563027689">"Избери"</string>
+ <string name="selected" msgid="9151797369975828124">"Избрано"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Изберете до <xliff:g id="COUNT_0">^1</xliff:g> ставка}one{Изберете до <xliff:g id="COUNT_1">^1</xliff:g> ставка}other{Изберете до <xliff:g id="COUNT_1">^1</xliff:g> ставки}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Неодамнешни"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Нема фотографии или видеа"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Нема албуми"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Прикажи ги избраните"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Фотографии"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Албуми"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Преглед"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Префрли на работен профил"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Префрли на личен профил"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Блокирано од администраторот"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Не е дозволено пристапување до работни податоци од лична апликација"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Не е дозволено пристапување до лични податоци од работна апликација"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Работните апликации се паузирани"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"За да ги отворите работните фотографии, вклучете ги работните апликации, па обидете се повторно"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Апликацијава може да пристапи само до фотографиите што ќе ги изберете"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> ставка}one{<xliff:g id="COUNT_1">^1</xliff:g> ставка}other{<xliff:g id="COUNT_1">^1</xliff:g> ставки}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Додај (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Камера"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Преземања"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Омилени"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Слики од екранот"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Подвижна фотографија"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> снимена на <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Видеото е снимено на <xliff:g id="TIME">%1$s</xliff:g> со траење од <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Фотографија"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Подвижна фотографија"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Исклучете звук на видео"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Вклучете звук на видео"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Пушти го видеото"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Паузирај го видеото"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Аудиовизуелните содржини во облак сега се достапни од <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"не е избрано"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Да се дозволи <xliff:g id="APP_NAME_0">^1</xliff:g> да ја измени аудиодатотекава?}one{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да измени <xliff:g id="COUNT">^2</xliff:g> аудиодатотека?}other{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да измени <xliff:g id="COUNT">^2</xliff:g> аудиодатотеки?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Се изменува аудиодатотеката…}one{Се изменуваат <xliff:g id="COUNT">^1</xliff:g> аудиодатотека…}other{Се изменуваат <xliff:g id="COUNT">^1</xliff:g> аудиодатотеки…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Да се дозволи <xliff:g id="APP_NAME_0">^1</xliff:g> да го измени видеово?}one{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да измени <xliff:g id="COUNT">^2</xliff:g> видео?}other{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да измени <xliff:g id="COUNT">^2</xliff:g> видеа?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Се изменува видеото…}one{Се изменуваат <xliff:g id="COUNT">^1</xliff:g> видео…}other{Се изменуваат <xliff:g id="COUNT">^1</xliff:g> видеа…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Да се дозволи <xliff:g id="APP_NAME_0">^1</xliff:g> да ја измени фотографијава?}one{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да измени <xliff:g id="COUNT">^2</xliff:g> фотографија?}other{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да измени <xliff:g id="COUNT">^2</xliff:g> фотографии?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Се изменува фотографијата…}one{Се изменуваат <xliff:g id="COUNT">^1</xliff:g> фотографија…}other{Се изменуваат <xliff:g id="COUNT">^1</xliff:g> фотографии…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Да се дозволи <xliff:g id="APP_NAME_0">^1</xliff:g> да ја измени ставкава?}one{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да измени <xliff:g id="COUNT">^2</xliff:g> ставка?}other{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да измени <xliff:g id="COUNT">^2</xliff:g> ставки?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Се изменува ставката…}one{Се изменуваат <xliff:g id="COUNT">^1</xliff:g> ставка…}other{Се изменуваат <xliff:g id="COUNT">^1</xliff:g> ставки…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Да се дозволи <xliff:g id="APP_NAME_0">^1</xliff:g> да ја премести аудиодатотекава во корпата?}one{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> аудиодатотека во корпата?}other{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> аудиодатотеки во корпата?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Се преместува аудиодатотеката во корпата…}one{Се преместуваат <xliff:g id="COUNT">^1</xliff:g> аудиодатотека во корпата…}other{Се преместуваат <xliff:g id="COUNT">^1</xliff:g> аудиодатотеки во корпата…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Да се дозволи <xliff:g id="APP_NAME_0">^1</xliff:g> да го премести видеово во корпата?}one{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> видео во корпата?}other{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> видеа во корпата?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Се преместува видеото во корпата…}one{Се преместуваат <xliff:g id="COUNT">^1</xliff:g> видео во корпата…}other{Се преместуваат <xliff:g id="COUNT">^1</xliff:g> видеа во корпата…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Да се дозволи <xliff:g id="APP_NAME_0">^1</xliff:g> да ја премести фотографијава во корпата?}one{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> фотографија во корпата?}other{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> фотографии во корпата?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Се преместува фотографијата во корпата…}one{Се преместуваат <xliff:g id="COUNT">^1</xliff:g> фотографија во корпата…}other{Се преместуваат <xliff:g id="COUNT">^1</xliff:g> фотографии во корпата…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Да се дозволи <xliff:g id="APP_NAME_0">^1</xliff:g> да ја премести ставкава во корпата?}one{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> ставка во корпата?}other{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да премести <xliff:g id="COUNT">^2</xliff:g> ставки во корпата?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Се преместува ставката во корпата…}one{Се преместуваат <xliff:g id="COUNT">^1</xliff:g> ставка во корпата…}other{Се преместуваат <xliff:g id="COUNT">^1</xliff:g> ставки во корпата…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Да се дозволи <xliff:g id="APP_NAME_0">^1</xliff:g> да ја извади аудиодатотекава од корпата?}one{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да извади <xliff:g id="COUNT">^2</xliff:g> аудиодатотека од корпата?}other{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да извади <xliff:g id="COUNT">^2</xliff:g> аудиодатотеки од корпата?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Се вади аудиодатотеката од корпата…}one{Се вадат <xliff:g id="COUNT">^1</xliff:g> аудиодатотека од корпата…}other{Се вадат <xliff:g id="COUNT">^1</xliff:g> аудиодатотеки од корпата…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Да се дозволи <xliff:g id="APP_NAME_0">^1</xliff:g> да го извади видеово од корпата?}one{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да извади <xliff:g id="COUNT">^2</xliff:g> видео од корпата?}other{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да извади <xliff:g id="COUNT">^2</xliff:g> видеа од корпата?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Се вади видеото од корпата…}one{Се вадат <xliff:g id="COUNT">^1</xliff:g> видео од корпата…}other{Се вадат <xliff:g id="COUNT">^1</xliff:g> видеа од корпата…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Да се дозволи <xliff:g id="APP_NAME_0">^1</xliff:g> да ја извади фотографијава од корпата?}one{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да извади <xliff:g id="COUNT">^2</xliff:g> фотографија од корпата?}other{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да извади <xliff:g id="COUNT">^2</xliff:g> фотографии од корпата?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Се вади фотографијата од корпата…}one{Се вадат <xliff:g id="COUNT">^1</xliff:g> фотографија од корпата…}other{Се вадат <xliff:g id="COUNT">^1</xliff:g> фотографии од корпата…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Да се дозволи <xliff:g id="APP_NAME_0">^1</xliff:g> да ја извади ставкава од корпата?}one{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да извади <xliff:g id="COUNT">^2</xliff:g> ставка од корпата?}other{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да извади <xliff:g id="COUNT">^2</xliff:g> ставки од корпата?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Се вади ставката од корпата…}one{Се вадат <xliff:g id="COUNT">^1</xliff:g> ставка од корпата…}other{Се вадат <xliff:g id="COUNT">^1</xliff:g> ставки од корпата…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Да се дозволи <xliff:g id="APP_NAME_0">^1</xliff:g> да ја избрише аудиодатотекава?}one{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да избрише <xliff:g id="COUNT">^2</xliff:g> аудиодатотека?}other{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да избрише <xliff:g id="COUNT">^2</xliff:g> аудиодатотеки?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Се брише аудиодатотеката…}one{Се бришат <xliff:g id="COUNT">^1</xliff:g> аудиодатотека…}other{Се бришат <xliff:g id="COUNT">^1</xliff:g> аудиодатотеки…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Да се дозволи <xliff:g id="APP_NAME_0">^1</xliff:g> да го избрише видеово?}one{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да избрише <xliff:g id="COUNT">^2</xliff:g> видео?}other{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да избрише <xliff:g id="COUNT">^2</xliff:g> видеа?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Се брише видеото…}one{Се бришат <xliff:g id="COUNT">^1</xliff:g> видео…}other{Се бришат <xliff:g id="COUNT">^1</xliff:g> видеа…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Да се дозволи <xliff:g id="APP_NAME_0">^1</xliff:g> да ја избрише фотографијава?}one{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да избрише <xliff:g id="COUNT">^2</xliff:g> фотографија?}other{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да избрише <xliff:g id="COUNT">^2</xliff:g> фотографии?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Се брише фотографијата…}one{Се бришат <xliff:g id="COUNT">^1</xliff:g> фотографија…}other{Се бришат <xliff:g id="COUNT">^1</xliff:g> фотографии…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Да се дозволи <xliff:g id="APP_NAME_0">^1</xliff:g> да ја избрише ставкава?}one{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да избрише <xliff:g id="COUNT">^2</xliff:g> ставка?}other{Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да избрише <xliff:g id="COUNT">^2</xliff:g> ставки?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Се брише ставката…}one{Се бришат <xliff:g id="COUNT">^1</xliff:g> ставка…}other{Се бришат <xliff:g id="COUNT">^1</xliff:g> ставки…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> не може да обработува датотеки со аудиовизуелни содржини"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Транскодирањето е откажано"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Грешка при транскодирање"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index d84f95c..814ff17 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"മീഡിയ"</string>
<string name="storage_description" msgid="4081716890357580107">"ലോക്കൽ സ്റ്റോറേജ്"</string>
<string name="app_label" msgid="9035307001052716210">"മീഡിയ സ്റ്റോറേജ്"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"ഫോട്ടോ പിക്കർ"</string>
<string name="artist_label" msgid="8105600993099120273">"ആർട്ടിസ്റ്റ്"</string>
<string name="unknown" msgid="2059049215682829375">"അജ്ഞാതം"</string>
<string name="root_images" msgid="5861633549189045666">"ചിത്രങ്ങൾ"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"തുടരുക"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"അനുവദിക്കുക"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"നിരസിക്കുക"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other"><xliff:g id="COUNT_1">^1</xliff:g> അധിക ഇനങ്ങൾ കൂടി</item>
- <item quantity="one"><xliff:g id="COUNT_0">^1</xliff:g> അധിക ഇനം കൂടി</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> അധിക ഇനം കൂടി}other{<xliff:g id="COUNT_1">^1</xliff:g> അധിക ഇനങ്ങൾ കൂടി}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"താൽക്കാലികമായ ആപ്പ് ഫയലുകൾ മായ്ക്കുക"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> എന്നതിന് കുറച്ച് താൽക്കാലിക ഫയലുകൾ മായ്ക്കണമെന്നുണ്ട്. ബാറ്ററിയുടെയോ സെല്ലുലാർ ഡാറ്റയുടെയോ വർദ്ധിച്ച ഉപയോഗത്തിന് ഇത് കാരണമായേക്കാം."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"താൽക്കാലികമായ ആപ്പ് ഫയലുകൾ മായ്ക്കുന്നു…"</string>
<string name="clear" msgid="5524638938415865915">"മായ്ക്കുക"</string>
<string name="allow" msgid="8885707816848569619">"അനുവദിക്കൂ"</string>
<string name="deny" msgid="6040983710442068936">"നിരസിക്കുക"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ഓഡിയോ ഫയലുകൾ പരിഷ്കരിക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- <item quantity="one">ഈ ഓഡിയോ ഫയൽ പരിഷ്കരിക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ഓഡിയോ ഫയലുകൾ പരിഷ്ക്കരിക്കുന്നു…</item>
- <item quantity="one">ഓഡിയോ ഫയൽ പരിഷ്ക്കരിക്കുന്നു…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> വീഡിയോകൾ പരിഷ്കരിക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- <item quantity="one">ഈ വീഡിയോ പരിഷ്കരിക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> വീഡിയോകൾ പരിഷ്ക്കരിക്കുന്നു…</item>
- <item quantity="one">വീഡിയോ പരിഷ്ക്കരിക്കുന്നു…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ഫോട്ടോകൾ പരിഷ്കരിക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- <item quantity="one">ഈ ഫോട്ടോ പരിഷ്കരിക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ഫോട്ടോകൾ പരിഷ്ക്കരിക്കുന്നു…</item>
- <item quantity="one">ഫോട്ടോ പരിഷ്ക്കരിക്കുന്നു…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ഇനങ്ങൾ പരിഷ്കരിക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- <item quantity="one">ഈ ഇനം പരിഷ്കരിക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ഇനങ്ങൾ പരിഷ്ക്കരിക്കുന്നു…</item>
- <item quantity="one">ഇനം പരിഷ്ക്കരിക്കുന്നു…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ഓഡിയോ ഫയലുകൾ ട്രാഷിലേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- <item quantity="one">ഈ ഓഡിയോ ഫയൽ ട്രാഷിലേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ഓഡിയോ ഫയലുകൾ ട്രാഷിലേക്ക് നീക്കുന്നു…</item>
- <item quantity="one">ഓഡിയോ ഫയൽ ട്രാഷിലേക്ക് നീക്കുന്നു…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> വീഡിയോകൾ ട്രാഷിലേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- <item quantity="one">ഈ വീഡിയോ ട്രാഷിലേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> വീഡിയോകൾ ട്രാഷിലേക്ക് നീക്കുന്നു…</item>
- <item quantity="one">വീഡിയോ ട്രാഷിലേക്ക് നീക്കുന്നു…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ഫോട്ടോകൾ ട്രാഷിലേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- <item quantity="one">ഈ ഫോട്ടോ ട്രാഷിലേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ഫോട്ടോകൾ ട്രാഷിലേക്ക് നീക്കുന്നു…</item>
- <item quantity="one">ഫോട്ടോ ട്രാഷിലേക്ക് നീക്കുന്നു…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ഇനങ്ങൾ ട്രാഷിലേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- <item quantity="one">ഈ ഇനം ട്രാഷിലേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ഇനങ്ങൾ ട്രാഷിലേക്ക് നീക്കുന്നു…</item>
- <item quantity="one">ഇനം ട്രാഷിലേക്ക് നീക്കുന്നു…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ഓഡിയോ ഫയലുകൾ ട്രാഷിന് പുറത്തേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- <item quantity="one">ഈ ഓഡിയോ ഫയൽ ട്രാഷിന് പുറത്തേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ഓഡിയോ ഫയലുകൾ ട്രാഷിലേക്ക് നീക്കുന്നു…</item>
- <item quantity="one">ഓഡിയോ ഫയൽ ട്രാഷിലേക്ക് നീക്കുന്നു…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> വീഡിയോകൾ ട്രാഷിന് പുറത്തേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- <item quantity="one">ഈ വീഡിയോ ട്രാഷിന് പുറത്തേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> വീഡിയോകൾ ട്രാഷിൽ നിന്ന് നീക്കുന്നു…</item>
- <item quantity="one">വീഡിയോ ട്രാഷിൽ നിന്ന് നീക്കുന്നു…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ഫോട്ടോകൾ ട്രാഷിന് പുറത്തേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- <item quantity="one">ഈ ഫോട്ടോ ട്രാഷിന് പുറത്തേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ഫോട്ടോകൾ ട്രാഷിൽ നിന്ന് നീക്കുന്നു…</item>
- <item quantity="one">ഫോട്ടോ ട്രാഷിൽ നിന്ന് നീക്കുന്നു…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ഇനങ്ങൾ ട്രാഷിന് പുറത്തേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- <item quantity="one">ഈ ഇനം ട്രാഷിന് പുറത്തേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ഇനങ്ങൾ ട്രാഷിൽ നിന്ന് നീക്കുന്നു…</item>
- <item quantity="one">ഇനം ട്രാഷിൽ നിന്ന് നീക്കുന്നു…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ഓഡിയോ ഫയലുകൾ ഇല്ലാതാക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- <item quantity="one">ഈ ഓഡിയോ ഫയൽ ഇല്ലാതാക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ഓഡിയോ ഫയലുകൾ ഇല്ലാതാക്കുന്നു…</item>
- <item quantity="one">ഓഡിയോ ഫയൽ ഇല്ലാതാക്കുന്നു…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> വീഡിയോകൾ ഇല്ലാതാക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- <item quantity="one">ഈ വീഡിയോ ഇല്ലാതാക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> വീഡിയോകൾ ഇല്ലാതാക്കുന്നു…</item>
- <item quantity="one">വീഡിയോ ഇല്ലാതാക്കുന്നു…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ഫോട്ടോകൾ ഇല്ലാതാക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- <item quantity="one">ഈ ഫോട്ടോ ഇല്ലാതാക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ഫോട്ടോകൾ ഇല്ലാതാക്കുന്നു…</item>
- <item quantity="one">ഫോട്ടോ ഇല്ലാതാക്കുന്നു…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ഇനങ്ങൾ ഇല്ലാതാക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- <item quantity="one">ഈ ഇനം ഇല്ലാതാക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ഇനങ്ങൾ ഇല്ലാതാക്കുന്നു…</item>
- <item quantity="one">ഇനം ഇല്ലാതാക്കുന്നു…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"ചേർക്കുക"</string>
+ <string name="deselect" msgid="4297825044827769490">"തിരഞ്ഞെടുത്തത് മാറ്റുക"</string>
+ <string name="deselected" msgid="8488133193326208475">"തിരഞ്ഞെടുത്തത് മാറ്റി"</string>
+ <string name="select" msgid="2704765470563027689">"തിരഞ്ഞെടുക്കുക"</string>
+ <string name="selected" msgid="9151797369975828124">"തിരഞ്ഞെടുത്തു"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> ഇനം വരെ തിരഞ്ഞെടുക്കുക}other{<xliff:g id="COUNT_1">^1</xliff:g> ഇനങ്ങൾ വരെ തിരഞ്ഞെടുക്കുക}}"</string>
+ <string name="recent" msgid="6694613584743207874">"അടുത്തിടെയുള്ളത്"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"ഫോട്ടോകളോ വീഡിയോകളോ ഇല്ല"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"ആൽബങ്ങളൊന്നുമില്ല"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"തിരഞ്ഞെടുത്തത് കാണുക"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"ഫോട്ടോകൾ"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"ആൽബങ്ങൾ"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"പ്രിവ്യു"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"ഔദ്യോഗിക പ്രൊഫൈലിലേക്ക് മാറുക"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"വ്യക്തിഗത പ്രൊഫൈലിലേക്ക് മാറുക"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"നിങ്ങളുടെ അഡ്മിൻ ബ്ലോക്ക് ചെയ്തിരിക്കുന്നു"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"വ്യക്തിപര ആപ്പിലൂടെ ഔദ്യോഗിക ഡാറ്റ ആക്സസ് ചെയ്യുന്നത് അനുവദനീയമല്ല"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"ഔദ്യോഗിക ആപ്പിലൂടെ വ്യക്തിപരമായ ഡാറ്റ ആക്സസ് ചെയ്യുന്നത് അനുവദനീയമല്ല"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"ഔദ്യോഗിക ആപ്പുകൾ തൽക്കാലികമായി നിർത്തിയിരിക്കുന്നു"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"ഔദ്യോഗിക ഫോട്ടോകൾ തുറക്കാൻ, നിങ്ങളുടെ ഔദ്യോഗിക ആപ്പുകൾ ഓണാക്കി വീണ്ടും ശ്രമിക്കുക"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"നിങ്ങൾ തിരഞ്ഞെടുത്ത ഫോട്ടോകൾ മാത്രമേ ഈ ആപ്പിന് ആക്സസ് ചെയ്യാനാകൂ"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> ഇനം}other{<xliff:g id="COUNT_1">^1</xliff:g> ഇനങ്ങൾ}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"(<xliff:g id="COUNT">^1</xliff:g>) ചേർക്കുക"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"ക്യാമറ"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"ഡൗൺലോഡുകൾ"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"പ്രിയപ്പെട്ടവ"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"സ്ക്രീൻഷോട്ടുകൾ"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"ചലിക്കുന്ന ഫോട്ടോ"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="TIME">%2$s</xliff:g>-ന് എടുത്ത <xliff:g id="ITEM_NAME">%1$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"<xliff:g id="TIME">%1$s</xliff:g>-ന് <xliff:g id="DURATION">%2$s</xliff:g> ദൈർഘ്യത്തിൽ എടുത്ത വീഡിയോ"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"ഫോട്ടോ"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"ചലിക്കുന്ന ഫോട്ടോ"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"വീഡിയോ മ്യൂട്ട് ചെയ്യുന്നു"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"വീഡിയോ അൺമ്യൂട്ട് ചെയ്യുന്നു"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"വീഡിയോ പ്ലേ ചെയ്യുക"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"വീഡിയോ താൽക്കാലികമായി നിർത്തുക"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"ഇപ്പോൾ <xliff:g id="PKG_NAME">%1$s</xliff:g> എന്നതിൽ നിന്ന് ക്ലൗഡ് മീഡിയ ലഭ്യമാണ്"</string>
+ <string name="not_selected" msgid="2244008151669896758">"തിരഞ്ഞെടുത്തിട്ടില്ല"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{ഈ ഓഡിയോ ഫയൽ പരിഷ്ക്കരിക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}other{<xliff:g id="COUNT">^2</xliff:g> ഓഡിയോ ഫയലുകൾ പരിഷ്ക്കരിക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{ഓഡിയോ ഫയൽ പരിഷ്ക്കരിക്കുന്നു…}other{<xliff:g id="COUNT">^1</xliff:g> ഓഡിയോ ഫയലുകൾ പരിഷ്ക്കരിക്കുന്നു…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{ഈ വീഡിയോ പരിഷ്ക്കരിക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}other{<xliff:g id="COUNT">^2</xliff:g> വീഡിയോകൾ പരിഷ്ക്കരിക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{വീഡിയോ പരിഷ്ക്കരിക്കുന്നു…}other{<xliff:g id="COUNT">^1</xliff:g> വീഡിയോകൾ പരിഷ്ക്കരിക്കുന്നു…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{ഈ ഫോട്ടോ പരിഷ്ക്കരിക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}other{<xliff:g id="COUNT">^2</xliff:g> ഫോട്ടോകൾ പരിഷ്ക്കരിക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{ഫോട്ടോ പരിഷ്ക്കരിക്കുന്നു…}other{<xliff:g id="COUNT">^1</xliff:g> ഫോട്ടോകൾ പരിഷ്ക്കരിക്കുന്നു…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{ഈ ഇനം പരിഷ്ക്കരിക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}other{<xliff:g id="COUNT">^2</xliff:g> ഇനങ്ങൾ പരിഷ്ക്കരിക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{ഇനം പരിഷ്ക്കരിക്കുന്നു…}other{<xliff:g id="COUNT">^1</xliff:g> ഇനങ്ങൾ പരിഷ്ക്കരിക്കുന്നു…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{ഈ ഓഡിയോ ഫയൽ ട്രാഷിലേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}other{<xliff:g id="COUNT">^2</xliff:g> ഓഡിയോ ഫയലുകൾ ട്രാഷിലേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{ഓഡിയോ ഫയൽ ട്രാഷിലേക്ക് നീക്കുന്നു…}other{<xliff:g id="COUNT">^1</xliff:g> ഓഡിയോ ഫയലുകൾ ട്രാഷിലേക്ക് നീക്കുന്നു…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{ഈ വീഡിയോ ട്രാഷിലേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}other{<xliff:g id="COUNT">^2</xliff:g> വീഡിയോകൾ ട്രാഷിലേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{വീഡിയോ ട്രാഷിലേക്ക് നീക്കുന്നു…}other{<xliff:g id="COUNT">^1</xliff:g> വീഡിയോകൾ ട്രാഷിലേക്ക് നീക്കുന്നു…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{ഈ ഫോട്ടോ ട്രാഷിലേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}other{<xliff:g id="COUNT">^2</xliff:g> ഫോട്ടോകൾ ട്രാഷിലേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{ഫോട്ടോ ട്രാഷിലേക്ക് നീക്കുന്നു…}other{<xliff:g id="COUNT">^1</xliff:g> ഫോട്ടോകൾ ട്രാഷിലേക്ക് നീക്കുന്നു…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{ഈ ഇനം ട്രാഷിലേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}other{<xliff:g id="COUNT">^2</xliff:g> ഇനങ്ങൾ ട്രാഷിലേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{ഇനം ട്രാഷിലേക്ക് നീക്കുന്നു…}other{<xliff:g id="COUNT">^1</xliff:g> ഇനങ്ങൾ ട്രാഷിലേക്ക് നീക്കുന്നു…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{ഈ ഓഡിയോ ഫയൽ ട്രാഷിന് പുറത്തേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}other{<xliff:g id="COUNT">^2</xliff:g> ഓഡിയോ ഫയലുകൾ ട്രാഷിന് പുറത്തേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{ഓഡിയോ ഫയൽ ട്രാഷിലേക്ക് നീക്കുന്നു…}other{<xliff:g id="COUNT">^1</xliff:g> ഓഡിയോ ഫയലുകൾ ട്രാഷിലേക്ക് നീക്കുന്നു…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{ഈ വീഡിയോ ട്രാഷിന് പുറത്തേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}other{<xliff:g id="COUNT">^2</xliff:g> വീഡിയോകൾ ട്രാഷിന് പുറത്തേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{വീഡിയോ ട്രാഷിൽ നിന്ന് നീക്കുന്നു…}other{<xliff:g id="COUNT">^1</xliff:g> വീഡിയോകൾ ട്രാഷിൽ നിന്ന് നീക്കുന്നു…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{ഈ ഫോട്ടോ ട്രാഷിന് പുറത്തേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}other{<xliff:g id="COUNT">^2</xliff:g> ഫോട്ടോകൾ ട്രാഷിന് പുറത്തേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{ഫോട്ടോ ട്രാഷിൽ നിന്ന് നീക്കുന്നു…}other{<xliff:g id="COUNT">^1</xliff:g> ഫോട്ടോകൾ ട്രാഷിൽ നിന്ന് നീക്കുന്നു…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{ഈ ഇനം ട്രാഷിന് പുറത്തേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}other{<xliff:g id="COUNT">^2</xliff:g> ഇനങ്ങൾ ട്രാഷിന് പുറത്തേക്ക് നീക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{ഇനം ട്രാഷിൽ നിന്ന് നീക്കുന്നു…}other{<xliff:g id="COUNT">^1</xliff:g> ഇനങ്ങൾ ട്രാഷിൽ നിന്ന് നീക്കുന്നു…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{ഈ ഓഡിയോ ഫയൽ ഇല്ലാതാക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}other{<xliff:g id="COUNT">^2</xliff:g> ഓഡിയോ ഫയലുകൾ ഇല്ലാതാക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{ഓഡിയോ ഫയൽ ഇല്ലാതാക്കുന്നു…}other{<xliff:g id="COUNT">^1</xliff:g> ഓഡിയോ ഫയലുകൾ ഇല്ലാതാക്കുന്നു…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{ഈ വീഡിയോ ഇല്ലാതാക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}other{<xliff:g id="COUNT">^2</xliff:g> വീഡിയോകൾ ഇല്ലാതാക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{വീഡിയോ ഇല്ലാതാക്കുന്നു…}other{<xliff:g id="COUNT">^1</xliff:g> വീഡിയോകൾ ഇല്ലാതാക്കുന്നു…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{ഈ ഫോട്ടോ ഇല്ലാതാക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}other{<xliff:g id="COUNT">^2</xliff:g> ഫോട്ടോകൾ ഇല്ലാതാക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{ഫോട്ടോ ഇല്ലാതാക്കുന്നു…}other{<xliff:g id="COUNT">^1</xliff:g> ഫോട്ടോകൾ ഇല്ലാതാക്കുന്നു…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{ഈ ഇനം ഇല്ലാതാക്കാൻ <xliff:g id="APP_NAME_0">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}other{<xliff:g id="COUNT">^2</xliff:g> ഇനങ്ങൾ ഇല്ലാതാക്കാൻ <xliff:g id="APP_NAME_1">^1</xliff:g> എന്നതിനെ അനുവദിക്കണോ?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{ഇനം ഇല്ലാതാക്കുന്നു…}other{<xliff:g id="COUNT">^1</xliff:g> ഇനങ്ങൾ ഇല്ലാതാക്കുന്നു…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> എന്നതിന് മീഡിയ ഫയലുകൾ പ്രോസസ് ചെയ്യാനാകില്ല"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"മീഡിയ പ്രോസസ് ചെയ്യൽ റദ്ദാക്കി"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"മീഡിയ പ്രോസസ് ചെയ്യുന്നതിൽ പിശക്"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 3b791d8..2b94d43 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Медиа"</string>
<string name="storage_description" msgid="4081716890357580107">"Дотоод сан"</string>
<string name="app_label" msgid="9035307001052716210">"Медиа санах ой"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"Зураг сонгогч"</string>
<string name="artist_label" msgid="8105600993099120273">"Уран бүтээлч"</string>
<string name="unknown" msgid="2059049215682829375">"Тодорхойгүй"</string>
<string name="root_images" msgid="5861633549189045666">"Зураг"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Үргэлжлүүлэх"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Зөвшөөрөх"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Татгалзах"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Нэмээд нэмэлт <xliff:g id="COUNT_1">^1</xliff:g> зүйл</item>
- <item quantity="one">Нэмээд нэмэлт <xliff:g id="COUNT_0">^1</xliff:g> зүйл</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Гадна нэмэлт <xliff:g id="COUNT_0">^1</xliff:g> зүйл}other{Гадна нэмэлт <xliff:g id="COUNT_1">^1</xliff:g> зүйл}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Аппын түр зуурын файлуудыг арилгах"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> нь түр зуурын зарим файлыг арилгах хүсэлтэй байна. Энэ нь батарей эсвэл мобайл дата ашиглалтыг нэмэгдүүлэхэд хүргэж болзошгүй."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Аппын түр зуурын файлуудыг арилгаж байна…"</string>
<string name="clear" msgid="5524638938415865915">"Арилгах"</string>
<string name="allow" msgid="8885707816848569619">"Зөвшөөрөх"</string>
<string name="deny" msgid="6040983710442068936">"Татгалзах"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> аудио файл өөрчлөхийг зөвшөөрөх үү?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ аудио файлыг өөрчлөхийг зөвшөөрөх үү?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> аудио файлыг өөрчилж байна…</item>
- <item quantity="one">Аудио файлыг өөрчилж байна…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> видео өөрчлөхийг зөвшөөрөх үү?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ видеог өөрчлөхийг зөвшөөрөх үү?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> видеог өөрчилж байна…</item>
- <item quantity="one">Видеог өөрчилж байна…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> зураг өөрчлөхийг зөвшөөрөх үү?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ зургийг өөрчлөхийг зөвшөөрөх үү?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> зургийг өөрчилж байна…</item>
- <item quantity="one">Зургийг өөрчилж байна…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> зүйл өөрчлөхийг зөвшөөрөх үү?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ зүйлийг өөрчлөхийг зөвшөөрөх үү?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> зүйлийг өөрчилж байна…</item>
- <item quantity="one">Зүйлийг өөрчилж байна…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> аудио файлыг хогийн сав руу зөөхийг зөвшөөрөх үү?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ аудио файлыг хогийн сав руу зөөхийг зөвшөөрөх үү?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> аудио файлыг хогийн сав руу зөөж байна…</item>
- <item quantity="one">Аудио файлыг хогийн сав руу зөөж байна…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> видеог хогийн сав руу зөөхийг зөвшөөрөх үү?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ видеог хогийн сав руу зөөхийг зөвшөөрөх үү?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> видеог хогийн сав руу зөөж байна…</item>
- <item quantity="one">Видеог хогийн сав руу зөөж байна…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> зургийг хогийн сав руу зөөхийг зөвшөөрөх үү?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ зургийг хогийн сав руу зөөхийг зөвшөөрөх үү?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> зургийг хогийн сав руу зөөж байна…</item>
- <item quantity="one">Зургийг хогийн сав руу зөөж байна…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-д хогийн сав руу <xliff:g id="COUNT">^2</xliff:g> зүйлийг зөөхийг зөвшөөрөх үү?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ зүйлийг хогийн сав руу зөөхийг зөвшөөрөх үү?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> зүйлийг хогийн сав руу зөөж байна…</item>
- <item quantity="one">Зүйлийг хогийн сав руу зөөж байна…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> аудио файлыг хогийн савнаас гаргахыг зөвшөөрөх үү?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ аудио файлыг хогийн савнаас гаргахыг зөвшөөрөх үү?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> аудио файлыг хогийн савнаас гадагш зөөж байна…</item>
- <item quantity="one">Аудио файлыг хогийн савнаас гадагш зөөж байна…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> видеог хогийн савнаас гаргахыг зөвшөөрөх үү?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ видеог хогийн савнаас гаргахыг зөвшөөрөх үү?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> видеог хогийн савнаас гадагш зөөж байна…</item>
- <item quantity="one">Видеог хогийн савнаас гадагш зөөж байна…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> зургийг хогийн савнаас гаргахыг зөвшөөрөх үү?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ зургийг хогийн савнаас гаргахыг зөвшөөрөх үү?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> зургийг хогийн савнаас гадагш зөөж байна…</item>
- <item quantity="one">Зургийг хогийн савнаас гадагш зөөж байна…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> зүйлийг хогийн савнаас гаргахыг зөвшөөрөх үү?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ зүйлийг хогийн савнаас гаргахыг зөвшөөрөх үү?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> зүйлийг хогийн савнаас гадагш зөөж байна…</item>
- <item quantity="one">Зүйлийг хогийн савнаас гадагш зөөж байна…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> аудио файл устгахыг зөвшөөрөх үү?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ аудио файлыг устгахыг зөвшөөрөх үү?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> аудио файлыг устгаж байна…</item>
- <item quantity="one">Аудио файлыг устгаж байна…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> видео устгахыг зөвшөөрөх үү?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ видеог устгахыг зөвшөөрөх үү?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> видеог устгаж байна…</item>
- <item quantity="one">Видеог устгаж байна…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> зураг устгахыг зөвшөөрөх үү?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ зургийг устгахыг зөвшөөрөх үү?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> зургийг устгаж байна…</item>
- <item quantity="one">Зургийг устгаж байна…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> зүйл устгахыг зөвшөөрөх үү?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ зүйлийг устгахыг зөвшөөрөх үү?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> зүйлийг устгаж байна…</item>
- <item quantity="one">Зүйлийг устгаж байна…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Нэмэх"</string>
+ <string name="deselect" msgid="4297825044827769490">"Сонголтыг цуцлах"</string>
+ <string name="deselected" msgid="8488133193326208475">"Сонголтыг цуцалсан"</string>
+ <string name="select" msgid="2704765470563027689">"Сонгох"</string>
+ <string name="selected" msgid="9151797369975828124">"Сонгосон"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> хүртэлх зүйл сонгоно уу}other{<xliff:g id="COUNT_1">^1</xliff:g> хүртэлх зүйл сонгоно уу}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Саяхны"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Зураг эсвэл видео алга"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Цомог алга"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Сонгосныг харах"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Зураг"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Цомог"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Урьдчилан үзэх"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Ажлын профайл руу сэлгэх"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Хувийн профайл руу сэлгэх"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Танай админ блоклосон"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Хувийн аппаас ажлын өгөгдөлд хандахыг зөвшөөрдөггүй"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Ажлын аппаас хувийн өгөгдөлд хандахыг зөвшөөрдөггүй"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Ажлын аппуудыг түр зогсоосон"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Ажлын зургуудаа нээхийн тулд ажлын аппуудaa асааж, дараа нь дахин оролдоно уу"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Энэ апп зөвхөн таны сонгосон зургуудад хандах боломжтой"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> зүйл}other{<xliff:g id="COUNT_1">^1</xliff:g> зүйл}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Нэмэх (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Камер"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Таталтууд"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Дуртай зүйлс"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Дэлгэцийн агшин"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Хөдөлгөөнт зураг"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g>-г <xliff:g id="TIME">%2$s</xliff:g>-д авсан"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"<xliff:g id="TIME">%1$s</xliff:g>-д авсан, <xliff:g id="DURATION">%2$s</xliff:g>-н үргэлжлэх хугацаатай видео"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Зураг"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Хөдөлгөөнт зураг"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Видеоны дууг хаах"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Видеоны дууг нээх"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Видео тоглуулах"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Видеог түр зогсоох"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Үүлэн медиаг одоо <xliff:g id="PKG_NAME">%1$s</xliff:g>-с авах боломжтой боллоо"</string>
+ <string name="not_selected" msgid="2244008151669896758">"сонгоогүй"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ аудио файлыг өөрчлөхийг зөвшөөрөх үү?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> аудио файлыг өөрчлөхийг зөвшөөрөх үү?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Аудио файлыг өөрчилж байна…}other{<xliff:g id="COUNT">^1</xliff:g> аудио файлыг өөрчилж байна…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ видеог өөрчлөхийг зөвшөөрөх үү?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> видеог өөрчлөхийг зөвшөөрөх үү?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Видеог өөрчилж байна…}other{<xliff:g id="COUNT">^1</xliff:g> видеог өөрчилж байна…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ зургийг өөрчлөхийг зөвшөөрөх үү?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> зургийг өөрчлөхийг зөвшөөрөх үү?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Зургийг өөрчилж байна…}other{<xliff:g id="COUNT">^1</xliff:g> зургийг өөрчилж байна…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ зүйлийг өөрчлөхийг зөвшөөрөх үү?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> зүйлийг өөрчлөхийг зөвшөөрөх үү?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Зүйлийг өөрчилж байна…}other{<xliff:g id="COUNT">^1</xliff:g> зүйлийг өөрчилж байна…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ аудио файлыг хогийн сав руу зөөхийг зөвшөөрөх үү?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> аудио файлыг хогийн сав руу зөөхийг зөвшөөрөх үү?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Аудио файлыг хогийн сав руу зөөж байна…}other{<xliff:g id="COUNT">^1</xliff:g> аудио файлыг хогийн сав руу зөөж байна…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ видеог хогийн сав руу зөөхийг зөвшөөрөх үү?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> видеог хогийн сав руу зөөхийг зөвшөөрөх үү?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Видеог хогийн сав руу зөөж байна…}other{<xliff:g id="COUNT">^1</xliff:g> видеог хогийн сав руу зөөж байна…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ зургийг хогийн сав руу зөөхийг зөвшөөрөх үү?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> зургийг хогийн сав руу зөөхийг зөвшөөрөх үү?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Зургийг хогийн сав руу зөөж байна…}other{<xliff:g id="COUNT">^1</xliff:g> зургийг хогийн сав руу зөөж байна…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ зүйлийг хогийн сав руу зөөхийг зөвшөөрөх үү?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> зүйлийг хогийн сав руу зөөхийг зөвшөөрөх үү?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Зүйлийг хогийн сав руу зөөж байна…}other{<xliff:g id="COUNT">^1</xliff:g> зүйлийг хогийн сав руу зөөж байна…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ аудио файлыг хогийн савнаас гадагш зөөхийг зөвшөөрөх үү?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> аудио файлыг хогийн савнаас гадагш зөөхийг зөвшөөрөх үү?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Аудио файлыг хогийн савнаас гадагш зөөж байна…}other{<xliff:g id="COUNT">^1</xliff:g> аудио файлыг хогийн савнаас гадагш зөөж байна…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ видеог хогийн савнаас гадагш зөөхийг зөвшөөрөх үү?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> видеог хогийн савнаас гадагш зөөхийг зөвшөөрөх үү?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Видеог хогийн савнаас гадагш зөөж байна…}other{<xliff:g id="COUNT">^1</xliff:g> видеог хогийн савнаас гадагш зөөж байна…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ зургийг хогийн савнаас гадагш зөөхийг зөвшөөрөх үү?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> зургийг хогийн савнаас гадагш зөөхийг зөвшөөрөх үү?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Зургийг хогийн савнаас гадагш зөөж байна…}other{<xliff:g id="COUNT">^1</xliff:g> зургийг хогийн савнаас гадагш зөөж байна…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ зүйлийг хогийн савнаас гадагш зөөхийг зөвшөөрөх үү?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> зүйлийг хогийн савнаас гадагш зөөхийг зөвшөөрөх үү?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Зүйлийг хогийн савнаас гадагш зөөж байна…}other{<xliff:g id="COUNT">^1</xliff:g> зүйлийг хогийн савнаас гадагш зөөж байна…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ аудио файлыг устгахыг зөвшөөрөх үү?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> аудио файлыг устгахыг зөвшөөрөх үү?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Аудио файлыг устгаж байна…}other{<xliff:g id="COUNT">^1</xliff:g> аудио файлыг устгаж байна…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ видеог устгахыг зөвшөөрөх үү?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> видеог устгахыг зөвшөөрөх үү?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Видеог устгаж байна…}other{<xliff:g id="COUNT">^1</xliff:g> видеог устгаж байна…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ зургийг устгахыг зөвшөөрөх үү?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> зургийг устгахыг зөвшөөрөх үү?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Зургийг устгаж байна…}other{<xliff:g id="COUNT">^1</xliff:g> зургийг устгаж байна…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g>-д энэ зүйлийг устгахыг зөвшөөрөх үү?}other{<xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g id="COUNT">^2</xliff:g> зүйлийг устгахыг зөвшөөрөх үү?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Зүйлийг устгаж байна…}other{<xliff:g id="COUNT">^1</xliff:g> зүйлийг устгаж байна…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> медиа файлуудыг боловсруулах боломжгүй"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Медиагийн боловсруулалтыг цуцалсан"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Медиаг боловсруулахад алдаа гарлаа"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 308795f..6dd793e 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"मीडिया"</string>
<string name="storage_description" msgid="4081716890357580107">"स्थानिक स्टोरेज"</string>
<string name="app_label" msgid="9035307001052716210">"मीडिया स्टोरेज"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"फोटो पिकर"</string>
<string name="artist_label" msgid="8105600993099120273">"कलाकार"</string>
<string name="unknown" msgid="2059049215682829375">"अज्ञात"</string>
<string name="root_images" msgid="5861633549189045666">"इमेज"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"सुरू ठेवा"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"अनुमती द्या"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"नाकारा"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">आणखी <xliff:g id="COUNT_1">^1</xliff:g> अतिरिक्त आयटम</item>
- <item quantity="one">आणखी <xliff:g id="COUNT_0">^1</xliff:g> अतिरिक्त आयटम</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{आणखी <xliff:g id="COUNT_0">^1</xliff:g> अतिरिक्त आयटम}other{आणखी <xliff:g id="COUNT_1">^1</xliff:g> अतिरिक्त आयटम}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"तात्पुरत्या अॅप फाइल साफ करा"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ला काही तात्पुरत्या फाइल साफ करायच्या आहेत. यामुळे बॅटरी किंवा सेल्युलर डेटाच्या वापरात वाढ होऊ शकते."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"तात्पुरत्या ॲप फाइल काढून टाकत आहे…"</string>
<string name="clear" msgid="5524638938415865915">"साफ करा"</string>
<string name="allow" msgid="8885707816848569619">"अनुमती द्या"</string>
<string name="deny" msgid="6040983710442068936">"नकार द्या"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> ऑडिओ फाइल सुधारित करण्याची परवानगी द्यायची आहे का?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ला ही ऑडिओ फाइल सुधारित करण्याची परवानगी द्यायची आहे का?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ऑडिओ फाइल बदलत आहे…</item>
- <item quantity="one">ऑडिओ फाइल बदलत आहे…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> व्हिडिओ सुधारित करण्याची परवानगी द्यायची आहे का?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ला हा व्हिडिओ सुधारित करण्याची परवानगी द्यायची आहे का?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> व्हिडिओ बदलत आहे…</item>
- <item quantity="one">व्हिडिओ बदलत आहे…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> फोटो सुधारित करण्याची परवानगी द्यायची आहे का?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ला हा फोटो सुधारित करण्याची परवानगी द्यायची आहे का?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> फोटो बदलत आहे…</item>
- <item quantity="one">फोटो बदलत आहे…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> आयटम सुधारित करण्याची परवानगी द्यायची आहे का?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ला हा आयटम सुधारित करण्याची परवानगी द्यायची आहे का?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> आयटम बदलत आहे…</item>
- <item quantity="one">आयटम बदलत आहे…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> ऑडिओ फाइल कचऱ्यामध्ये हलवायची परवानगी द्यायची आहे का?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ला ही ऑडिओ फाइल कचऱ्यामध्ये हलवायची परवानगी द्यायची आहे का?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ऑडिओ ट्रॅशमध्ये हलवत आहे…</item>
- <item quantity="one">ऑडिओ ट्रॅशमध्ये हलवत आहे…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> व्हिडिओ कचऱ्यामध्ये हलवायची परवानगी द्यायची आहे का?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ला हा व्हिडिओ कचऱ्यामध्ये हलवायची परवानगी द्यायची आहे का?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> व्हिडिओ ट्रॅशमध्ये हलवत आहे…</item>
- <item quantity="one">व्हिडिओ ट्रॅशमध्ये हलवत आहे…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> फोटो कचऱ्यामध्ये हलवायची परवानगी द्यायची आहे का?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ला हा फोटो कचऱ्यामध्ये हलवायची परवानगी द्यायची आहे का?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> फोटो ट्रॅशमध्ये हलवत आहे…</item>
- <item quantity="one">फोटो ट्रॅशमध्ये हलवत आहे…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> आयटम कचऱ्यामध्ये हलवण्याची परवानगी द्यायची आहे का?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ला हा आयटम कचऱ्यामध्ये हलवायची परवानगी द्यायची आहे का?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> आयटम ट्रॅशमध्ये हलवत आहे…</item>
- <item quantity="one">आयटम ट्रॅशमध्ये हलवत आहे…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> ऑडिओ फाइल कचऱ्यामधून बाहेर हलवायची परवानगी द्यायची आहे का?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ला ही ऑडिओ फाइल कचऱ्यामधून बाहेर हलवायची परवानगी द्यायची आहे का?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ऑडिओ फाइल ट्रॅशमधून बाहेर हलवत आहे…</item>
- <item quantity="one">ऑडिओ फाइल ट्रॅशमधून बाहेर हलवत आहे…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> व्हिडिओ कचऱ्यामधून बाहेर हलवायची परवानगी द्यायची आहे का?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ला हा व्हिडिओ कचऱ्यामधून बाहेर हलवायची परवानगी द्यायची आहे का?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> व्हिडिओ ट्रॅशमधून बाहेर हलवत आहे…</item>
- <item quantity="one">व्हिडिओ ट्रॅशमधून बाहेर हलवत आहे…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> फोटो कचऱ्यामधून बाहेर हलवायची परवानगी द्यायची आहे का?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ला हा फोटो कचऱ्यामधून बाहेर हलवायची परवानगी द्यायची आहे का?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> फोटो ट्रॅशमधून बाहेर हलवत आहे…</item>
- <item quantity="one">फोटो ट्रॅशमधून बाहेर हलवत आहे…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> आयटम कचऱ्यामधून बाहेर हलवायची परवानगी द्यायची आहे का?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ला हा आयटम कचऱ्यामधून बाहेर हलवायची परवानगी द्यायची आहे का?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> आयटम ट्रॅशमधून बाहेर हलवत आहे…</item>
- <item quantity="one">आयटम ट्रॅशमधून बाहेर हलवत आहे…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> ऑडिओ फाइल हटवायची परवानगी द्यायची आहे का?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ला ही ऑडिओ फाइल हटवायची परवानगी द्यायची आहे का?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ऑडिओ फाइल हटवत आहे…</item>
- <item quantity="one">ऑडिओ फाइल हटवत आहे…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> व्हिडिओ हटवायची परवानगी द्यायची आहे का?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ला हा व्हिडिओ हटवायची परवानगी द्यायची आहे का?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> व्हिडिओ हटवत आहे…</item>
- <item quantity="one">व्हिडिओ हटवत आहे…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> फोटो हटवायची परवानगी द्यायची आहे का?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ला हा फोटो हटवायची परवानगी द्यायची आहे का?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> फोटो हटवत आहे…</item>
- <item quantity="one">फोटो हटवत आहे…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> आयटम हटवायची परवानगी द्यायची आहे का?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ला हा आयटम हटवायची परवानगी द्यायची आहे का?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> आयटम हटवत आहे…</item>
- <item quantity="one">आयटम हटवत आहे…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"जोडा"</string>
+ <string name="deselect" msgid="4297825044827769490">"निवड रद्द करा"</string>
+ <string name="deselected" msgid="8488133193326208475">"निवड रद्द केली आहे"</string>
+ <string name="select" msgid="2704765470563027689">"निवडा"</string>
+ <string name="selected" msgid="9151797369975828124">"निवडले आहे"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{कमाल <xliff:g id="COUNT_0">^1</xliff:g> आयटम निवडा}other{कमाल <xliff:g id="COUNT_1">^1</xliff:g> आयटम निवडा}}"</string>
+ <string name="recent" msgid="6694613584743207874">"अलीकडील"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"कोणतेही फोटो किंवा व्हिडिओ नाहीत"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"कोणतेही अल्बम नाहीत"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"निवडलेले पहा"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"फोटो"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"अल्बम"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"पूर्वावलोकन"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"ऑफिसवर स्विच करा"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"वैयक्तिकवर स्विच करा"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"तुमच्या ॲडमिनने ब्लॉक केले"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"वैयक्तिक ॲपवरून कामाशी संबंधित डेटा अॅक्सेस करण्याची परवानगी नाही"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"कामाशी संबंधित ॲपवरून वैयक्तिक डेटा अॅक्सेस करण्याची परवानगी नाही"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"कामाशी संबंधित अॅप्स थांबवली आहेत"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"कामाशी संबंधित फोटो उघडण्यासाठी, कामाशी संबंधित अॅप्स सुरू करा त्यानंतर पुन्हा प्रयत्न करा"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"हे ॲप फक्त तुम्ही निवडलेले फोटो अॅक्सेस करू शकते"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> आयटम}other{<xliff:g id="COUNT_1">^1</xliff:g> आयटम}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"(<xliff:g id="COUNT">^1</xliff:g>) जोडा"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"कॅमेरा"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"डाउनलोड"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"आवडते"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"स्क्रीनशॉट"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"मोशन फोटो"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> <xliff:g id="TIME">%2$s</xliff:g> वाजता घेतला"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"<xliff:g id="TIME">%1$s</xliff:g> रोजी घेतलेला <xliff:g id="DURATION">%2$s</xliff:g> कालावधीचा व्हिडिओ"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"फोटो"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"मोशन फोटो"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"व्हिडिओ म्यूट करा"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"व्हिडिओ अनम्यूट करा"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"व्हिडिओ प्ले करा"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"व्हिडिओ थांबवा"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"आता <xliff:g id="PKG_NAME">%1$s</xliff:g> कडून क्लाउड मीडिया उपलब्ध आहे"</string>
+ <string name="not_selected" msgid="2244008151669896758">"निवडला नाही"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ला या ऑडिओ फाइलमध्ये फेरबदल करण्याची अनुमती द्यायची आहे का?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> ऑडिओ फाइलमध्ये फेरबदल करण्याची अनुमती द्यायची आहे का?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{ऑडिओ फाइलमध्ये फेरबदल करत आहे…}other{<xliff:g id="COUNT">^1</xliff:g> ऑडिओ फाइलमध्ये फेरबदल करत आहे…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ला या व्हिडिओमध्ये फेरबदल करण्याची अनुमती द्यायची आहे का?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> व्हिडिओमध्ये फेरबदल करण्याची अनुमती द्यायची आहे का?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{व्हिडिओमध्ये फेरबदल करत आहे…}other{<xliff:g id="COUNT">^1</xliff:g> व्हिडिओमध्ये फेरबदल करत आहे…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ला या फोटोमध्ये फेरबदल करण्याची अनुमती द्यायची आहे का?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> फोटोमध्ये फेरबदल करण्याची अनुमती द्यायची आहे का?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{फोटोमध्ये फेरबदल करत आहे…}other{<xliff:g id="COUNT">^1</xliff:g> फोटोमध्ये फेरबदल करत आहे…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ला या आयटममध्ये फेरबदल करण्याची अनुमती द्यायची आहे का?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> आयटममध्ये फेरबदल करण्याची अनुमती द्यायची आहे का?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{आयटममध्ये फेरबदल करत आहे…}other{<xliff:g id="COUNT">^1</xliff:g> आयटममध्ये फेरबदल करत आहे…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ला ही ऑडिओ फाइल ट्रॅशमध्ये हलवण्याची अनुमती द्यायची आहे का?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> ऑडिओ फाइल ट्रॅशमध्ये हलवण्याची अनुमती द्यायची आहे का?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{ऑडिओ फाइल ट्रॅशमध्ये हलवत आहे…}other{<xliff:g id="COUNT">^1</xliff:g> ऑडिओ फाइल ट्रॅशमध्ये हलवत आहे…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ला हा व्हिडिओ ट्रॅशमध्ये हलवण्याची अनुमती द्यायची आहे का?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> व्हिडिओ ट्रॅशमध्ये हलवण्याची अनुमती द्यायची आहे का?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{व्हिडिओ ट्रॅशमध्ये हलवत आहे…}other{<xliff:g id="COUNT">^1</xliff:g> व्हिडिओ ट्रॅशमध्ये हलवत आहे…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ला हा फोटो ट्रॅशमध्ये हलवण्याची अनुमती द्यायची आहे का?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> फोटो ट्रॅशमध्ये हलवण्याची अनुमती द्यायची आहे का?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{फोटो ट्रॅशमध्ये हलवत आहे…}other{<xliff:g id="COUNT">^1</xliff:g> फोटो ट्रॅशमध्ये हलवत आहे…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ला हा आयटम ट्रॅशमध्ये हलवण्याची अनुमती द्यायची आहे का?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> आयटम ट्रॅशमध्ये हलवण्याची अनुमती द्यायची आहे का?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{आयटम ट्रॅशमध्ये हलवत आहे…}other{<xliff:g id="COUNT">^1</xliff:g> आयटम ट्रॅशमध्ये हलवत आहे…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ला ही ऑडिओ फाइल ट्रॅशमधून हलवण्याची अनुमती द्यायची आहे का?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> ऑडिओ फाइल ट्रॅशमधून हलवण्याची अनुमती द्यायची आहे का?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{ऑडिओ फाइल ट्रॅशमधून हलवत आहे…}other{<xliff:g id="COUNT">^1</xliff:g> ऑडिओ फाइल ट्रॅशमधून हलवत आहे…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ला हा व्हिडिओ ट्रॅशमधून हलवण्याची अनुमती द्यायची आहे का?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> व्हिडिओ ट्रॅशमधून हलवण्याची अनुमती द्यायची आहे का?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{व्हिडिओ ट्रॅशमधून हलवत आहे…}other{<xliff:g id="COUNT">^1</xliff:g> व्हिडिओ ट्रॅशमधून हलवत आहे…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ला हा फोटो ट्रॅशमधून हलवण्याची अनुमती द्यायची आहे का?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> फोटो ट्रॅशमधून हलवण्याची अनुमती द्यायची आहे का?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{फोटो ट्रॅशमधून हलवत आहे…}other{<xliff:g id="COUNT">^1</xliff:g> फोटो ट्रॅशमधून हलवत आहे…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ला हा आयटम ट्रॅशमधून हलवण्याची अनुमती द्यायची आहे का?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> आयटम ट्रॅशमधून हलवण्याची अनुमती द्यायची आहे का?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{आयटम ट्रॅशमधून हलवत आहे…}other{<xliff:g id="COUNT">^1</xliff:g> आयटम ट्रॅशमधून हलवत आहे…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ला ही ऑडिओ फाइल हटवण्याची अनुमती द्यायची आहे का?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> ऑडिओ फाइल हटवण्याची अनुमती द्यायची आहे का?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{ऑडिओ फाइल हटवत आहे…}other{<xliff:g id="COUNT">^1</xliff:g> ऑडिओ फाइल हटवत आहे…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ला हा व्हिडिओ हटवण्याची अनुमती द्यायची आहे का?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> व्हिडिओ हटवण्याची अनुमती द्यायची आहे का?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{व्हिडिओ हटवत आहे…}other{<xliff:g id="COUNT">^1</xliff:g> व्हिडिओ हटवत आहे…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ला हा फोटो हटवण्याची अनुमती द्यायची आहे का?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> फोटो हटवण्याची अनुमती द्यायची आहे का?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{फोटो हटवत आहे…}other{<xliff:g id="COUNT">^1</xliff:g> फोटो हटवत आहे…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ला हा आयटम हटवण्याची अनुमती द्यायची आहे का?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g id="COUNT">^2</xliff:g> आयटम हटवण्याची अनुमती द्यायची आहे का?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{आयटम हटवत आहे…}other{<xliff:g id="COUNT">^1</xliff:g> आयटम हटवत आहे…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> मीडिया फाइलवर प्रक्रिया करू नाही"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"मीडियावर प्रक्रिया करणे रद्द केले"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"मीडियावर प्रक्रिया करण्यासंबंधित एरर"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index dc5d040..a16a090 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Pemilih foto"</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>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Teruskan"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Benarkan"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Tolak"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Serta <xliff:g id="COUNT_1">^1</xliff:g> item tambahan</item>
- <item quantity="one">Serta <xliff:g id="COUNT_0">^1</xliff:g> item tambahan</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Serta <xliff:g id="COUNT_0">^1</xliff:g> item tambahan}other{Serta <xliff:g id="COUNT_1">^1</xliff:g> item tambahan}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Kosongkan fail apl sementara"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> mahu mengosongkan beberapa fail sementara. Tindakan ini mungkin menyebabkan peningkatan penggunaan bateri atau data selular."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Mengosongkan fail apl sementara…"</string>
<string name="clear" msgid="5524638938415865915">"Kosongkan"</string>
<string name="allow" msgid="8885707816848569619">"Benarkan"</string>
<string name="deny" msgid="6040983710442068936">"Tolak"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengubah suai <xliff:g id="COUNT">^2</xliff:g> fail audio?</item>
- <item quantity="one">Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengubah suai fail audio ini?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">Mengubah suai <xliff:g id="COUNT">^1</xliff:g> fail audio…</item>
- <item quantity="one">Mengubah suai fail audio…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengubah suai <xliff:g id="COUNT">^2</xliff:g> video?</item>
- <item quantity="one">Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengubah suai video ini?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">Mengubah suai <xliff:g id="COUNT">^1</xliff:g> video…</item>
- <item quantity="one">Mengubah suai video…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengubah suai <xliff:g id="COUNT">^2</xliff:g> foto?</item>
- <item quantity="one">Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengubah suai foto ini?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">Mengubah suai <xliff:g id="COUNT">^1</xliff:g> foto…</item>
- <item quantity="one">Mengubah suai foto…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengubah suai <xliff:g id="COUNT">^2</xliff:g> item?</item>
- <item quantity="one">Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengubah suai item ini?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">Mengubah suai <xliff:g id="COUNT">^1</xliff:g> item…</item>
- <item quantity="one">Mengubah suai item…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengalihkan <xliff:g id="COUNT">^2</xliff:g> fail audio ke sampah?</item>
- <item quantity="one">Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengalihkan fail audio ini ke sampah?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">Mengalihkan <xliff:g id="COUNT">^1</xliff:g> fail audio ke sampah…</item>
- <item quantity="one">Mengalihkan fail audio ke sampah…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengalihkan <xliff:g id="COUNT">^2</xliff:g> video ke sampah?</item>
- <item quantity="one">Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengalihkan video ini ke sampah?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">Mengalihkan <xliff:g id="COUNT">^1</xliff:g> video ke sampah…</item>
- <item quantity="one">Mengalihkan video ke sampah…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengalihkan <xliff:g id="COUNT">^2</xliff:g> foto ke sampah?</item>
- <item quantity="one">Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengalihkan foto ini ke sampah?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">Mengalihkan <xliff:g id="COUNT">^1</xliff:g> foto ke sampah…</item>
- <item quantity="one">Mengalihkan foto ke sampah…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengalihkan <xliff:g id="COUNT">^2</xliff:g> item ke sampah?</item>
- <item quantity="one">Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengalihkan item ini ke sampah?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">Mengalihkan <xliff:g id="COUNT">^1</xliff:g> item ke sampah…</item>
- <item quantity="one">Mengalihkan item ke sampah…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengalihkan <xliff:g id="COUNT">^2</xliff:g> fail audio keluar daripada sampah?</item>
- <item quantity="one">Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengalihkan fail audio ini keluar daripada sampah?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">Mengalihkan <xliff:g id="COUNT">^1</xliff:g> fail audio keluar dari sampah…</item>
- <item quantity="one">Mengalihkan fail audio keluar dari sampah…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengalihkan <xliff:g id="COUNT">^2</xliff:g> video keluar daripada sampah?</item>
- <item quantity="one">Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengalihkan video ini keluar daripada sampah?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">Mengalih <xliff:g id="COUNT">^1</xliff:g> video keluar dari sampah…</item>
- <item quantity="one">Mengalih video keluar dari sampah…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengalihkan <xliff:g id="COUNT">^2</xliff:g> foto keluar daripada sampah?</item>
- <item quantity="one">Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengalihkan foto ini keluar daripada sampah?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">Mengalih <xliff:g id="COUNT">^1</xliff:g> foto keluar dari sampah…</item>
- <item quantity="one">Mengalih foto keluar dari sampah…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengalihkan <xliff:g id="COUNT">^2</xliff:g> item keluar daripada sampah?</item>
- <item quantity="one">Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengalihkan item ini keluar daripada sampah?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">Mengalih <xliff:g id="COUNT">^1</xliff:g> item keluar dari sampah…</item>
- <item quantity="one">Mengalih item keluar dari sampah…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> memadamkan <xliff:g id="COUNT">^2</xliff:g> fail audio?</item>
- <item quantity="one">Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> memadamkan fail audio ini?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">Memadamkan <xliff:g id="COUNT">^1</xliff:g> fail audio…</item>
- <item quantity="one">Memadamkan fail audio…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> memadamkan <xliff:g id="COUNT">^2</xliff:g> video?</item>
- <item quantity="one">Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> memadamkan video ini?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">Memadamkan <xliff:g id="COUNT">^1</xliff:g> video…</item>
- <item quantity="one">Memadamkan video…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> memadamkan <xliff:g id="COUNT">^2</xliff:g> foto?</item>
- <item quantity="one">Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> memadamkan foto ini?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">Memadamkan <xliff:g id="COUNT">^1</xliff:g> foto…</item>
- <item quantity="one">Memadamkan foto…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> memadamkan <xliff:g id="COUNT">^2</xliff:g> item?</item>
- <item quantity="one">Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> memadamkan item ini?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">Memadamkan <xliff:g id="COUNT">^1</xliff:g> item…</item>
- <item quantity="one">Memadamkan item…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Tambah"</string>
+ <string name="deselect" msgid="4297825044827769490">"Nyahpilih"</string>
+ <string name="deselected" msgid="8488133193326208475">"Dinyahpilih"</string>
+ <string name="select" msgid="2704765470563027689">"Pilih"</string>
+ <string name="selected" msgid="9151797369975828124">"Dipilih"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Pilih hingga <xliff:g id="COUNT_0">^1</xliff:g> item}other{Pilih hingga <xliff:g id="COUNT_1">^1</xliff:g> item}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Terkini"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Tiada foto atau video"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Tiada album"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Lihat terpilih"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Foto"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Album"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Pratonton"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Beralih kepada kerja"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Beralih kepada peribadi"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Disekat oleh pentadbir anda"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Pengaksesan data kerja daripada apl peribadi tidak dibenarkan"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Pengaksesan data peribadi daripada apl kerja tidak dibenarkan"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Apl kerja dijeda"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Untuk membuka foto kerja, hidupkan apl kerja anda, kemudian cuba lagi"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Apl ini hanya dapat mengakses foto yang anda pilih"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}other{<xliff:g id="COUNT_1">^1</xliff:g> item}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Tambah (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Muat turun"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Kegemaran"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Tangkapan skrin"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Foto dengan Gerakan"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> diambil pada <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Video diambil pada <xliff:g id="TIME">%1$s</xliff:g> dengan tempoh <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Foto"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Foto dengan Gerakan"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Redam video"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Nyahredam video"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Mainkan video"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Jeda video"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Media awan kini tersedia daripada <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"tidak dipilih"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengubah suai fail audio ini?}other{Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengubah suai <xliff:g id="COUNT">^2</xliff:g> fail audio?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Mengubah suai fail audio…}other{Mengubah suai <xliff:g id="COUNT">^1</xliff:g> fail audio…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengubah suai video ini?}other{Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengubah suai <xliff:g id="COUNT">^2</xliff:g> video?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Mengubah suai video…}other{Mengubah suai <xliff:g id="COUNT">^1</xliff:g> video…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengubah suai foto ini?}other{Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengubah suai <xliff:g id="COUNT">^2</xliff:g> foto?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Mengubah suai foto…}other{Mengubah suai <xliff:g id="COUNT">^1</xliff:g> foto…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengubah suai item ini?}other{Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengubah suai <xliff:g id="COUNT">^2</xliff:g> item?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Mengubah suai item…}other{Mengubah suai <xliff:g id="COUNT">^1</xliff:g> item…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengalihkan fail audio ini ke sampah?}other{Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengalihkan <xliff:g id="COUNT">^2</xliff:g> fail audio ke sampah?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Mengalihkan fail audio ke sampah…}other{Mengalihkan <xliff:g id="COUNT">^1</xliff:g> fail audio ke sampah…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengalihkan video ini ke sampah?}other{Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengalihkan <xliff:g id="COUNT">^2</xliff:g> video ke sampah?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Mengalihkan video ke sampah…}other{Mengalihkan <xliff:g id="COUNT">^1</xliff:g> video ke sampah…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengalihkan foto ini ke sampah?}other{Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengalihkan <xliff:g id="COUNT">^2</xliff:g> foto ke sampah?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Mengalihkan foto ke sampah…}other{Mengalihkan <xliff:g id="COUNT">^1</xliff:g> foto ke sampah…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengalihkan item ini ke sampah?}other{Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengalihkan <xliff:g id="COUNT">^2</xliff:g> item ke sampah?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Mengalihkan item ke sampah…}other{Mengalihkan <xliff:g id="COUNT">^1</xliff:g> item ke sampah…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengalihkan fail audio ini keluar daripada sampah?}other{Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengalihkan <xliff:g id="COUNT">^2</xliff:g> fail audio keluar daripada sampah?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Mengalihkan fail audio keluar daripada sampah…}other{Mengalihkan <xliff:g id="COUNT">^1</xliff:g> fail audio keluar daripada sampah…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengalihkan video ini keluar daripada sampah?}other{Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengalihkan <xliff:g id="COUNT">^2</xliff:g> video keluar daripada sampah?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Mengalihkan video keluar daripada sampah…}other{Mengalihkan <xliff:g id="COUNT">^1</xliff:g> video keluar daripada sampah…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengalihkan foto ini keluar daripada sampah?}other{Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengalihkan <xliff:g id="COUNT">^2</xliff:g> foto keluar daripada sampah?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Mengalihkan foto keluar daripada sampah…}other{Mengalihkan <xliff:g id="COUNT">^1</xliff:g> foto keluar daripada sampah…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> mengalihkan item ini keluar daripada sampah?}other{Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> mengalihkan <xliff:g id="COUNT">^2</xliff:g> item keluar daripada sampah?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Mengalihkan item keluar daripada sampah…}other{Mengalihkan <xliff:g id="COUNT">^1</xliff:g> item keluar daripada sampah…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> memadamkan fail audio ini?}other{Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> memadamkan <xliff:g id="COUNT">^2</xliff:g> fail audio?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Memadamkan fail audio…}other{Memadamkan <xliff:g id="COUNT">^1</xliff:g> fail audio…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> memadamkan video ini?}other{Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> memadamkan <xliff:g id="COUNT">^2</xliff:g> video?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Memadamkan video…}other{Memadamkan <xliff:g id="COUNT">^1</xliff:g> video…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> memadamkan foto ini?}other{Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> memadamkan <xliff:g id="COUNT">^2</xliff:g> foto?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Memadamkan foto…}other{Memadamkan <xliff:g id="COUNT">^1</xliff:g> foto…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Benarkan <xliff:g id="APP_NAME_0">^1</xliff:g> memadamkan item ini?}other{Benarkan <xliff:g id="APP_NAME_1">^1</xliff:g> memadamkan <xliff:g id="COUNT">^2</xliff:g> item?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Memadamkan item…}other{Memadamkan <xliff:g id="COUNT">^1</xliff:g> item…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> tidak dapat memproses fail media"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Pemprosesan media dibatalkan"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Ralat pemprosesan media"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index ce0b311..62131d8 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"မီဒီယာ"</string>
<string name="storage_description" msgid="4081716890357580107">"စက်တွင်း သိုလှောင်ခန်း"</string>
<string name="app_label" msgid="9035307001052716210">"မီဒီယာ သိုလှောင်ခန်း"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"ဓာတ်ပုံ ရွေးချယ်ရေးစနစ်"</string>
<string name="artist_label" msgid="8105600993099120273">"အနုပညာရှင်"</string>
<string name="unknown" msgid="2059049215682829375">"အမျိုးအမည်မသိ"</string>
<string name="root_images" msgid="5861633549189045666">"ပုံများ"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"ရှေ့ဆက်ရန်"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"ခွင့်ပြုရန်"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"ငြင်းပယ်ရန်"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">ထပ်ပေါင်းအရာ <xliff:g id="COUNT_1">^1</xliff:g> ခု အပါအဝင်</item>
- <item quantity="one">ထပ်ပေါင်းအရာ <xliff:g id="COUNT_0">^1</xliff:g> ခု အပါအဝင်</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{နောက်ထပ်ဖိုင် <xliff:g id="COUNT_0">^1</xliff:g> ခု အပါအဝင်}other{နောက်ထပ်ဖိုင် <xliff:g id="COUNT_1">^1</xliff:g> ခု အပါအဝင်}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"ယာယီအက်ပ်ဖိုင်များ ရှင်းရန်"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> သည် ယာယီဖိုင်များမှ အချို့ကို ရှင်းထုတ်လိုသည်။ ထို့ကြောင့် ဘက်ထရီ သို့မဟုတ် ဆယ်လူလာဒေတာ အသုံးပြုမှု မြင့်တက်လာနိုင်သည်။"</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"ယာယီအက်ပ်ဖိုင်များ ရှင်းထုတ်နေသည်…"</string>
<string name="clear" msgid="5524638938415865915">"ရှင်းရန်"</string>
<string name="allow" msgid="8885707816848569619">"ခွင့်ပြုရန်"</string>
<string name="deny" msgid="6040983710442068936">"ပယ်ရန်"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ကို အသံဖိုင် <xliff:g id="COUNT">^2</xliff:g> ဖိုင် ပြင်ဆင်ခွင့်ပြုမလား။</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ကို ဤအသံဖိုင် ပြင်ဆင်ခွင့်ပြုမလား။</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">အသံဖိုင် <xliff:g id="COUNT">^1</xliff:g> ခုကို ပြင်ဆင်နေသည်…</item>
- <item quantity="one">အသံဖိုင်ကို ပြင်ဆင်နေသည်…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ကို ဗီဒီယို <xliff:g id="COUNT">^2</xliff:g> ခု ပြင်ဆင်ခွင့်ပြုမလား။</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ကို ဤဗီဒီယို ပြင်ဆင်ခွင့်ပြုမလား။</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">ဗီဒီယို <xliff:g id="COUNT">^1</xliff:g> ခုကို ပြင်ဆင်နေသည်…</item>
- <item quantity="one">ဗီဒီယိုကို ပြင်ဆင်နေသည်…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> အား ဓာတ်ပုံ <xliff:g id="COUNT">^2</xliff:g> ပုံကို ပြုပြင်ခွင့်ပြုမလား။</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ကို ဤဓာတ်ပုံ ပြင်ဆင်ခွင့်ပြုမလား။</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">ဓာတ်ပုံ <xliff:g id="COUNT">^1</xliff:g> ပုံကို ပြင်ဆင်နေသည်…</item>
- <item quantity="one">ဓာတ်ပုံကို ပြင်ဆင်နေသည်…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ကို ဤအရာ <xliff:g id="COUNT">^2</xliff:g> ခု ပြင်ဆင်ခွင့်ပြုမလား။</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ကို ဤအရာ ပြင်ဆင်ခွင့်ပြုမလား။</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">ဖိုင် <xliff:g id="COUNT">^1</xliff:g> ခုကို ပြင်ဆင်နေသည်…</item>
- <item quantity="one">ဖိုင်ကို ပြင်ဆင်နေသည်…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> အား အသံဖိုင် <xliff:g id="COUNT">^2</xliff:g> ဖိုင်ကို အမှိုက်ပုံးသို့ ရွှေ့ခွင့်ပြုမလား။</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> အား ဤအသံဖိုင်ကို အမှိုက်ပုံးသို့ ရွှေ့ခွင့်ပြုမလား။</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">အသံဖိုင် <xliff:g id="COUNT">^1</xliff:g> ခုကို အမှိုက်ပုံးထဲသို့ ရွှေ့နေသည်…</item>
- <item quantity="one">အသံဖိုင်ကို အမှိုက်ပုံးထဲသို့ ရွှေ့နေသည်…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> အား ဗီဒီယို <xliff:g id="COUNT">^2</xliff:g> ခုကို အမှိုက်ပုံးသို့ ရွှေ့ခွင့်ပြုမလား။</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> အား ဤဗီဒီယိုကို အမှိုက်ပုံးသို့ ရွှေ့ခွင့်ပြုမလား။</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">ဗီဒီယို <xliff:g id="COUNT">^1</xliff:g> ခုကို အမှိုက်ပုံးထဲသို့ ရွှေ့နေသည်…</item>
- <item quantity="one">ဗီဒီယိုကို အမှိုက်ပုံးထဲသို့ ရွှေ့နေသည်…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> အား ဓာတ်ပုံ <xliff:g id="COUNT">^2</xliff:g> ပုံကို အမှိုက်ပုံးသို့ ရွှေ့ခွင့်ပြုမလား။</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> အား ဤဓာတ်ပုံကို အမှိုက်ပုံးသို့ ရွှေ့ခွင့်ပြုမလား။</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">ဓာတ်ပုံ <xliff:g id="COUNT">^1</xliff:g> ပုံကို အမှိုက်ပုံးထဲသို့ ရွှေ့နေသည်…</item>
- <item quantity="one">ဓာတ်ပုံကို အမှိုက်ပုံးထဲသို့ ရွှေ့နေသည်…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> အား ဤအရာ <xliff:g id="COUNT">^2</xliff:g> ခုကို အမှိုက်ပုံးသို့ ရွှေ့ခွင့်ပြုမလား။</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> အား ဤအရာကို အမှိုက်ပုံးသို့ ရွှေ့ခွင့်ပြုမလား။</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">ဖိုင် <xliff:g id="COUNT">^1</xliff:g> ခုကို အမှိုက်ပုံးထဲသို့ ရွှေ့နေသည်…</item>
- <item quantity="one">ဖိုင်ကို အမှိုက်ပုံးထဲသို့ ရွှေ့နေသည်…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> အား အသံဖိုင် <xliff:g id="COUNT">^2</xliff:g> ဖိုင်ကို အမှိုက်ပုံးထဲမှ ရွှေ့ခွင့်ပြုမလား။</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> အား ဤအသံဖိုင်ကို အမှိုက်ပုံးထဲမှ ရွှေ့ခွင့်ပြုမလား။</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">အသံဖိုင်ကို <xliff:g id="COUNT">^1</xliff:g> ခုကို အမှိုက်ပုံးထဲမှ ရွှေ့နေသည်…</item>
- <item quantity="one">အသံဖိုင်ကို အမှိုက်ပုံးထဲမှ ရွှေ့နေသည်…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> အား ဗီဒီယို <xliff:g id="COUNT">^2</xliff:g> ခုကို အမှိုက်ပုံးထဲမှ ရွှေ့ခွင့်ပြုမလား။</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> အား ဤဗီဒီယိုကို အမှိုက်ပုံးထဲမှ ရွှေ့ခွင့်ပြုမလား။</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">ဗီဒီယို <xliff:g id="COUNT">^1</xliff:g> ခုကို အမှိုက်ပုံးထဲမှ ရွှေ့နေသည်…</item>
- <item quantity="one">ဗီဒီယိုကို အမှိုက်ပုံးထဲမှ ရွှေ့နေသည်…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> အား ဓာတ်ပုံ <xliff:g id="COUNT">^2</xliff:g> ပုံကို အမှိုက်ပုံးထဲမှ ရွှေ့ခွင့်ပြုမလား။</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> အား ဤဓာတ်ပုံကို အမှိုက်ပုံးထဲမှ ရွှေ့ခွင့်ပြုမလား။</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">ဓာတ်ပုံ <xliff:g id="COUNT">^1</xliff:g> ပုံကို အမှိုက်ပုံးထဲမှ ရွှေ့နေသည်…</item>
- <item quantity="one">ဓာတ်ပုံကို အမှိုက်ပုံးထဲမှ ရွှေ့နေသည်…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> အား ဤအရာ <xliff:g id="COUNT">^2</xliff:g> ခုကို အမှိုက်ပုံးထဲမှ ရွှေ့ခွင့်ပြုမလား။</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> အား ဤအရာကို အမှိုက်ပုံးထဲမှ ရွှေ့ခွင့်ပြုမလား။</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">ဖိုင် <xliff:g id="COUNT">^1</xliff:g> ခုကို အမှိုက်ပုံးထဲမှ ရွှေ့နေသည်…</item>
- <item quantity="one">ဖိုင်ကို အမှိုက်ပုံးထဲမှ ရွှေ့နေသည်…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> အား အသံဖိုင် <xliff:g id="COUNT">^2</xliff:g> ဖိုင်ကို ဖျက်ခွင့်ပြုမလား။</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> အား ဤအသံဖိုင်ကို ဖျက်ခွင့်ပြုမလား။</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">အသံဖိုင် <xliff:g id="COUNT">^1</xliff:g> ခုကို ဖျက်နေသည်…</item>
- <item quantity="one">အသံဖိုင်ကို ဖျက်နေသည်…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ကို ဗီဒီယို <xliff:g id="COUNT">^2</xliff:g> ခု ဖျက်ခွင့်ပြုမလား။</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ကို ဤဗီဒီယို ဖျက်ခွင့်ပြုမလား။</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">ဗီဒီယို <xliff:g id="COUNT">^1</xliff:g> ခုကို ဖျက်နေသည်…</item>
- <item quantity="one">ဗီဒီယိုကို ဖျက်နေသည်…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ကို ဓာတ်ပုံ <xliff:g id="COUNT">^2</xliff:g> ပုံ ဖျက်ခွင့်ပြုမလား။</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ကို ဤဓာတ်ပုံ ဖျက်ခွင့်ပြုမလား။</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">ဓာတ်ပုံ <xliff:g id="COUNT">^1</xliff:g> ပုံကို ဖျက်နေသည်…</item>
- <item quantity="one">ဓာတ်ပုံကို ဖျက်နေသည်…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> အား ဤအရာ <xliff:g id="COUNT">^2</xliff:g> ခုကို ဖျက်ခွင့်ပြုမလား။</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> အား ဤအရာကို ဖျက်ခွင့်ပြုမလား။</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">ဖိုင် <xliff:g id="COUNT">^1</xliff:g> ခုကို ဖျက်နေသည်…</item>
- <item quantity="one">ဖိုင်ကို ဖျက်နေသည်…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"ထည့်ရန်"</string>
+ <string name="deselect" msgid="4297825044827769490">"မရွေးပါနှင့်"</string>
+ <string name="deselected" msgid="8488133193326208475">"ရွေးချယ်မထားပါ"</string>
+ <string name="select" msgid="2704765470563027689">"ရွေးရန်"</string>
+ <string name="selected" msgid="9151797369975828124">"ရွေးချယ်ထားသည်"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{ဖိုင် <xliff:g id="COUNT_0">^1</xliff:g> ခုအထိ ရွေးနိုင်သည်}other{ဖိုင် <xliff:g id="COUNT_1">^1</xliff:g> ခုအထိ ရွေးနိုင်သည်}}"</string>
+ <string name="recent" msgid="6694613584743207874">"မကြာသေးမီက"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"ဓာတ်ပုံ (သို့) ဗီဒီယိုများ မရှိပါ"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"အယ်လ်ဘမ်များ မရှိပါ"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"ပြသမှုကို ရွေးချယ်ထားသည်"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"ဓာတ်ပုံများ"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"အယ်လ်ဘမ်များ"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"အစမ်းကြည့်ရှုခြင်း"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"အလုပ်သို့ ပြောင်းပါ"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"ပုဂ္ဂိုလ်ရေးသီးသန့်အဖြစ် ပြောင်းပါ"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"သင့်စီမံခန့်ခွဲသူက ပိတ်ထားသည်"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"ကိုယ်ရေးသုံးအက်ပ်ဖြင့် အလုပ်ဒေတာများ သုံးခွင့်မရှိပါ"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"အလုပ်သုံးအက်ပ်ဖြင့် ကိုယ်ပိုင်ဒေတာများ သုံးခွင့်မရှိပါ"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"အလုပ်သုံးအက်ပ်များကို ခေတ္တရပ်ထားသည်"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"အလုပ်ဓာတ်ပုံများဖွင့်ရန် သင့်အလုပ်သုံးအက်ပ်များကိုဖွင့်ပြီး ထပ်စမ်းကြည့်ပါ"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"ဤအက်ပ်သည် သင်ရွေးချယ်သောဓာတ်ပုံများကိုသာ ကြည့်နိုင်သည်"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{ဖိုင် <xliff:g id="COUNT_0">^1</xliff:g> ခု}other{ဖိုင် <xliff:g id="COUNT_1">^1</xliff:g> ခု}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"(<xliff:g id="COUNT">^1</xliff:g>) ခု ထည့်ရန်"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"ကင်မရာ"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"ဒေါင်းလုဒ်များ"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"စိတ်ကြိုက်များ"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"ဖန်သားပြင်ဓာတ်ပုံများ"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"တဒင်္ဂ ဗီဒီယို"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="TIME">%2$s</xliff:g> တွင် ရိုက်ကူးခဲ့သော <xliff:g id="ITEM_NAME">%1$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"ဗီဒီယိုကို <xliff:g id="TIME">%1$s</xliff:g> တွင် <xliff:g id="DURATION">%2$s</xliff:g> ကြာ ရိုက်ကူးထားသည်"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"ဓာတ်ပုံ"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"တဒင်္ဂ ဗီဒီယို"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"ဗီဒီယိုအသံပိတ်ရန်"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"ဗီဒီယိုအသံပြန်ဖွင့်ရန်"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"ဗီဒီယို ဖွင့်ရန်"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"ဗီဒီယို ခဏရပ်ရန်"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"<xliff:g id="PKG_NAME">%1$s</xliff:g> တွင် Cloud မီဒီယာကို ယခု ရနိုင်ပြီ"</string>
+ <string name="not_selected" msgid="2244008151669896758">"ရွေးချယ်မထားပါ"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ကို ဤအသံဖိုင် ပြင်ဆင်ခွင့်ပြုမလား။}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ကို အသံဖိုင် <xliff:g id="COUNT">^2</xliff:g> ဖိုင် ပြင်ဆင်ခွင့်ပြုမလား။}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{အသံဖိုင်ကို ပြင်ဆင်နေသည်…}other{အသံဖိုင် <xliff:g id="COUNT">^1</xliff:g> ခုကို ပြင်ဆင်နေသည်…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ကို ဤဗီဒီယို ပြင်ဆင်ခွင့်ပြုမလား။}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ကို ဗီဒီယို <xliff:g id="COUNT">^2</xliff:g> ခု ပြင်ဆင်ခွင့်ပြုမလား။}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{ဗီဒီယိုကို ပြင်ဆင်နေသည်…}other{ဗီဒီယို <xliff:g id="COUNT">^1</xliff:g> ခုကို ပြင်ဆင်နေသည်…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ကို ဤဓာတ်ပုံ ပြင်ဆင်ခွင့်ပြုမလား။}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ကို ဓာတ်ပုံ <xliff:g id="COUNT">^2</xliff:g> ပုံ ပြင်ဆင်ခွင့်ပြုမလား။}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{ဓာတ်ပုံကို ပြင်ဆင်နေသည်…}other{ဓာတ်ပုံ <xliff:g id="COUNT">^1</xliff:g> ပုံကို ပြင်ဆင်နေသည်…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ကို ဤဖိုင် ပြင်ဆင်ခွင့်ပြုမလား။}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ကို ဤဖိုင် <xliff:g id="COUNT">^2</xliff:g> ခု ပြင်ဆင်ခွင့်ပြုမလား။}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{ဖိုင်ကို ပြင်ဆင်နေသည်…}other{ဖိုင် <xliff:g id="COUNT">^1</xliff:g> ခုကို ပြင်ဆင်နေသည်…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> အား ဤအသံဖိုင်ကို အမှိုက်ပုံးသို့ ရွှေ့ခွင့်ပြုမလား။}other{<xliff:g id="APP_NAME_1">^1</xliff:g> အား အသံဖိုင် <xliff:g id="COUNT">^2</xliff:g> ဖိုင်ကို အမှိုက်ပုံးသို့ ရွှေ့ခွင့်ပြုမလား။}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{အသံဖိုင်ကို အမှိုက်ပုံးထဲသို့ ရွှေ့နေသည်…}other{အသံဖိုင် <xliff:g id="COUNT">^1</xliff:g> ခုကို အမှိုက်ပုံးထဲသို့ ရွှေ့နေသည်…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> အား ဤဗီဒီယိုကို အမှိုက်ပုံးသို့ ရွှေ့ခွင့်ပြုမလား။}other{<xliff:g id="APP_NAME_1">^1</xliff:g> အား ဗီဒီယို <xliff:g id="COUNT">^2</xliff:g> ခုကို အမှိုက်ပုံးသို့ ရွှေ့ခွင့်ပြုမလား။}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{ဗီဒီယိုကို အမှိုက်ပုံးထဲသို့ ရွှေ့နေသည်…}other{ဗီဒီယို <xliff:g id="COUNT">^1</xliff:g> ခုကို အမှိုက်ပုံးထဲသို့ ရွှေ့နေသည်…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> အား ဤဓာတ်ပုံကို အမှိုက်ပုံးသို့ ရွှေ့ခွင့်ပြုမလား။}other{<xliff:g id="APP_NAME_1">^1</xliff:g> အား ဓာတ်ပုံ <xliff:g id="COUNT">^2</xliff:g> ပုံကို အမှိုက်ပုံးသို့ ရွှေ့ခွင့်ပြုမလား။}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{ဓာတ်ပုံကို အမှိုက်ပုံးထဲသို့ ရွှေ့နေသည်…}other{ဓာတ်ပုံ <xliff:g id="COUNT">^1</xliff:g> ပုံကို အမှိုက်ပုံးထဲသို့ ရွှေ့နေသည်…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> အား ဤဖိုင်ကို အမှိုက်ပုံးသို့ ရွှေ့ခွင့်ပြုမလား။}other{<xliff:g id="APP_NAME_1">^1</xliff:g> အား ဤဖိုင် <xliff:g id="COUNT">^2</xliff:g> ခုကို အမှိုက်ပုံးသို့ ရွှေ့ခွင့်ပြုမလား။}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{ဖိုင်ကို အမှိုက်ပုံးထဲသို့ ရွှေ့နေသည်…}other{ဖိုင် <xliff:g id="COUNT">^1</xliff:g> ခုကို အမှိုက်ပုံးထဲသို့ ရွှေ့နေသည်…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> အား ဤအသံဖိုင်ကို အမှိုက်ပုံးထဲမှ ရွှေ့ခွင့်ပြုမလား။}other{<xliff:g id="APP_NAME_1">^1</xliff:g> အား အသံဖိုင် <xliff:g id="COUNT">^2</xliff:g> ဖိုင်ကို အမှိုက်ပုံးထဲမှ ရွှေ့ခွင့်ပြုမလား။}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{အသံဖိုင်ကို အမှိုက်ပုံးထဲမှ ရွှေ့နေသည်…}other{အသံဖိုင်ကို <xliff:g id="COUNT">^1</xliff:g> ခုကို အမှိုက်ပုံးထဲမှ ရွှေ့နေသည်…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> အား ဤဗီဒီယိုကို အမှိုက်ပုံးထဲမှ ရွှေ့ခွင့်ပြုမလား။}other{<xliff:g id="APP_NAME_1">^1</xliff:g> အား ဗီဒီယို <xliff:g id="COUNT">^2</xliff:g> ခုကို အမှိုက်ပုံးထဲမှ ရွှေ့ခွင့်ပြုမလား။}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{ဗီဒီယိုကို အမှိုက်ပုံးထဲမှ ရွှေ့နေသည်…}other{ဗီဒီယို <xliff:g id="COUNT">^1</xliff:g> ခုကို အမှိုက်ပုံးထဲမှ ရွှေ့နေသည်…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> အား ဤဓာတ်ပုံကို အမှိုက်ပုံးထဲမှ ရွှေ့ခွင့်ပြုမလား။}other{<xliff:g id="APP_NAME_1">^1</xliff:g> အား ဓာတ်ပုံ <xliff:g id="COUNT">^2</xliff:g> ပုံကို အမှိုက်ပုံးထဲမှ ရွှေ့ခွင့်ပြုမလား။}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{ဓာတ်ပုံကို အမှိုက်ပုံးထဲမှ ရွှေ့နေသည်…}other{ဓာတ်ပုံ <xliff:g id="COUNT">^1</xliff:g> ပုံကို အမှိုက်ပုံးထဲမှ ရွှေ့နေသည်…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> အား ဤဖိုင်ကို အမှိုက်ပုံးထဲမှ ရွှေ့ခွင့်ပြုမလား။}other{<xliff:g id="APP_NAME_1">^1</xliff:g> အား ဤဖိုင် <xliff:g id="COUNT">^2</xliff:g> ခုကို အမှိုက်ပုံးထဲမှ ရွှေ့ခွင့်ပြုမလား။}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{ဖိုင်ကို အမှိုက်ပုံးထဲမှ ရွှေ့နေသည်…}other{ဖိုင် <xliff:g id="COUNT">^1</xliff:g> ခုကို အမှိုက်ပုံးထဲမှ ရွှေ့နေသည်…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ကို ဤအသံဖိုင် ဖျက်ခွင့်ပြုမလား။}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ကို အသံဖိုင် <xliff:g id="COUNT">^2</xliff:g> ဖိုင် ဖျက်ခွင့်ပြုမလား။}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{အသံဖိုင်ကို ဖျက်နေသည်…}other{အသံဖိုင် <xliff:g id="COUNT">^1</xliff:g> ခုကို ဖျက်နေသည်…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ကို ဤဗီဒီယို ဖျက်ခွင့်ပြုမလား။}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ကို ဗီဒီယို <xliff:g id="COUNT">^2</xliff:g> ခု ဖျက်ခွင့်ပြုမလား။}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{ဗီဒီယိုကို ဖျက်နေသည်…}other{ဗီဒီယို <xliff:g id="COUNT">^1</xliff:g> ခုကို ဖျက်နေသည်…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ကို ဤဓာတ်ပုံ ဖျက်ခွင့်ပြုမလား။}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ကို ဓာတ်ပုံ <xliff:g id="COUNT">^2</xliff:g> ပုံ ဖျက်ခွင့်ပြုမလား။}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{ဓာတ်ပုံကို ဖျက်နေသည်…}other{ဓာတ်ပုံ <xliff:g id="COUNT">^1</xliff:g> ပုံကို ဖျက်နေသည်…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ကို ဤဖိုင် ဖျက်ခွင့်ပြုမလား။}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ကို ဤဖိုင် <xliff:g id="COUNT">^2</xliff:g> ခု ဖျက်ခွင့်ပြုမလား။}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{ဖိုင်ကို ဖျက်နေသည်…}other{ဖိုင် <xliff:g id="COUNT">^1</xliff:g> ခုကို ဖျက်နေသည်…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> က မီဒီယာဖိုင်များကို မလုပ်ဆောင်နိုင်ပါ"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"မီဒီယာ လုပ်ဆောင်ခြင်းကို ရပ်လိုက်သည်"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"မီဒီယာ လုပ်ဆောင်ခြင်း အမှားရှိသည်"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 3ebc4c1..91d6669 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Bildevelger"</string>
<string name="artist_label" msgid="8105600993099120273">"Artist"</string>
<string name="unknown" msgid="2059049215682829375">"Ukjent"</string>
<string name="root_images" msgid="5861633549189045666">"Bilder"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Fortsett"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Tillat"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Avvis"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Pluss <xliff:g id="COUNT_1">^1</xliff:g> elementer til</item>
- <item quantity="one">Pluss <xliff:g id="COUNT_0">^1</xliff:g> element til</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Pluss <xliff:g id="COUNT_0">^1</xliff:g> element til}other{Pluss <xliff:g id="COUNT_1">^1</xliff:g> elementer til}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Slett midlertidige appfiler"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vil slette noen midlertidige filer. Dette kan resultere i økt bruk av batteri eller mobildata."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Sletter midlertidige appfiler …"</string>
<string name="clear" msgid="5524638938415865915">"Slett"</string>
<string name="allow" msgid="8885707816848569619">"Tillat"</string>
<string name="deny" msgid="6040983710442068936">"Avvis"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> endrer <xliff:g id="COUNT">^2</xliff:g> lydfiler?</item>
- <item quantity="one">Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> endrer denne lydfilen?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">Endrer <xliff:g id="COUNT">^1</xliff:g> lydfiler …</item>
- <item quantity="one">Endrer lydfilen …</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> endrer <xliff:g id="COUNT">^2</xliff:g> videoer?</item>
- <item quantity="one">Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> endrer denne videoen?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">Endrer <xliff:g id="COUNT">^1</xliff:g> videoer …</item>
- <item quantity="one">Endrer videoen …</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> endrer <xliff:g id="COUNT">^2</xliff:g> bilder?</item>
- <item quantity="one">Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> endrer dette bildet?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">Endrer <xliff:g id="COUNT">^1</xliff:g> bilder …</item>
- <item quantity="one">Endrer bildet …</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> endrer <xliff:g id="COUNT">^2</xliff:g> elementer?</item>
- <item quantity="one">Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> endrer dette elementet?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">Endrer <xliff:g id="COUNT">^1</xliff:g> elementer …</item>
- <item quantity="one">Endrer elementet …</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> flytter <xliff:g id="COUNT">^2</xliff:g> lydfiler til papirkurven?</item>
- <item quantity="one">Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> flytter denne lydfilen til papirkurven?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">Flytter <xliff:g id="COUNT">^1</xliff:g> lydfiler til papirkurven …</item>
- <item quantity="one">Flytter lydfilen til papirkurven …</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> flytter <xliff:g id="COUNT">^2</xliff:g> videoer til papirkurven?</item>
- <item quantity="one">Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> flytter denne videoen til papirkurven?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">Flytter <xliff:g id="COUNT">^1</xliff:g> videoer til papirkurven …</item>
- <item quantity="one">Flytter videoen til papirkurven …</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> flytter <xliff:g id="COUNT">^2</xliff:g> bilder til papirkurven?</item>
- <item quantity="one">Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> flytter dette bildet til papirkurven?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">Flytter <xliff:g id="COUNT">^1</xliff:g> bilder til papirkurven …</item>
- <item quantity="one">Flytter bildet til papirkurven …</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> flytter <xliff:g id="COUNT">^2</xliff:g> elementer til papirkurven?</item>
- <item quantity="one">Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> flytter dette elementet til papirkurven?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">Flytter <xliff:g id="COUNT">^1</xliff:g> elementer til papirkurven …</item>
- <item quantity="one">Flytter elementet til papirkurven …</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> flytter <xliff:g id="COUNT">^2</xliff:g> lydfiler ut av papirkurven?</item>
- <item quantity="one">Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> flytter denne lydfilen ut av papirkurven?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">Flytter <xliff:g id="COUNT">^1</xliff:g> lydfiler ut av papirkurven …</item>
- <item quantity="one">Flytter lydfilen ut av papirkurven …</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> flytter <xliff:g id="COUNT">^2</xliff:g> videoer ut av papirkurven?</item>
- <item quantity="one">Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> flytter denne videoen ut av papirkurven?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">Flytter <xliff:g id="COUNT">^1</xliff:g> videoer ut av papirkurven …</item>
- <item quantity="one">Flytter videoen ut av papirkurven …</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> flytter <xliff:g id="COUNT">^2</xliff:g> bilder ut av papirkurven?</item>
- <item quantity="one">Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> flytter dette bildet ut av papirkurven?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">Flytter <xliff:g id="COUNT">^1</xliff:g> bilder ut av papirkurven …</item>
- <item quantity="one">Flytter bildet ut av papirkurven …</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> flytter <xliff:g id="COUNT">^2</xliff:g> elementer ut av papirkurven?</item>
- <item quantity="one">Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> flytter dette elementet ut av papirkurven?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">Flytter <xliff:g id="COUNT">^1</xliff:g> elementer ut av papirkurven …</item>
- <item quantity="one">Flytter elementet ut av papirkurven …</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> sletter <xliff:g id="COUNT">^2</xliff:g> lydfiler?</item>
- <item quantity="one">Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> sletter denne lydfilen?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">Sletter <xliff:g id="COUNT">^1</xliff:g> lydfiler …</item>
- <item quantity="one">Sletter lydfilen …</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> sletter <xliff:g id="COUNT">^2</xliff:g> videoer?</item>
- <item quantity="one">Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> sletter denne videoen?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">Sletter <xliff:g id="COUNT">^1</xliff:g> videoer …</item>
- <item quantity="one">Sletter videoen …</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> sletter <xliff:g id="COUNT">^2</xliff:g> bilder?</item>
- <item quantity="one">Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> sletter dette bildet?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">Sletter <xliff:g id="COUNT">^1</xliff:g> bilder …</item>
- <item quantity="one">Sletter bildet …</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> sletter <xliff:g id="COUNT">^2</xliff:g> elementer?</item>
- <item quantity="one">Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> sletter dette elementet?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">Sletter <xliff:g id="COUNT">^1</xliff:g> elementer …</item>
- <item quantity="one">Sletter elementet …</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Legg til"</string>
+ <string name="deselect" msgid="4297825044827769490">"Fjern merking"</string>
+ <string name="deselected" msgid="8488133193326208475">"Ikke valgt"</string>
+ <string name="select" msgid="2704765470563027689">"Velg"</string>
+ <string name="selected" msgid="9151797369975828124">"Valgt"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Velg opptil <xliff:g id="COUNT_0">^1</xliff:g> element}other{Velg opptil <xliff:g id="COUNT_1">^1</xliff:g> elementer}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Nylige"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Ingen bilder eller videoer"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Ingen album"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Vis valgte"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Bilder"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Album"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Forhåndsvisning"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Bytt til jobbprofilen"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Bytt til den personlige profilen"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Blokkert av administratoren din"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Du får ikke tilgang til jobbdata fra personlige apper"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Du får ikke tilgang til personlige data fra jobbapper"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Jobbapper er satt på pause"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"For å åpne jobbilder, slå på jobbapper og prøv på nytt"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Denne appen har bare tilgang til bildene du velger"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> element}other{<xliff:g id="COUNT_1">^1</xliff:g> elementer}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Legg til (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Nedlastinger"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Favoritter"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Skjermdumper"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Levende bilde"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> – tatt <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Videoen ble spilt inn <xliff:g id="TIME">%1$s</xliff:g> med varigheten <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Bilde"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Levende bilde"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Kutt lyden i videoen"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Slå på lyden i videoen"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Spill av videoen"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Sett videoen på pause"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Skymedier er nå tilgjengelige fra <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"ikke valgt"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> endrer denne lydfilen?}other{Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> endrer <xliff:g id="COUNT">^2</xliff:g> lydfiler?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Endrer lydfilen …}other{Endrer <xliff:g id="COUNT">^1</xliff:g> lydfiler …}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> endrer denne videoen?}other{Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> endrer <xliff:g id="COUNT">^2</xliff:g> videoer?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Endrer videoen …}other{Endrer <xliff:g id="COUNT">^1</xliff:g> videoer …}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> endrer dette bildet?}other{Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> endrer <xliff:g id="COUNT">^2</xliff:g> bilder?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Endrer bildet …}other{Endrer <xliff:g id="COUNT">^1</xliff:g> bilder …}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> endrer dette elementet?}other{Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> endrer <xliff:g id="COUNT">^2</xliff:g> elementer?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Endrer elementet …}other{Endrer <xliff:g id="COUNT">^1</xliff:g> elementer …}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> flytter denne lydfilen til papirkurven?}other{Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> flytter <xliff:g id="COUNT">^2</xliff:g> lydfiler til papirkurven?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Flytter lydfilen til papirkurven …}other{Flytter <xliff:g id="COUNT">^1</xliff:g> lydfiler til papirkurven …}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> flytter denne videoen til papirkurven?}other{Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> flytter <xliff:g id="COUNT">^2</xliff:g> videoer til papirkurven?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Flytter videoen til papirkurven …}other{Flytter <xliff:g id="COUNT">^1</xliff:g> videoer til papirkurven …}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> flytter dette bildet til papirkurven?}other{Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> flytter <xliff:g id="COUNT">^2</xliff:g> bilder til papirkurven?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Flytter bildet til papirkurven …}other{Flytter <xliff:g id="COUNT">^1</xliff:g> bilder til papirkurven …}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> flytter dette elementet til papirkurven?}other{Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> flytter <xliff:g id="COUNT">^2</xliff:g> elementer til papirkurven?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Flytter elementet til papirkurven …}other{Flytter <xliff:g id="COUNT">^1</xliff:g> elementer til papirkurven …}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> flytter denne lydfilen ut av papirkurven?}other{Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> flytter <xliff:g id="COUNT">^2</xliff:g> lydfiler ut av papirkurven?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Flytter lydfilen ut av papirkurven …}other{Flytter <xliff:g id="COUNT">^1</xliff:g> lydfiler ut av papirkurven …}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> flytter denne videoen ut av papirkurven?}other{Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> flytter <xliff:g id="COUNT">^2</xliff:g> videoer ut av papirkurven?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Flytter videoen ut av papirkurven …}other{Flytter <xliff:g id="COUNT">^1</xliff:g> videoer ut av papirkurven …}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> flytter dette bildet ut av papirkurven?}other{Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> flytter <xliff:g id="COUNT">^2</xliff:g> bilder ut av papirkurven?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Flytter bildet ut av papirkurven …}other{Flytter <xliff:g id="COUNT">^1</xliff:g> bilder ut av papirkurven …}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> flytter dette elementet ut av papirkurven?}other{Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> flytter <xliff:g id="COUNT">^2</xliff:g> elementer ut av papirkurven?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Flytter elementet ut av papirkurven …}other{Flytter <xliff:g id="COUNT">^1</xliff:g> elementer ut av papirkurven …}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> sletter denne lydfilen?}other{Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> sletter <xliff:g id="COUNT">^2</xliff:g> lydfiler?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Sletter lydfilen …}other{Sletter <xliff:g id="COUNT">^1</xliff:g> lydfiler …}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> sletter denne videoen?}other{Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> sletter <xliff:g id="COUNT">^2</xliff:g> videoer?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Sletter videoen …}other{Sletter <xliff:g id="COUNT">^1</xliff:g> videoer …}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> sletter dette bildet?}other{Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> sletter <xliff:g id="COUNT">^2</xliff:g> bilder?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Sletter bildet …}other{Sletter <xliff:g id="COUNT">^1</xliff:g> bilder …}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Vil du tillate at <xliff:g id="APP_NAME_0">^1</xliff:g> sletter dette elementet?}other{Vil du tillate at <xliff:g id="APP_NAME_1">^1</xliff:g> sletter <xliff:g id="COUNT">^2</xliff:g> elementer?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Sletter elementet …}other{Sletter <xliff:g id="COUNT">^1</xliff:g> elementer …}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> kan ikke behandle mediefiler"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Behandlingen av mediene er avbrutt"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Feil under behandling av mediene"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index cd177d2..b04e978 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"मिडिया"</string>
<string name="storage_description" msgid="4081716890357580107">"स्थानीय भण्डारण"</string>
<string name="app_label" msgid="9035307001052716210">"मिडिया भण्डारण"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"फोटो पिकर"</string>
<string name="artist_label" msgid="8105600993099120273">"कलाकार"</string>
<string name="unknown" msgid="2059049215682829375">"अज्ञात"</string>
<string name="root_images" msgid="5861633549189045666">"फोटो"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"जारी राख्नुहोस्"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"अनुमति दिइयोस्"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"अनुमति नदिनुहोस्"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">थप <xliff:g id="COUNT_1">^1</xliff:g> वटा अतिरिक्त वस्तु</item>
- <item quantity="one">थप <xliff:g id="COUNT_0">^1</xliff:g> अतिरिक्त वस्तु</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{थप <xliff:g id="COUNT_0">^1</xliff:g> वटा अतिरिक्त वस्तु}other{थप <xliff:g id="COUNT_1">^1</xliff:g> वटा अतिरिक्त वस्तु}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"एपका अस्थायी फाइलहरू हटाउनुहोस्"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> केही अस्थायी फाइलहरू मेटाउन चाहन्छ। यो कार्य गर्ने अनुमति दिनुभएका खण्डमा ब्याट्रीको खपत वा सेलुलर डेटाको प्रयोग बढ्न सक्छ।"</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"एपका अस्थायी फाइलहरू मेटाउँदै…"</string>
<string name="clear" msgid="5524638938415865915">"हटाउनुहोस्"</string>
<string name="allow" msgid="8885707816848569619">"अनुमति दिनुहोस्"</string>
<string name="deny" msgid="6040983710442068936">"अस्वीकार गर्नुहोस्"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> लाई यी <xliff:g id="COUNT">^2</xliff:g> अडियो फाइलहरू परिमार्जन गर्न दिने हो?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो अडियो फाइल परिमार्जन गर्न दिने हो?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> वटा अडियो फाइल परिमार्जन गरिँदै छन्…</item>
- <item quantity="one">अडियो फाइल परिमार्जन गरिँदै छ…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> लाई यी <xliff:g id="COUNT">^2</xliff:g> भिडियोहरू परिमार्जन गर्न दिने हो?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो भिडियो परिमार्जन गर्न दिने हो?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> वटा भिडियो परिमार्जन गरिँदै छन्…</item>
- <item quantity="one">भिडियो परिमार्जन गरिँदै छ…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> लाई यी <xliff:g id="COUNT">^2</xliff:g> फोटोहरू परिमार्जन गर्न दिने हो?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो फोटो परिमार्जन गर्न दिने हो?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> वटा फोटो परिमार्जन गरिँदै छन्…</item>
- <item quantity="one">फोटो परिमार्जन गरिँदै छ…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> लाई यी <xliff:g id="COUNT">^2</xliff:g> वस्तुहरू परिमार्जन गर्न दिने हो?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो वस्तु परिमार्जन गर्न दिने हो?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> वटा वस्तु परिमार्जन गरिँदै छन्…</item>
- <item quantity="one">वस्तु परिमार्जन गरिँदै छ…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> लाई यी <xliff:g id="COUNT">^2</xliff:g> अडियो फाइलहरू सारेर ट्र्यासमा लैजान दिने हो?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो अडियो फाइल सारेर ट्र्यासमा लैजान दिने हो?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> वटा अडियो फाइल सारेर ट्र्यासमा लगिँदै छन्…</item>
- <item quantity="one">अडियो फाइल सारेर ट्र्यासमा लगिँदै छ…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> लाई यी <xliff:g id="COUNT">^2</xliff:g> भिडियोहरू सारेर ट्र्यासमा लैजान दिने हो?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो भिडियो सारेर ट्र्यासमा लैजान दिने हो?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> वटा भिडियो सारेर ट्र्यासमा लगिँदै छन्…</item>
- <item quantity="one">भिडियो सारेर ट्र्यासमा लगिँदै छ…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> लाई यी <xliff:g id="COUNT">^2</xliff:g> फोटोहरू सारेर ट्र्यासमा लैजान दिने हो?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो फोटो सारेर ट्र्यासमा लैजान दिने हो?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> वटा फोटो सारेर ट्र्यासमा लगिँदै छन्…</item>
- <item quantity="one">फोटो सारेर ट्र्यासमा लगिँदै छ…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> लाई यी <xliff:g id="COUNT">^2</xliff:g> वस्तुहरू सारेर ट्र्यासमा लैजान दिने हो?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो वस्तु सारेर ट्र्यासमा लैजान दिने हो?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> वटा वस्तु सारेर ट्र्यासमा लगिँदै छन्…</item>
- <item quantity="one">वस्तु सारेर ट्र्यासमा लगिँदै छ…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> लाई यी <xliff:g id="COUNT">^2</xliff:g> अडियो फाइलहरू रद्दीको टोकरीबाट निकालेर ल्याउन दिने हो?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो अडियो फाइल रद्दीको टोकरीबाट निकालेर ल्याउन दिने हो?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">ट्र्यासबाट <xliff:g id="COUNT">^1</xliff:g> वटा अडियो फाइल सारिँदै छन्…</item>
- <item quantity="one">ट्र्यासबाट अडियो फाइल सारिँदै छ…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> लाई यी <xliff:g id="COUNT">^2</xliff:g> भिडियोहरू रद्दीको टोकरीबाट निकालेर ल्याउन दिने हो?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो भिडियो रद्दीको टोकरीबाट निकालेर ल्याउन दिने हो?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">ट्र्यासबाट <xliff:g id="COUNT">^1</xliff:g> वटा भिडियो सारिँदै छन्…</item>
- <item quantity="one">ट्र्यासबाट भिडियो सारिँदै छ…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> लाई यी <xliff:g id="COUNT">^2</xliff:g> फोटोहरू रद्दीको टोकरीबाट निकालेर ल्याउन दिने हो?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो फोटो रद्दीको टोकरीबाट निकालेर ल्याउन दिने हो?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">ट्र्यासबाट <xliff:g id="COUNT">^1</xliff:g> वटा फोटो सारिँदै छन्…</item>
- <item quantity="one">ट्र्यासबाट फोटो सारिँदै छ…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> लाई यी <xliff:g id="COUNT">^2</xliff:g> वस्तुहरू रद्दीको टोकरीबाट निकालेर ल्याउन दिने हो?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो वस्तु रद्दीको टोकरीबाट निकालेर ल्याउन दिने हो?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">ट्र्यासबाट <xliff:g id="COUNT">^1</xliff:g> वटा वस्तु सारिँदै छन्…</item>
- <item quantity="one">ट्र्यासबाट वस्तु सारिँदै छ…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> लाई यी <xliff:g id="COUNT">^2</xliff:g> अडियो फाइलहरू मेटाउन दिने हो?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो अडियो फाइल मेटाउन दिने हो?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> वटा अडियो फाइल मेटाइँदै छन्…</item>
- <item quantity="one">अडियो फाइल मेटाइँदै छ…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> लाई यी <xliff:g id="COUNT">^2</xliff:g> भिडियोहरू मेटाउन दिने हो?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो भिडियो मेटाउन दिने हो?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> वटा भिडियो मेटाइँदै छन्…</item>
- <item quantity="one">भिडियो मेटाइँदै छ…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> लाई यी <xliff:g id="COUNT">^2</xliff:g> फोटोहरू मेटाउन दिने हो?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो फोटो मेटाउन दिने हो?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> वटा फोटो मेटाइँदै छन्…</item>
- <item quantity="one">फोटो मेटाइँदै छ…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> लाई यी <xliff:g id="COUNT">^2</xliff:g> वस्तुहरू मेटाउन दिने हो?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो वस्तु मेटाउन दिने हो?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> वटा वस्तु मेटाइँदै छन्…</item>
- <item quantity="one">वस्तु मेटाइँदै छ…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"हाल्नुहोस्"</string>
+ <string name="deselect" msgid="4297825044827769490">"चयन रद्द गर्नुहोस्"</string>
+ <string name="deselected" msgid="8488133193326208475">"चयन रद्द गरियो"</string>
+ <string name="select" msgid="2704765470563027689">"चयन गर्नुहोस्"</string>
+ <string name="selected" msgid="9151797369975828124">"चयन गरियो"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{बढीमा <xliff:g id="COUNT_0">^1</xliff:g> वटा वस्तु चयन गर्नुहोस्}other{बढीमा <xliff:g id="COUNT_1">^1</xliff:g> वटा वस्तु चयन गर्नुहोस्}}"</string>
+ <string name="recent" msgid="6694613584743207874">"हालसालैका"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"कुनै पनि फोटो वा भिडियो छैन"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"कुनै पनि एल्बम छैन"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"चयन गरिएका फोटो तथा भिडियोहरू हेर्नुहोस्"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"फोटोहरू"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"एल्बमहरू"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"प्रिभ्यू"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"कार्य प्रोफाइल प्रयोग गर्नुहोस्"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"व्यक्तिगत प्रोफाइल प्रयोग गर्नुहोस्"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"तपाईंका एड्मिनले ब्लक गरेका छन्"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"व्यक्तिगत एपमार्फत कामसम्बन्धी डेटा हेर्ने वा प्रयोग गर्ने अनुमति दिइएको छैन"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"कामसम्बन्धी एपमार्फत व्यक्तिगत जानकारी हेर्ने वा प्रयोग गर्ने अनुमति दिइएको छैन।"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"कामसम्बन्धी एपहरू पज गरिएका छन्"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"कामसम्बन्धी फोटोहरू खोल्न आफ्ना कामसम्बन्धी एपहरू अन गर्नुहोस् अनि फेरि प्रयास गर्नुहोस्"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"यो एपले तपाईंले चयन गरेका फोटो मात्र प्रयोग गर्न सक्छ"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> वटा वस्तु}other{<xliff:g id="COUNT_1">^1</xliff:g> वटा वस्तु}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"थप्नुहोस् (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"क्यामेरा"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"डाउनलोडहरू"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"मन पर्ने कुराहरू"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"स्क्रिनसटहरू"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"मोसन फोटो"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="TIME">%2$s</xliff:g> खिचिएको समय: <xliff:g id="ITEM_NAME">%1$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"<xliff:g id="TIME">%1$s</xliff:g> मा खचिएको <xliff:g id="DURATION">%2$s</xliff:g> अवधिको भिडियो"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"फोटो"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"मोसन फोटो"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"भिडियो म्युट गर्नुहोस्"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"भिडियो अनम्युट गर्नुहोस्"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"भिडियो प्ले गर्नुहोस्"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"भिडियो पज गर्नुहोस्"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"क्लाउड मिडिया अब <xliff:g id="PKG_NAME">%1$s</xliff:g> मा उपलब्ध छ"</string>
+ <string name="not_selected" msgid="2244008151669896758">"चयन गरिएको छैन"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो अडियो फाइल परिमार्जन गर्न दिने हो?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> लाई <xliff:g id="COUNT">^2</xliff:g> वटा अडियो फाइल परिमार्जन गर्न दिने हो?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{अडियो फाइल परिमार्जन गरिँदै छ…}other{<xliff:g id="COUNT">^1</xliff:g> वटा अडियो फाइल परिमार्जन गरिँदै छन्…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो भिडियो परिमार्जन गर्न दिने हो?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> लाई <xliff:g id="COUNT">^2</xliff:g> वटा भिडियो परिमार्जन गर्न दिने हो?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{भिडियो परिमार्जन गरिँदै छ…}other{<xliff:g id="COUNT">^1</xliff:g> वटा भिडियो परिमार्जन गरिँदै छन्…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो फोटो परिमार्जन गर्न दिने हो?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> लाई <xliff:g id="COUNT">^2</xliff:g> वटा फोटो परिमार्जन गर्न दिने हो?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{फोटो परिमार्जन गरिँदै छ…}other{<xliff:g id="COUNT">^1</xliff:g> वटा फोटो परिमार्जन गरिँदै छन्…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो वस्तु परिमार्जन गर्न दिने हो?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> लाई <xliff:g id="COUNT">^2</xliff:g> वटा वस्तु परिमार्जन गर्न दिने हो?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{वस्तु परिमार्जन गरिँदै छ…}other{<xliff:g id="COUNT">^1</xliff:g> वटा वस्तु परिमार्जन गरिँदै छन्…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो अडियो फाइल सारेर ट्र्यासमा लैजान दिने हो?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> लाई <xliff:g id="COUNT">^2</xliff:g> वटा अडियो फाइल सारेर ट्र्यासमा लैजान दिने हो?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{अडियो फाइल सारेर ट्र्यासमा लगिँदै छ…}other{<xliff:g id="COUNT">^1</xliff:g> वटा अडियो फाइल सारेर ट्र्यासमा लगिँदै छन्…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो भिडियो सारेर ट्र्यासमा लैजान दिने हो?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> लाई <xliff:g id="COUNT">^2</xliff:g> वटा भिडियो सारेर ट्र्यासमा लैजान दिने हो?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{भिडियो सारेर ट्र्यासमा लगिँदै छ…}other{<xliff:g id="COUNT">^1</xliff:g> वटा भिडियो सारेर ट्र्यासमा लगिँदै छन्…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो फोटो सारेर ट्र्यासमा लैजान दिने हो?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> लाई <xliff:g id="COUNT">^2</xliff:g> वटा फोटो सारेर ट्र्यासमा लैजान दिने हो?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{फोटो सारेर ट्र्यासमा लगिँदै छ…}other{<xliff:g id="COUNT">^1</xliff:g> वटा फोटो सारेर ट्र्यासमा लगिँदै छन्…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो वस्तु सारेर ट्र्यासमा लैजान दिने हो?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> लाई <xliff:g id="COUNT">^2</xliff:g> वटा वस्तु सारेर ट्र्यासमा लैजान दिने हो?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{वस्तु सारेर ट्र्यासमा लगिँदै छ…}other{<xliff:g id="COUNT">^1</xliff:g> वटा वस्तु सारेर ट्र्यासमा लगिँदै छन्…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो अडियो फाइल ट्र्यासबाट बाहिर निकाल्न दिने हो?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> लाई <xliff:g id="COUNT">^2</xliff:g> वटा अडियो फाइल ट्र्यासबाट बाहिर निकाल्न दिने हो?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{अडियो फाइल ट्र्यासबाट बाहिर निकालिँदै छ…}other{<xliff:g id="COUNT">^1</xliff:g> वटा अडियो फाइल ट्र्यासबाट बाहिर निकालिँदै छन्…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो भिडियो ट्र्यासबाट बाहिर निकाल्न दिने हो?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> लाई <xliff:g id="COUNT">^2</xliff:g> वटा भिडियो ट्र्यासबाट बाहिर निकाल्न दिने हो?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{भिडियो ट्र्यासबाट बाहिर निकालिँदै छ…}other{<xliff:g id="COUNT">^1</xliff:g> वटा भिडियो ट्र्यासबाट बाहिर निकालिँदै छन्…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो फोटो ट्र्यासबाट बाहिर निकाल्न दिने हो?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> लाई <xliff:g id="COUNT">^2</xliff:g> वटा फोटो ट्र्यासबाट बाहिर निकाल्न दिने हो?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{फोटो ट्र्यासबाट बाहिर निकालिँदै छ…}other{<xliff:g id="COUNT">^1</xliff:g> वटा फोटो ट्र्यासबाट बाहिर निकालिँदै छन्…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो वस्तु ट्र्यासबाट बाहिर निकाल्न दिने हो?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> लाई <xliff:g id="COUNT">^2</xliff:g> वटा वस्तु ट्र्यासबाट बाहिर निकाल्न दिने हो?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{वस्तु ट्र्यासबाट बाहिर निकालिँदै छ…}other{<xliff:g id="COUNT">^1</xliff:g> वटा वस्तु ट्र्यासबाट बाहिर निकालिँदै छन्…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो अडियो फाइल मेटाउन दिने हो?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> लाई <xliff:g id="COUNT">^2</xliff:g> वटा अडियो फाइल मेटाउन दिने हो?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{अडियो फाइल मेटाइँदै छ…}other{<xliff:g id="COUNT">^1</xliff:g> वटा अडियो फाइल मेटाइँदै छन्…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो भिडियो मेटाउन दिने हो?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> लाई <xliff:g id="COUNT">^2</xliff:g> वटा भिडियो मेटाउन दिने हो?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{भिडियो मेटाइँदै छ…}other{<xliff:g id="COUNT">^1</xliff:g> वटा भिडियो मेटाइँदै छन्…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो फोटो मेटाउन दिने हो?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> लाई <xliff:g id="COUNT">^2</xliff:g> वटा फोटो मेटाउन दिने हो?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{फोटो मेटाइँदै छ…}other{<xliff:g id="COUNT">^1</xliff:g> वटा फोटो मेटाइँदै छन्…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो वस्तु मेटाउन दिने हो?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> लाई <xliff:g id="COUNT">^2</xliff:g> वटा वस्तु मेटाउन दिने हो?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{वस्तु मेटाइँदै छ…}other{<xliff:g id="COUNT">^1</xliff:g> वटा वस्तु मेटाइँदै छन्…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> ले मिडिया फाइलहरू प्रयोग गर्न सक्दैन"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"मिडिया प्रोसेस गर्ने कार्य रद्द गरियो"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"मिडिया प्रोसेस गर्ने क्रममा त्रुटि भयो"</string>
diff --git a/res/values-night-v31/colors.xml b/res/values-night-v31/colors.xml
new file mode 100644
index 0000000..a9e43f7
--- /dev/null
+++ b/res/values-night-v31/colors.xml
@@ -0,0 +1,23 @@
+<?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.
+ -->
+
+<resources>
+
+ <!-- PhotoPicker -->
+ <color name="picker_background_color">@android:color/system_neutral1_800</color>
+
+</resources>
diff --git a/res/values-night-v31/styles.xml b/res/values-night-v31/styles.xml
new file mode 100644
index 0000000..1c120aa
--- /dev/null
+++ b/res/values-night-v31/styles.xml
@@ -0,0 +1,39 @@
+<?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.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <style name="PickerMaterialTheme" parent="@style/Theme.Material3.DayNight.NoActionBar">
+ <item name="materialAlertDialogTheme">@style/ProfileDialogTheme</item>
+ <item name="pickerDragBarColor">@android:color/system_neutral1_700</item>
+ <item name="pickerHighlightColor">@android:color/system_accent1_100</item>
+ <item name="pickerHighlightTextColor">?android:attr/textColorPrimaryInverse</item>
+ <item name="pickerProfileButtonColor">@android:color/system_accent1_100</item>
+ <item name="pickerDisabledProfileButtonColor">@android:color/system_neutral1_700</item>
+ <item name="pickerProfileButtonTextColor">?android:attr/textColorPrimaryInverse</item>
+ <item name="pickerDisabledProfileButtonTextColor">?android:attr/textColorTertiary</item>
+ <item name="pickerSelectedTabBackgroundColor">@android:color/system_accent1_100</item>
+ <item name="pickerSelectedTabTextColor">?android:attr/textColorPrimaryInverse</item>
+ <item name="pickerTabBackgroundColor">?android:attr/colorBackground</item>
+ <item name="pickerTextColor">?android:attr/textColorSecondary</item>
+ <item name="pickerSelectedColor">@android:color/system_accent1_300</item>
+ <item name="pickerProfileDialogButtonAndIconColor">@android:color/system_accent1_300</item>
+ <item name="pickerProfileDialogTitleColor">?android:attr/textColorPrimaryInverse</item>
+ <item name="pickerProfileDialogBodyColor">?android:attr/textColorSecondaryInverse</item>
+ <item name="pickerProfileDialogBackgroundColor">@android:color/system_neutral1_800</item>
+ </style>
+
+</resources>
diff --git a/res/values-night/colors.xml b/res/values-night/colors.xml
index 1478eb2..648c6d7 100644
--- a/res/values-night/colors.xml
+++ b/res/values-night/colors.xml
@@ -18,4 +18,7 @@
<resources>
<color name="thumb_gray_color">#3C4043</color>
<color name="clear_cache_icon_color">#DADCE0</color>
+
+ <!-- PhotoPicker -->
+ <color name="picker_background_color">#202124</color>
</resources>
diff --git a/res/values-night/styles.xml b/res/values-night/styles.xml
index 9b4da23..f13b5ea 100644
--- a/res/values-night/styles.xml
+++ b/res/values-night/styles.xml
@@ -35,4 +35,24 @@
<item name="android:alertDialogTheme">@style/AlertDialogTheme</item>
</style>
+ <style name="PickerMaterialTheme" parent="@style/Theme.MaterialComponents.DayNight.NoActionBar">
+ <item name="materialAlertDialogTheme">@style/ProfileDialogTheme</item>
+ <item name="pickerDragBarColor">#686868</item>
+ <item name="pickerHighlightColor">?android:attr/colorAccent</item>
+ <item name="pickerHighlightTextColor">#202124</item>
+ <item name="pickerProfileButtonColor">#1F1F1F</item>
+ <item name="pickerDisabledProfileButtonColor">#444746</item>
+ <item name="pickerProfileButtonTextColor">#A8C7FA</item>
+ <item name="pickerDisabledProfileButtonTextColor">#428F8F8F</item>
+ <item name="pickerSelectedTabBackgroundColor">#3D8AB4F8</item>
+ <item name="pickerSelectedTabTextColor">#8AB4F8</item>
+ <item name="pickerTabBackgroundColor">@color/picker_background_color</item>
+ <item name="pickerTextColor">?android:attr/textColorSecondary</item>
+ <item name="pickerSelectedColor">?android:attr/colorAccent</item>
+ <item name="pickerProfileDialogButtonAndIconColor">#669DF6</item>
+ <item name="pickerProfileDialogTitleColor">#F1F3F4</item>
+ <item name="pickerProfileDialogBodyColor">#DADCE0</item>
+ <item name="pickerProfileDialogBackgroundColor">@android:color/black</item>
+ </style>
+
</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index dcd8a36..7b45f1b 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Fotokiezer"</string>
<string name="artist_label" msgid="8105600993099120273">"Artiest"</string>
<string name="unknown" msgid="2059049215682829375">"Onbekend"</string>
<string name="root_images" msgid="5861633549189045666">"Afbeeldingen"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Doorgaan"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Toestaan"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Weigeren"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Plus <xliff:g id="COUNT_1">^1</xliff:g> extra items</item>
- <item quantity="one">Plus <xliff:g id="COUNT_0">^1</xliff:g> extra item</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Plus <xliff:g id="COUNT_0">^1</xliff:g> extra item}other{Plus <xliff:g id="COUNT_1">^1</xliff:g> extra items}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Tijdelijke app-bestanden wissen"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> wil een aantal tijdelijke bestanden wissen. Dit kan leiden tot een groter verbruik van de batterij of mobiele data."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Tijdelijke app-bestanden wissen…"</string>
<string name="clear" msgid="5524638938415865915">"Wissen"</string>
<string name="allow" msgid="8885707816848569619">"Toestaan"</string>
<string name="deny" msgid="6040983710442068936">"Weigeren"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> audiobestanden te wijzigen?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> toestaan dit audiobestand te wijzigen?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> audiobestanden aanpassen…</item>
- <item quantity="one">Audiobestand aanpassen…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> video\'s te wijzigen?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> toestaan deze video te wijzigen?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> video\'s aanpassen…</item>
- <item quantity="one">Video aanpassen…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> foto\'s te wijzigen?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> toestaan deze foto te wijzigen?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> foto\'s aanpassen…</item>
- <item quantity="one">Foto aanpassen…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> items te wijzigen?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> toestaan dit item te wijzigen?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> items aanpassen…</item>
- <item quantity="one">Item aanpassen…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> audiobestanden naar de prullenbak te verplaatsen?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> toestaan dit audiobestand naar de prullenbak te verplaatsen?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> audiobestanden naar prullenbak verplaatsen…</item>
- <item quantity="one">Audiobestand naar prullenbak verplaatsen…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> video\'s naar de prullenbak te verplaatsen?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> toestaan deze video naar de prullenbak te verplaatsen?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> video\'s naar prullenbak verplaatsen…</item>
- <item quantity="one">Video naar prullenbak verplaatsen…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> foto\'s naar de prullenbak te verplaatsen?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> toestaan deze foto naar de prullenbak te verplaatsen?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> foto\'s naar prullenbak verplaatsen…</item>
- <item quantity="one">Foto naar prullenbak verplaatsen…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> items naar de prullenbak te verplaatsen?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> toestaan dit item naar de prullenbak te verplaatsen?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> items naar prullenbak verplaatsen…</item>
- <item quantity="one">Item naar prullenbak verplaatsen…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> audiobestanden uit de prullenbak te halen?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> toestaan dit audiobestand uit de prullenbak te halen?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> audiobestanden uit prullenbak halen…</item>
- <item quantity="one">Audiobestand uit prullenbak halen…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> video\'s uit de prullenbak te halen?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> toestaan deze video uit de prullenbak te halen?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> video\'s uit prullenbak halen…</item>
- <item quantity="one">Video uit prullenbak halen…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> foto\'s uit de prullenbak te halen?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> toestaan deze foto uit de prullenbak te halen?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> foto\'s uit prullenbak halen…</item>
- <item quantity="one">Foto uit prullenbak halen…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> items uit de prullenbak te halen?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> toestaan dit item uit de prullenbak te halen?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> items uit prullenbak halen…</item>
- <item quantity="one">Item uit prullenbak halen…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> audiobestanden te verwijderen?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> toestaan dit audiobestand te verwijderen?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> audiobestanden verwijderen…</item>
- <item quantity="one">Audiobestand verwijderen…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> video\'s te verwijderen?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> toestaan deze video te verwijderen?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> video\'s verwijderen…</item>
- <item quantity="one">Video verwijderen…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> foto\'s te verwijderen?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> toestaan deze foto te verwijderen?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> foto\'s verwijderen…</item>
- <item quantity="one">Foto verwijderen…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> items te verwijderen?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> toestaan dit item te verwijderen?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> items verwijderen…</item>
- <item quantity="one">Item verwijderen…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Toevoegen"</string>
+ <string name="deselect" msgid="4297825044827769490">"Deselecteren"</string>
+ <string name="deselected" msgid="8488133193326208475">"Gedeselecteerd"</string>
+ <string name="select" msgid="2704765470563027689">"Selecteren"</string>
+ <string name="selected" msgid="9151797369975828124">"Geselecteerd"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Selecteer maximaal <xliff:g id="COUNT_0">^1</xliff:g> item}other{Selecteer maximaal <xliff:g id="COUNT_1">^1</xliff:g> items}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Recent"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Geen foto\'s of video\'s"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Geen albums"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Selectie bekijken"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Foto\'s"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albums"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Voorbeeld"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Overschakelen naar werkprofiel"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Overschakelen naar persoonlijk profiel"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Geblokkeerd door je beheerder"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Het is niet toegestaan om werkgegevens te openen via een persoonlijke app"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Het is niet toegestaan om persoonsgegevens te openen via een werk-app"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Werk-apps zijn onderbroken"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Als je werkfoto\'s wilt openen, zet je je werk-apps aan en probeer je het opnieuw"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Deze app heeft alleen toegang tot de foto\'s die je selecteert"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}other{<xliff:g id="COUNT_1">^1</xliff:g> items}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Toevoegen (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Camera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Downloads"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Favorieten"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Screenshots"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Bewegingsfoto"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> gemaakt op <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Video opgenomen op <xliff:g id="TIME">%1$s</xliff:g> met een duur van <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Foto"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"Gif"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Bewegingsfoto"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Geluid van video uitzetten"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Geluid van video aanzetten"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Video afspelen"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Video onderbreken"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Cloudmedia nu beschikbaar van <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"niet geselecteerd"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> toestaan dit audiobestand aan te passen?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> audiobestanden aan te passen?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Audiobestand aanpassen…}other{<xliff:g id="COUNT">^1</xliff:g> audiobestanden aanpassen…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> toestaan deze video aan te passen?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> video\'s aan te passen?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Video aanpassen…}other{<xliff:g id="COUNT">^1</xliff:g> video\'s aanpassen…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> toestaan deze foto aan te passen?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> foto\'s aan te passen?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Foto aanpassen…}other{<xliff:g id="COUNT">^1</xliff:g> foto\'s aanpassen…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> toestaan dit item aan te passen?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> items aan te passen?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Item aanpassen…}other{<xliff:g id="COUNT">^1</xliff:g> items aanpassen…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> toestaan dit audiobestand naar de prullenbak te verplaatsen?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> audiobestanden naar de prullenbak te verplaatsen?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Audiobestand naar prullenbak verplaatsen…}other{<xliff:g id="COUNT">^1</xliff:g> audiobestanden naar prullenbak verplaatsen…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> toestaan deze video naar de prullenbak te verplaatsen?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> video\'s naar de prullenbak te verplaatsen?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Video naar prullenbak verplaatsen…}other{<xliff:g id="COUNT">^1</xliff:g> video\'s naar prullenbak verplaatsen…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> toestaan deze foto naar de prullenbak te verplaatsen?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> foto\'s naar de prullenbak te verplaatsen?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Foto naar prullenbak verplaatsen…}other{<xliff:g id="COUNT">^1</xliff:g> foto\'s naar prullenbak verplaatsen…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> toestaan dit item naar de prullenbak te verplaatsen?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> items naar de prullenbak te verplaatsen?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Item naar prullenbak verplaatsen…}other{<xliff:g id="COUNT">^1</xliff:g> items naar prullenbak verplaatsen…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> toestaan dit audiobestand uit de prullenbak te halen?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> audiobestanden uit de prullenbak te halen?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Audiobestand uit prullenbak halen…}other{<xliff:g id="COUNT">^1</xliff:g> audiobestanden uit prullenbak halen…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> toestaan deze video uit de prullenbak te halen?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> video\'s uit de prullenbak te halen?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Video uit prullenbak halen…}other{<xliff:g id="COUNT">^1</xliff:g> video\'s uit prullenbak halen…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> toestaan deze foto uit de prullenbak te halen?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> foto\'s uit de prullenbak te halen?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Foto uit prullenbak halen…}other{<xliff:g id="COUNT">^1</xliff:g> foto\'s uit prullenbak halen…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> toestaan dit item uit de prullenbak te halen?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> items uit de prullenbak te halen?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Item uit prullenbak halen…}other{<xliff:g id="COUNT">^1</xliff:g> items uit prullenbak halen…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> toestaan dit audiobestand te verwijderen?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> audiobestanden te verwijderen?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Audiobestand verwijderen…}other{<xliff:g id="COUNT">^1</xliff:g> audiobestanden verwijderen…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> toestaan deze video te verwijderen?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> video\'s te verwijderen?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Video verwijderen…}other{<xliff:g id="COUNT">^1</xliff:g> video\'s verwijderen…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> toestaan deze foto te verwijderen?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> foto\'s te verwijderen?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Foto verwijderen…}other{<xliff:g id="COUNT">^1</xliff:g> foto\'s verwijderen…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> toestaan dit item te verwijderen?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> toestaan <xliff:g id="COUNT">^2</xliff:g> items te verwijderen?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Item verwijderen…}other{<xliff:g id="COUNT">^1</xliff:g> items verwijderen…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> kan geen mediabestanden verwerken"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Mediaverwerking geannuleerd"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Fout bij mediaverwerking"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index d052a54..0e594d0 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"ମିଡିଆ"</string>
<string name="storage_description" msgid="4081716890357580107">"ଲୋକାଲ୍ ଷ୍ଟୋରେଜ୍"</string>
<string name="app_label" msgid="9035307001052716210">"ମିଡିଆ ଷ୍ଟୋରେଜ୍"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"ଫଟୋ ପିକର"</string>
<string name="artist_label" msgid="8105600993099120273">"କଳାକାର"</string>
<string name="unknown" msgid="2059049215682829375">"ଅଜଣା"</string>
<string name="root_images" msgid="5861633549189045666">"ଇମେଜ୍"</string>
@@ -29,154 +30,93 @@
<string name="permission_required_action" msgid="706370952366113539">"ଜାରି ରଖନ୍ତୁ"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"ଅନୁମତି ଦିଅନ୍ତୁ"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"ଅଗ୍ରାହ୍ୟ କରନ୍ତୁ"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">ଅଧିକ <xliff:g id="COUNT_1">^1</xliff:g>ଟି ଅତିରିକ୍ତ ଆଇଟମ୍</item>
- <item quantity="one">ଅଧିକ <xliff:g id="COUNT_0">^1</xliff:g>ଟି ଅତିରିକ୍ତ ଆଇଟମ୍</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{ଏହା ସହିତ <xliff:g id="COUNT_0">^1</xliff:g>ଟି ଅତିରିକ୍ତ ଆଇଟମ}other{ଏହା ସହିତ <xliff:g id="COUNT_1">^1</xliff:g>ଟି ଅତିରିକ୍ତ ଆଇଟମ}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"ଅସ୍ଥାୟୀ ଆପ୍ ଫାଇଲଗୁଡ଼ିକୁ ଖାଲି କରନ୍ତୁ"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> କିଛି ଅସ୍ଥାୟୀ ଫାଇଲ୍ ଖାଲି କରିବାକୁ ଚାହୁଁଛି। ଏହା ଫଳରେ ବ୍ୟାଟେରୀ କିମ୍ବା ସେଲ୍ୟୁଲାର୍ ଡାଟାର ବ୍ୟବହାର ଅଧିକ ହୋଇପାରେ।"</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"ଅସ୍ଥାୟୀ ଆପ୍ ଫାଇଲଗୁଡ଼ିକୁ ଖାଲି କରାଯାଉଛି…"</string>
<string name="clear" msgid="5524638938415865915">"ଖାଲି କରନ୍ତୁ"</string>
<string name="allow" msgid="8885707816848569619">"ଅନୁମତି ଦିଅନ୍ତୁ"</string>
<string name="deny" msgid="6040983710442068936">"ଅଗ୍ରାହ୍ୟ କରନ୍ତୁ"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g>ଟି ଅଡିଓ ଫାଇଲକୁ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- <item quantity="one">ଏହି ଅଡିଓ ଫାଇଲକୁ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>ଟି ଅଡିଓ ଫାଇଲ୍ ପରିବର୍ତ୍ତନ କରାଯାଉଛି…</item>
- <item quantity="one">ଅଡିଓ ଫାଇଲ୍ ପରିବର୍ତ୍ତନ କରାଯାଉଛି…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g>ଟି ଭିଡିଓକୁ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- <item quantity="one">ଏହି ଭିଡିଓକୁ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>ଟି ଭିଡିଓ ପରିବର୍ତ୍ତନ କରାଯାଉଛି…</item>
- <item quantity="one">ଭିଡିଓ ପରିବର୍ତ୍ତନ କରାଯାଉଛି…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g>ଟି ଫଟୋ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- <item quantity="one">ଏହି ଫଟୋକୁ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>ଟି ଫଟୋ ପରିବର୍ତ୍ତନ କରାଯାଉଛି…</item>
- <item quantity="one">ଫଟୋ ପରିବର୍ତ୍ତନ କରାଯାଉଛି…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g>ଟି ଆଇଟମକୁ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- <item quantity="one">ଏହି ଆଇଟମକୁ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>ଟି ଆଇଟମ୍ ପରିବର୍ତ୍ତନ କରାଯାଉଛି…</item>
- <item quantity="one">ଆଇଟମ୍ ପରିବର୍ତ୍ତନ କରାଯାଉଛି…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g>ଟି ଅଡିଓ ଫାଇଲକୁ ଟ୍ରାସକୁ ମୁଭ୍ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- <item quantity="one">ଏହି ଅଡିଓ ଫାଇଲକୁ ଟ୍ରାସକୁ ମୁଭ୍ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>ଟି ଅଡିଓ ଫାଇଲ୍ ଟ୍ରାସକୁ ମୁଭ୍ କରାଯାଉଛି…</item>
- <item quantity="one">ଅଡିଓ ଫାଇଲ୍ ଟ୍ରାସକୁ ମୁଭ୍ କରାଯାଉଛି…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g>ଟି ଭିଡିଓକୁ ଟ୍ରାସକୁ ମୁଭ୍ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- <item quantity="one">ଏହି ଭିଡିଓକୁ ଟ୍ରାସକୁ ମୁଭ୍ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>ଟି ଭିଡିଓ ଟ୍ରାସକୁ ମୁଭ୍ କରାଯାଉଛି…</item>
- <item quantity="one">ଭିଡିଓ ଟ୍ରାସକୁ ମୁଭ୍ କରାଯାଉଛି…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g>ଟି ଫଟୋକୁ ଟ୍ରାସକୁ ମୁଭ୍ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- <item quantity="one">ଏହି ଫଟୋକୁ ଟାସକୁ ମୁଭ୍ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>ଟି ଫଟୋ ଟ୍ରାସକୁ ମୁଭ୍ କରାଯାଉଛି…</item>
- <item quantity="one">ଫଟୋ ଟ୍ରାସକୁ ମୁଭ୍ କରାଯାଉଛି…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g>ଟି ଆଇଟମକୁ ଟ୍ରାସକୁ ମୁଭ୍ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- <item quantity="one">ଏହି ଆଇଟମକୁ ଟ୍ରାସକୁ ମୁଭ୍ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>ଟି ଆଇଟମ୍ ଟ୍ରାସକୁ ମୁଭ୍ କରାଯାଉଛି…</item>
- <item quantity="one">ଆଇଟମ୍ ଟ୍ରାସକୁ ମୁଭ୍ କରାଯାଉଛି…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g>ଟି ଅଡିଓ ଫାଇଲକୁ ଟ୍ରାସରୁ ବାହାର କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- <item quantity="one">ଏହି ଅଡିଓ ଫାଇଲକୁ ଟ୍ରାସରୁ ବାହାର କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>ଟି ଅଡିଓ ଫାଇଲକୁ ଟ୍ରାସରୁ ମୁଭ୍ କରାଯାଉଛି…</item>
- <item quantity="one">ଅଡିଓ ଫାଇଲକୁ ଟ୍ରାସରୁ ମୁଭ୍ କରାଯାଉଛି…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g>ଟି ଭିଡିଓକୁ ଟ୍ରାସରୁ ବାହାର କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- <item quantity="one">ଏହି ଭିଡିଓକୁ ଟ୍ରାସରୁ ବାହାର କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>ଟି ଭିଡିଓକୁ ଟ୍ରାସରୁ ମୁଭ୍ କରାଯାଉଛି…</item>
- <item quantity="one">ଭିଡିଓକୁ ଟ୍ରାସରୁ ମୁଭ୍ କରାଯାଉଛି…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g>ଟି ଫଟୋକୁ ଟ୍ରାସରୁ ବାହାର କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- <item quantity="one">ଏହି ଫଟୋକୁ ଟ୍ରାସରୁ ବାହାର କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>ଟି ଫଟୋକୁ ଟ୍ରାସରୁ ମୁଭ୍ କରାଯାଉଛି…</item>
- <item quantity="one">ଫଟୋକୁ ଟ୍ରାସରୁ ମୁଭ୍ କରାଯାଉଛି…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g>ଟି ଆଇଟମକୁ ଟ୍ରାସରୁ ବାହାର କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- <item quantity="one">ଏହି ଆଇଟମକୁ ଟ୍ରାସରୁ ବାହାର କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>ଟି ଆଇଟମକୁ ଟ୍ରାସରୁ ମୁଭ୍ କରାଯାଉଛି…</item>
- <item quantity="one">ଆଇଟମକୁ ଟ୍ରାସରୁ ମୁଭ୍ କରାଯାଉଛି…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g>ଟି ଅଡିଓ ଫାଇଲକୁ ଡିଲିଟ୍ କରିବାକୁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- <item quantity="one">ଏହି ଅଡିଓ ଫାଇଲକୁ ଡିଲିଟ୍ କରିବାକୁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>ଟି ଅଡିଓ ଫାଇଲ୍ ଡିଲିଟ୍ କରାଯାଉଛି…</item>
- <item quantity="one">ଅଡିଓ ଫାଇଲ୍ ଡିଲିଟ୍ କରାଯାଉଛି…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g>ଟି ଭିଡିଓକୁ ଡିଲିଟ୍ କରିବାକୁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- <item quantity="one">ଏହି ଭିଡିଓକୁ ଡିଲିଟ୍ କରିବାକୁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>ଟି ଭିଡିଓ ଡିଲିଟ୍ କରାଯାଉଛି…</item>
- <item quantity="one">ଭିଡିଓ ଡିଲିଟ୍ କରାଯାଉଛି…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g>ଟି ଫଟୋକୁ ଡିଲିଟ୍ କରିବାକୁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- <item quantity="one">ଏହି ଫଟୋକୁ ଡିଲିଟ୍ କରିବାକୁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>ଟି ଫଟୋ ଡିଲିଟ୍ କରାଯାଉଛି…</item>
- <item quantity="one">ଫଟୋ ଡିଲିଟ୍ କରାଯାଉଛି…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g>ଟି ଆଇଟମକୁ ଡିଲିଟ୍ କରିବାକୁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- <item quantity="one">ଏହି ଆଇଟମକୁ ଡିଲିଟ୍ କରିବାକୁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>ଟି ଆଇଟମ୍ ଡିଲିଟ୍ କରାଯାଉଛି…</item>
- <item quantity="one">ଆଇଟମ୍ ଡିଲିଟ୍ କରାଯାଉଛି…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"ଯୋଗ କରନ୍ତୁ"</string>
+ <string name="deselect" msgid="4297825044827769490">"ଅଚୟନ କରନ୍ତୁ"</string>
+ <string name="deselected" msgid="8488133193326208475">"ଅଚୟନ କରାଯାଇଛି"</string>
+ <string name="select" msgid="2704765470563027689">"ଚୟନ କରନ୍ତୁ"</string>
+ <string name="selected" msgid="9151797369975828124">"ଚୟନ କରାଯାଇଛି"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g>ଟି ପର୍ଯ୍ୟନ୍ତ ଆଇଟମ ଚୟନ କରନ୍ତୁ}other{<xliff:g id="COUNT_1">^1</xliff:g>ଟି ପର୍ଯ୍ୟନ୍ତ ଆଇଟମ ଚୟନ କରନ୍ତୁ}}"</string>
+ <string name="recent" msgid="6694613584743207874">"ବର୍ତ୍ତମାନର"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"କୌଣସି ଫଟୋ କିମ୍ବା ଭିଡିଓ ନାହିଁ"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"କୌଣସି ଆଲବମ ନାହିଁ"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"ଚୟନିତଗୁଡ଼ିକୁ ଦେଖନ୍ତୁ"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"ଫଟୋ"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"ଆଲବମ୍"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"ପ୍ରିଭ୍ୟୁ"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"ୱାର୍କକୁ ସ୍ୱିଚ୍ କରନ୍ତୁ"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"ବ୍ୟକ୍ତିଗତକୁ ସ୍ୱିଚ୍ କରନ୍ତୁ"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"ଆପଣଙ୍କ ଆଡମିନଙ୍କ ଦ୍ୱାରା ବ୍ଲକ୍ କରାଯାଇଛି"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"କୌଣସି ବ୍ୟକ୍ତିଗତ ଆପରୁ ୱାର୍କ ଡାଟାକୁ ଆକ୍ସେସ୍ କରିବା ପାଇଁ ଅନୁମତି ଦିଆଯାଇନାହିଁ"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"କୌଣସି ୱାର୍କ ଆପରୁ ବ୍ୟକ୍ତିଗତ ଡାଟାକୁ ଆକ୍ସେସ୍ କରିବା ପାଇଁ ଅନୁମତି ଦିଆଯାଇନାହିଁ"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"ୱାର୍କ ଆପଗୁଡ଼ିକୁ ବିରତ କରାଯାଇଛି"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"ୱାର୍କ ଫଟୋଗୁଡ଼ିକୁ ଖୋଲିବାକୁ, ଆପଣଙ୍କ ୱାର୍କ ଆପଗୁଡ଼ିକୁ ଚାଲୁ କରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"ଏହି ଆପ କେବଳ ଆପଣ ଚୟନ କରିଥିବା ଫଟୋଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିପାରିବ"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g>ଟି ଆଇଟମ}other{<xliff:g id="COUNT_1">^1</xliff:g>ଟି ଆଇଟମ}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"(<xliff:g id="COUNT">^1</xliff:g>)ଟି ଯୋଗ କରନ୍ତୁ"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"କ୍ୟାମେରା"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"ଡାଉନଲୋଡଗୁଡ଼ିକ"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"ପସନ୍ଦଗୁଡ଼ିକ"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"ସ୍କ୍ରିନସଟଗୁଡ଼ିକ"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"ମୋସନ ଫଟୋ"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="TIME">%2$s</xliff:g>ରେ <xliff:g id="ITEM_NAME">%1$s</xliff:g> ନିଆଯାଇଛି"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"<xliff:g id="DURATION">%2$s</xliff:g> ଅବଧି ସହିତ <xliff:g id="TIME">%1$s</xliff:g>ରେ ନିଆଯାଇଥିବା ଭିଡିଓ"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"ଫଟୋ"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"ମୋସନ ଫଟୋ"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"ଭିଡିଓକୁ ମ୍ୟୁଟ କରନ୍ତୁ"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"ଭିଡିଓକୁ ଅନମ୍ୟୁଟ କରନ୍ତୁ"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"ଭିଡିଓ ଚଲାନ୍ତୁ"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"ଭିଡିଓକୁ ବିରତ କରନ୍ତୁ"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"ବର୍ତ୍ତମାନ <xliff:g id="PKG_NAME">%1$s</xliff:g>ରୁ କ୍ଲାଉଡ ମିଡିଆ ଉପଲବ୍ଧ ଅଛି"</string>
+ <string name="not_selected" msgid="2244008151669896758">"ଚୟନ କରାଯାଇନାହିଁ"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{ଏହି ଅଡିଓ ଫାଇଲକୁ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}other{<xliff:g id="COUNT">^2</xliff:g>ଟି ଅଡିଓ ଫାଇଲକୁ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{ଅଡିଓ ଫାଇଲ ପରିବର୍ତ୍ତନ କରାଯାଉଛି…}other{<xliff:g id="COUNT">^1</xliff:g>ଟି ଅଡିଓ ଫାଇଲ ପରିବର୍ତ୍ତନ କରାଯାଉଛି…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{ଏହି ଭିଡିଓକୁ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}other{<xliff:g id="COUNT">^2</xliff:g>ଟି ଭିଡିଓକୁ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{ଭିଡିଓ ପରିବର୍ତ୍ତନ କରାଯାଉଛି…}other{<xliff:g id="COUNT">^1</xliff:g>ଟି ଭିଡିଓ ପରିବର୍ତ୍ତନ କରାଯାଉଛି…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{ଏହି ଫଟୋକୁ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}other{<xliff:g id="COUNT">^2</xliff:g>ଟି ଫଟୋକୁ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{ଫଟୋ ପରିବର୍ତ୍ତନ କରାଯାଉଛି…}other{<xliff:g id="COUNT">^1</xliff:g>ଟି ଫଟୋ ପରିବର୍ତ୍ତନ କରାଯାଉଛି…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{ଏହି ଆଇଟମକୁ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}other{<xliff:g id="COUNT">^2</xliff:g>ଟି ଆଇଟମକୁ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{ଆଇଟମ ପରିବର୍ତ୍ତନ କରାଯାଉଛି…}other{<xliff:g id="COUNT">^1</xliff:g>ଟି ଆଇଟମ ପରିବର୍ତ୍ତନ କରାଯାଉଛି…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{ଏହି ଅଡିଓ ଫାଇଲକୁ ଟ୍ରାସକୁ ମୁଭ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}other{<xliff:g id="COUNT">^2</xliff:g>ଟି ଅଡିଓ ଫାଇଲକୁ ଟ୍ରାସକୁ ମୁଭ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{ଅଡିଓ ଫାଇଲ ଟ୍ରାସକୁ ମୁଭ କରାଯାଉଛି…}other{<xliff:g id="COUNT">^1</xliff:g>ଟି ଅଡିଓ ଫାଇଲ ଟ୍ରାସକୁ ମୁଭ କରାଯାଉଛି…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{ଏହି ଭିଡିଓକୁ ଟ୍ରାସକୁ ମୁଭ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}other{<xliff:g id="COUNT">^2</xliff:g>ଟି ଭିଡିଓକୁ ଟ୍ରାସକୁ ମୁଭ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{ଭିଡିଓ ଟ୍ରାସକୁ ମୁଭ କରାଯାଉଛି…}other{<xliff:g id="COUNT">^1</xliff:g>ଟି ଭିଡିଓ ଟ୍ରାସକୁ ମୁଭ କରାଯାଉଛି…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{ଏହି ଫଟୋକୁ ଟାସକୁ ମୁଭ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}other{<xliff:g id="COUNT">^2</xliff:g>ଟି ଫଟୋକୁ ଟ୍ରାସକୁ ମୁଭ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{ଫଟୋ ଟ୍ରାସକୁ ମୁଭ କରାଯାଉଛି…}other{<xliff:g id="COUNT">^1</xliff:g>ଟି ଫଟୋ ଟ୍ରାସକୁ ମୁଭ କରାଯାଉଛି…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{ଏହି ଆଇଟମକୁ ଟ୍ରାସକୁ ମୁଭ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}other{<xliff:g id="COUNT">^2</xliff:g>ଟି ଆଇଟମକୁ ଟ୍ରାସକୁ ମୁଭ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{ଆଇଟମ ଟ୍ରାସକୁ ମୁଭ କରାଯାଉଛି…}other{<xliff:g id="COUNT">^1</xliff:g>ଟି ଆଇଟମ ଟ୍ରାସକୁ ମୁଭ କରାଯାଉଛି…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{ଏହି ଅଡିଓ ଫାଇଲକୁ ଟ୍ରାସରୁ ମୁଭ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}other{<xliff:g id="COUNT">^2</xliff:g>ଟି ଅଡିଓ ଫାଇଲକୁ ଟ୍ରାସରୁ ମୁଭ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{ଅଡିଓ ଫାଇଲକୁ ଟ୍ରାସରୁ ମୁଭ କରାଯାଉଛି…}other{<xliff:g id="COUNT">^1</xliff:g>ଟି ଅଡିଓ ଫାଇଲକୁ ଟ୍ରାସରୁ ମୁଭ କରାଯାଉଛି…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{ଏହି ଭିଡିଓକୁ ଟ୍ରାସରୁ ମୁଭ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}other{<xliff:g id="COUNT">^2</xliff:g>ଟି ଭିଡିଓକୁ ଟ୍ରାସରୁ ମୁଭ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{ଭିଡିଓକୁ ଟ୍ରାସରୁ ମୁଭ କରାଯାଉଛି…}other{<xliff:g id="COUNT">^1</xliff:g>ଟି ଭିଡିଓକୁ ଟ୍ରାସରୁ ମୁଭ କରାଯାଉଛି…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{ଏହି ଫଟୋକୁ ଟ୍ରାସରୁ ମୁଭ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}other{<xliff:g id="COUNT">^2</xliff:g>ଟି ଫଟୋକୁ ଟ୍ରାସରୁ ମୁଭ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{ଫଟୋକୁ ଟ୍ରାସରୁ ମୁଭ କରାଯାଉଛି…}other{<xliff:g id="COUNT">^1</xliff:g>ଟି ଫଟୋକୁ ଟ୍ରାସରୁ ମୁଭ କରାଯାଉଛି…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{ଏହି ଆଇଟମକୁ ଟ୍ରାସରୁ ମୁଭ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}other{<xliff:g id="COUNT">^2</xliff:g>ଟି ଆଇଟମକୁ ଟ୍ରାସରୁ ମୁଭ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{ଆଇଟମକୁ ଟ୍ରାସରୁ ମୁଭ କରାଯାଉଛି…}other{<xliff:g id="COUNT">^1</xliff:g>ଟି ଆଇଟମକୁ ଟ୍ରାସରୁ ମୁଭ କରାଯାଉଛି…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{ଏହି ଅଡିଓ ଫାଇଲକୁ ଡିଲିଟ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}other{<xliff:g id="COUNT">^2</xliff:g>ଟି ଅଡିଓ ଫାଇଲକୁ ଡିଲିଟ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{ଅଡିଓ ଫାଇଲ ଡିଲିଟ କରାଯାଉଛି…}other{<xliff:g id="COUNT">^1</xliff:g>ଟି ଅଡିଓ ଫାଇଲ ଡିଲିଟ କରାଯାଉଛି…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{ଏହି ଭିଡିଓକୁ ଡିଲିଟ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}other{<xliff:g id="COUNT">^2</xliff:g>ଟି ଭିଡିଓକୁ ଡିଲିଟ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{ଭିଡିଓ ଡିଲିଟ କରାଯାଉଛି…}other{<xliff:g id="COUNT">^1</xliff:g>ଟି ଭିଡିଓ ଡିଲିଟ କରାଯାଉଛି…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{ଏହି ଫଟୋକୁ ଡିଲିଟ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}other{<xliff:g id="COUNT">^2</xliff:g>ଟି ଫଟୋକୁ ଡିଲିଟ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{ଫଟୋ ଡିଲିଟ କରାଯାଉଛି…}other{<xliff:g id="COUNT">^1</xliff:g>ଟି ଫଟୋ ଡିଲିଟ କରାଯାଉଛି…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{ଏହି ଆଇଟମକୁ ଡିଲିଟ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}other{<xliff:g id="COUNT">^2</xliff:g>ଟି ଆଇଟମକୁ ଡିଲିଟ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{ଆଇଟମ ଡିଲିଟ କରାଯାଉଛି…}other{<xliff:g id="COUNT">^1</xliff:g>ଟି ଆଇଟମ ଡିଲିଟ କରାଯାଉଛି…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> ମିଡିଆ ଫାଇଲଗୁଡ଼ିକୁ ପ୍ରକ୍ରିୟାନ୍ୱିତ କରିପାରିବ ନାହିଁ"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"ମିଡିଆ ପ୍ରକ୍ରିୟାକରଣ ବାତିଲ୍ କରାଯାଇଛି"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"ମିଡିଆ ପ୍ରକ୍ରିୟାକରଣ ତ୍ରୁଟି"</string>
<string name="transcode_processing_success" msgid="447288876429730122">"ମିଡିଆ ପ୍ରକ୍ରିୟାକରଣ ସଫଳ ହୋଇଛି"</string>
<string name="transcode_processing_started" msgid="7789086308155361523">"ମିଡିଆ ପ୍ରକ୍ରିୟାକରଣ ଆରମ୍ଭ କରାଯାଇଛି"</string>
<string name="transcode_processing" msgid="6753136468864077258">"ମିଡିଆ ପ୍ରକ୍ରିୟାକରଣ କରାଯାଉଛି…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"ବାତିଲ୍ କରନ୍ତୁ"</string>
+ <string name="transcode_cancel" msgid="8555752601907598192">"ବାତିଲ କରନ୍ତୁ"</string>
<string name="transcode_wait" msgid="8909773149560697501">"ଅପେକ୍ଷା କରନ୍ତୁ"</string>
</resources>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 0838494..407abb1 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"ਮੀਡੀਆ"</string>
<string name="storage_description" msgid="4081716890357580107">"ਸਥਾਨਕ ਸਟੋਰੇਜ"</string>
<string name="app_label" msgid="9035307001052716210">"ਮੀਡੀਆ ਸਟੋਰੇਜ"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"ਫ਼ੋਟੋ ਚੋਣਕਾਰ"</string>
<string name="artist_label" msgid="8105600993099120273">"ਕਲਾਕਾਰ"</string>
<string name="unknown" msgid="2059049215682829375">"ਅਗਿਆਤ"</string>
<string name="root_images" msgid="5861633549189045666">"ਚਿੱਤਰ"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"ਜਾਰੀ ਰੱਖੋ"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"ਕਰਨ ਦਿਓ"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"ਨਾ ਕਰਨ ਦਿਓ"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">ਇਸ ਤੋਂ ਇਲਾਵਾ <xliff:g id="COUNT_1">^1</xliff:g> ਵਾਧੂ ਆਈਟਮ</item>
- <item quantity="other">ਇਸ ਤੋਂ ਇਲਾਵਾ <xliff:g id="COUNT_1">^1</xliff:g> ਵਾਧੂ ਆਈਟਮਾਂ</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}one{+<xliff:g id="COUNT_1">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{ਇਸ ਤੋਂ ਇਲਾਵਾ <xliff:g id="COUNT_0">^1</xliff:g> ਵਾਧੂ ਆਈਟਮ}one{ਇਸ ਤੋਂ ਇਲਾਵਾ <xliff:g id="COUNT_1">^1</xliff:g> ਵਾਧੂ ਆਈਟਮ}other{ਇਸ ਤੋਂ ਇਲਾਵਾ <xliff:g id="COUNT_1">^1</xliff:g> ਵਾਧੂ ਆਈਟਮਾਂ}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"ਅਸਥਾਈ ਐਪ ਫ਼ਾਈਲਾਂ ਨੂੰ ਕਲੀਅਰ ਕਰੋ"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ਕੁਝ ਅਸਥਾਈ ਫ਼ਾਈਲਾਂ ਕਲੀਅਰ ਕਰਨਾ ਚਾਹੁੰਦੀ ਹੈ। ਇਸਦੇ ਨਤੀਜੇ ਵਜੋਂ ਬੈਟਰੀ ਜਾਂ ਸੈਲਿਊਲਰ ਡਾਟਾ ਦੀ ਵਰਤੋਂ ਵਧ ਸਕਦੀ ਹੈ।"</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"ਅਸਥਾਈ ਐਪ ਫ਼ਾਈਲਾਂ ਨੂੰ ਕਲੀਅਰ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
<string name="clear" msgid="5524638938415865915">"ਕਲੀਅਰ ਕਰੋ"</string>
<string name="allow" msgid="8885707816848569619">"ਆਗਿਆ ਦਿਓ"</string>
<string name="deny" msgid="6040983710442068936">"ਨਾ ਕਰਨ ਦਿਓ"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲ ਨੂੰ ਸੋਧਣ ਦੇਣਾ ਹੈ?</item>
- <item quantity="other">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲਾਂ ਨੂੰ ਸੋਧਣ ਦੇਣਾ ਹੈ?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲ ਸੋਧੀ ਜਾ ਰਹੀ ਹੈ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲਾਂ ਸੋਧੀਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਵੀਡੀਓ ਨੂੰ ਸੋਧਣ ਦੇਣਾ ਹੈ?</item>
- <item quantity="other">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਵੀਡੀਓ ਨੂੰ ਸੋਧਣ ਦੇਣਾ ਹੈ?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ਵੀਡੀਓ ਸੋਧਿਆ ਜਾ ਰਿਹਾ ਹੈ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ਵੀਡੀਓ ਸੋਧੇ ਜਾ ਰਹੇ ਹਨ…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਫ਼ੋਟੋ ਨੂੰ ਸੋਧਣ ਦੇਣਾ ਹੈ?</item>
- <item quantity="other">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਫ਼ੋਟੋਆਂ ਨੂੰ ਸੋਧਣ ਦੇਣਾ ਹੈ?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ਫ਼ੋਟੋ ਸੋਧੀ ਜਾ ਰਹੀ ਹੈ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ਫ਼ੋਟੋਆਂ ਸੋਧੀਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਈਟਮ ਨੂੰ ਸੋਧਣ ਦੇਣਾ ਹੈ?</item>
- <item quantity="other">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਈਟਮਾਂ ਨੂੰ ਸੋਧਣ ਦੇਣਾ ਹੈ?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ਆਈਟਮ ਸੋਧੀ ਜਾ ਰਹੀ ਹੈ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ਆਈਟਮਾਂ ਸੋਧੀਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲ ਨੂੰ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?</item>
- <item quantity="other">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲਾਂ ਨੂੰ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਈ ਜਾ ਰਹੀ ਹੈ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲਾਂ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਈਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਵੀਡੀਓ ਨੂੰ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?</item>
- <item quantity="other">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਵੀਡੀਓ ਨੂੰ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ਵੀਡੀਓ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਇਆ ਜਾ ਰਿਹਾ ਹੈ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ਵੀਡੀਓ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਏ ਜਾ ਰਹੇ ਹਨ…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਫ਼ੋਟੋ ਨੂੰ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਉਣ ਦੇਣਾ ਹੈ?</item>
- <item quantity="other">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਫ਼ੋਟੋਆਂ ਨੂੰ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਉਣ ਦੇਣਾ ਹੈ?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ਫ਼ੋਟੋ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਈ ਜਾ ਰਹੀ ਹੈ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ਫ਼ੋਟੋਆਂ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਈਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਈਟਮ ਨੂੰ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?</item>
- <item quantity="other">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਈਟਮਾਂ ਨੂੰ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ਆਈਟਮ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਈ ਜਾ ਰਹੀ ਹੈ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ਆਈਟਮਾਂ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਈਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲ ਨੂੰ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?</item>
- <item quantity="other">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲਾਂ ਨੂੰ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਈ ਜਾ ਰਹੀ ਹੈ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲਾਂ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਈਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਵੀਡੀਓ ਨੂੰ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?</item>
- <item quantity="other">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਵੀਡੀਓ ਨੂੰ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ਵੀਡੀਓ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਇਆ ਜਾ ਰਿਹਾ ਹੈ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ਵੀਡੀਓ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਏ ਜਾ ਰਹੇ ਹਨ…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਫ਼ੋਟੋ ਨੂੰ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਉਣ ਦੇਣਾ ਹੈ?</item>
- <item quantity="other">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਫ਼ੋਟੋਆਂ ਨੂੰ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਉਣ ਦੇਣਾ ਹੈ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ਫ਼ੋਟੋ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਈ ਜਾ ਰਹੀ ਹੈ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ਫ਼ੋਟੋਆਂ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਈਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਈਟਮ ਨੂੰ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?</item>
- <item quantity="other">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਈਟਮਾਂ ਨੂੰ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ਆਈਟਮ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਈ ਜਾ ਰਹੀ ਹੈ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ਆਈਟਮਾਂ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਈਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲ ਨੂੰ ਮਿਟਾਉਣ ਦੇਣਾ ਹੈ?</item>
- <item quantity="other">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲਾਂ ਨੂੰ ਮਿਟਾਉਣ ਦੇਣਾ ਹੈ?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲ ਮਿਟਾਈ ਜਾ ਰਹੀ ਹੈ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲਾਂ ਮਿਟਾਈਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਵੀਡੀਓ ਨੂੰ ਮਿਟਾਉਣ ਦੇਣਾ ਹੈ?</item>
- <item quantity="other">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਵੀਡੀਓ ਨੂੰ ਮਿਟਾਉਣ ਦੇਣਾ ਹੈ?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ਵੀਡੀਓ ਮਿਟਾਇਆ ਜਾ ਰਿਹਾ ਹੈ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ਵੀਡੀਓ ਮਿਟਾਏ ਜਾ ਰਹੇ ਹਨ…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਫ਼ੋਟੋ ਨੂੰ ਮਿਟਾਉਣ ਦੇਣਾ ਹੈ?</item>
- <item quantity="other">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਫ਼ੋਟੋਆਂ ਨੂੰ ਮਿਟਾਉਣ ਦੇਣਾ ਹੈ?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ਫ਼ੋਟੋ ਮਿਟਾਈ ਜਾ ਰਹੀ ਹੈ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ਫ਼ੋਟੋਆਂ ਮਿਟਾਈਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਈਟਮ ਨੂੰ ਮਿਟਾਉਣ ਦੇਣਾ ਹੈ?</item>
- <item quantity="other">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਈਟਮਾਂ ਨੂੰ ਮਿਟਾਉਣ ਦੇਣਾ ਹੈ?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ਆਈਟਮ ਮਿਟਾਈ ਜਾ ਰਹੀ ਹੈ…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ਆਈਟਮਾਂ ਮਿਟਾਈਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"ਸ਼ਾਮਲ ਕਰੋ"</string>
+ <string name="deselect" msgid="4297825044827769490">"ਅਣ-ਚੁਣਿਆ ਕਰੋ"</string>
+ <string name="deselected" msgid="8488133193326208475">"ਅਣ-ਚੁਣਿਆ"</string>
+ <string name="select" msgid="2704765470563027689">"ਚੁਣੋ"</string>
+ <string name="selected" msgid="9151797369975828124">"ਚੁਣਿਆ ਗਿਆ"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> ਤੱਕ ਆਈਟਮ ਚੁਣੋ}one{<xliff:g id="COUNT_1">^1</xliff:g> ਤੱਕ ਆਈਟਮ ਚੁਣੋ}other{<xliff:g id="COUNT_1">^1</xliff:g> ਤੱਕ ਆਈਟਮਾਂ ਚੁਣੋ}}"</string>
+ <string name="recent" msgid="6694613584743207874">"ਹਾਲੀਆ"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"ਕੋਈ ਫ਼ੋਟੋ ਜਾਂ ਵੀਡੀਓ ਨਹੀਂ"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"ਕੋਈ ਐਲਬਮ ਨਹੀਂ"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"ਚੁਣੀਆਂ ਗਈਆਂ ਆਈਟਮਾਂ ਦੇਖੋ"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"ਫ਼ੋਟੋਆਂ"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"ਐਲਬਮਾਂ"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"ਪੂਰਵ-ਝਲਕ"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"ਨਿੱਜੀ ਪ੍ਰੋਫਾਈਲ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਬਲਾਕ ਕੀਤਾ ਗਿਆ"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"ਨਿੱਜੀ ਐਪ ਤੋਂ ਕਾਰਜ-ਸਥਾਨ ਦੇ ਡਾਟੇ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"ਕੰਮ ਸੰਬੰਧੀ ਐਪ ਤੋਂ ਨਿੱਜੀ ਡਾਟੇ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਨੂੰ ਰੋਕਿਆ ਗਿਆ ਹੈ"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"ਕੰਮ ਸੰਬੰਧੀ ਫ਼ੋਟੋਆਂ ਖੋਲ੍ਹਣ ਲਈ, ਆਪਣੀਆਂ ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਨੂੰ ਚਾਲੂ ਕਰੋ ਅਤੇ ਫਿਰ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"ਇਹ ਐਪ ਸਿਰਫ਼ ਤੁਹਾਡੇ ਵੱਲੋਂ ਚੁਣੀਆਂ ਗਈਆਂ ਫ਼ੋਟੋਆਂ ਤੱਕ ਪਹੁੰਚ ਕਰ ਸਕਦੀ ਹੈ"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> ਆਈਟਮ}one{<xliff:g id="COUNT_1">^1</xliff:g> ਆਈਟਮ}other{<xliff:g id="COUNT_1">^1</xliff:g> ਆਈਟਮਾਂ}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"(<xliff:g id="COUNT">^1</xliff:g>) ਸ਼ਾਮਲ ਕਰੋ"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"ਕੈਮਰਾ"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"ਡਾਊਨਲੋਡ"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"ਮਨਪਸੰਦ"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"ਸਕ੍ਰੀਨਸ਼ਾਟ"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"ਮੋਸ਼ਨ ਫ਼ੋਟੋ"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> ਨੂੰ <xliff:g id="TIME">%2$s</xliff:g> \'ਤੇ ਲਿਆ ਗਿਆ"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"<xliff:g id="DURATION">%2$s</xliff:g> ਮਿਆਦ ਵਾਲੇ ਵੀਡੀਓ ਨੂੰ <xliff:g id="TIME">%1$s</xliff:g> ਨੂੰ ਬਣਾਇਆ ਗਿਆ"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"ਫ਼ੋਟੋ"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"ਮੋਸ਼ਨ ਫ਼ੋਟੋ"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"ਵੀਡੀਓ ਮਿਊਟ ਕਰੋ"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"ਵੀਡੀਓ ਅਣਮਿਊਟ ਕਰੋ"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"ਵੀਡੀਓ ਚਲਾਓ"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"ਵੀਡੀਓ ਰੋਕੋ"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"ਕਲਾਊਡ ਮੀਡੀਆ ਹੁਣ <xliff:g id="PKG_NAME">%1$s</xliff:g> ਤੋਂ ਉਪਲਬਧ ਹੈ"</string>
+ <string name="not_selected" msgid="2244008151669896758">"ਚੁਣਿਆ ਨਹੀਂ ਗਿਆ"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{ਕੀ <xliff:g id="APP_NAME_0">^1</xliff:g> ਨੂੰ ਇਸ ਆਡੀਓ ਫ਼ਾਈਲ ਨੂੰ ਸੋਧਣ ਦੇਣਾ ਹੈ?}one{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲ ਨੂੰ ਸੋਧਣ ਦੇਣਾ ਹੈ?}other{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲਾਂ ਨੂੰ ਸੋਧਣ ਦੇਣਾ ਹੈ?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{ਆਡੀਓ ਫ਼ਾਈਲ ਸੋਧੀ ਜਾ ਰਹੀ ਹੈ…}one{<xliff:g id="COUNT">^1</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲ ਸੋਧੀ ਜਾ ਰਹੀ ਹੈ…}other{<xliff:g id="COUNT">^1</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲਾਂ ਸੋਧੀਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{ਕੀ <xliff:g id="APP_NAME_0">^1</xliff:g> ਨੂੰ ਇਸ ਵੀਡੀਓ ਨੂੰ ਸੋਧਣ ਦੇਣਾ ਹੈ?}one{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਵੀਡੀਓ ਨੂੰ ਸੋਧਣ ਦੇਣਾ ਹੈ?}other{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਵੀਡੀਓ ਨੂੰ ਸੋਧਣ ਦੇਣਾ ਹੈ?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{ਵੀਡੀਓ ਸੋਧਿਆ ਜਾ ਰਿਹਾ ਹੈ…}one{<xliff:g id="COUNT">^1</xliff:g> ਵੀਡੀਓ ਸੋਧਿਆ ਜਾ ਰਿਹਾ ਹੈ…}other{<xliff:g id="COUNT">^1</xliff:g> ਵੀਡੀਓ ਸੋਧੇ ਜਾ ਰਹੇ ਹਨ…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{ਕੀ <xliff:g id="APP_NAME_0">^1</xliff:g> ਨੂੰ ਇਸ ਫ਼ੋਟੋ ਨੂੰ ਸੋਧਣ ਦੇਣਾ ਹੈ?}one{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਫ਼ੋਟੋ ਨੂੰ ਸੋਧਣ ਦੇਣਾ ਹੈ?}other{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਫ਼ੋਟੋਆਂ ਨੂੰ ਸੋਧਣ ਦੇਣਾ ਹੈ?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{ਫ਼ੋਟੋ ਸੋਧੀ ਜਾ ਰਹੀ ਹੈ…}one{<xliff:g id="COUNT">^1</xliff:g> ਫ਼ੋਟੋ ਸੋਧੀ ਜਾ ਰਹੀ ਹੈ…}other{<xliff:g id="COUNT">^1</xliff:g> ਫ਼ੋਟੋਆਂ ਸੋਧੀਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{ਕੀ <xliff:g id="APP_NAME_0">^1</xliff:g> ਨੂੰ ਇਸ ਆਈਟਮ ਨੂੰ ਸੋਧਣ ਦੇਣਾ ਹੈ?}one{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਈਟਮ ਨੂੰ ਸੋਧਣ ਦੇਣਾ ਹੈ?}other{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਈਟਮਾਂ ਨੂੰ ਸੋਧਣ ਦੇਣਾ ਹੈ?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{ਆਈਟਮ ਸੋਧੀ ਜਾ ਰਹੀ ਹੈ…}one{<xliff:g id="COUNT">^1</xliff:g> ਆਈਟਮ ਸੋਧੀ ਜਾ ਰਹੀ ਹੈ…}other{<xliff:g id="COUNT">^1</xliff:g> ਆਈਟਮਾਂ ਸੋਧੀਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{ਕੀ <xliff:g id="APP_NAME_0">^1</xliff:g> ਨੂੰ ਇਸ ਆਡੀਓ ਫ਼ਾਈਲ ਨੂੰ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?}one{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲ ਨੂੰ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?}other{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲਾਂ ਨੂੰ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{ਆਡੀਓ ਫ਼ਾਈਲ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਈ ਜਾ ਰਹੀ ਹੈ…}one{<xliff:g id="COUNT">^1</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਈ ਜਾ ਰਹੀ ਹੈ…}other{<xliff:g id="COUNT">^1</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲਾਂ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਈਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{ਕੀ <xliff:g id="APP_NAME_0">^1</xliff:g> ਨੂੰ ਇਸ ਵੀਡੀਓ ਨੂੰ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?}one{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਵੀਡੀਓ ਨੂੰ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?}other{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਵੀਡੀਓ ਨੂੰ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{ਵੀਡੀਓ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਇਆ ਜਾ ਰਿਹਾ ਹੈ…}one{<xliff:g id="COUNT">^1</xliff:g> ਵੀਡੀਓ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਇਆ ਜਾ ਰਿਹਾ ਹੈ…}other{<xliff:g id="COUNT">^1</xliff:g> ਵੀਡੀਓ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਏ ਜਾ ਰਹੇ ਹਨ…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{ਕੀ <xliff:g id="APP_NAME_0">^1</xliff:g> ਨੂੰ ਇਸ ਫ਼ੋਟੋ ਨੂੰ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਉਣ ਦੇਣਾ ਹੈ?}one{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਫ਼ੋਟੋ ਨੂੰ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਉਣ ਦੇਣਾ ਹੈ?}other{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਫ਼ੋਟੋਆਂ ਨੂੰ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਉਣ ਦੇਣਾ ਹੈ?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{ਫ਼ੋਟੋ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਈ ਜਾ ਰਹੀ ਹੈ…}one{<xliff:g id="COUNT">^1</xliff:g> ਫ਼ੋਟੋ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਈ ਜਾ ਰਹੀ ਹੈ…}other{<xliff:g id="COUNT">^1</xliff:g> ਫ਼ੋਟੋਆਂ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਈਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{ਕੀ <xliff:g id="APP_NAME_0">^1</xliff:g> ਨੂੰ ਇਸ ਆਈਟਮ ਨੂੰ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?}one{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਈਟਮ ਨੂੰ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?}other{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਈਟਮਾਂ ਨੂੰ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{ਆਈਟਮ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਈ ਜਾ ਰਹੀ ਹੈ…}one{<xliff:g id="COUNT">^1</xliff:g> ਆਈਟਮ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਈ ਜਾ ਰਹੀ ਹੈ…}other{<xliff:g id="COUNT">^1</xliff:g> ਆਈਟਮਾਂ ਰੱਦੀ ਵਿੱਚ ਲਿਜਾਈਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{ਕੀ <xliff:g id="APP_NAME_0">^1</xliff:g> ਨੂੰ ਇਸ ਆਡੀਓ ਫ਼ਾਈਲ ਨੂੰ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?}one{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲ ਨੂੰ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?}other{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲਾਂ ਨੂੰ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{ਆਡੀਓ ਫ਼ਾਈਲ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਈ ਜਾ ਰਹੀ ਹੈ…}one{<xliff:g id="COUNT">^1</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਈ ਜਾ ਰਹੀ ਹੈ…}other{<xliff:g id="COUNT">^1</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲਾਂ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਈਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{ਕੀ <xliff:g id="APP_NAME_0">^1</xliff:g> ਨੂੰ ਇਸ ਵੀਡੀਓ ਨੂੰ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?}one{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਵੀਡੀਓ ਨੂੰ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?}other{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਵੀਡੀਓ ਨੂੰ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{ਵੀਡੀਓ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਇਆ ਜਾ ਰਿਹਾ ਹੈ…}one{<xliff:g id="COUNT">^1</xliff:g> ਵੀਡੀਓ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਇਆ ਜਾ ਰਿਹਾ ਹੈ…}other{<xliff:g id="COUNT">^1</xliff:g> ਵੀਡੀਓ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਏ ਜਾ ਰਹੇ ਹਨ…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{ਕੀ <xliff:g id="APP_NAME_0">^1</xliff:g> ਨੂੰ ਇਸ ਫ਼ੋਟੋ ਨੂੰ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਉਣ ਦੇਣਾ ਹੈ?}one{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਫ਼ੋਟੋ ਨੂੰ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਉਣ ਦੇਣਾ ਹੈ?}other{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਫ਼ੋਟੋਆਂ ਨੂੰ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਉਣ ਦੇਣਾ ਹੈ?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{ਫ਼ੋਟੋ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਈ ਜਾ ਰਹੀ ਹੈ…}one{<xliff:g id="COUNT">^1</xliff:g> ਫ਼ੋਟੋ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਈ ਜਾ ਰਹੀ ਹੈ…}other{<xliff:g id="COUNT">^1</xliff:g> ਫ਼ੋਟੋਆਂ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਈਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{ਕੀ <xliff:g id="APP_NAME_0">^1</xliff:g> ਨੂੰ ਇਸ ਆਈਟਮ ਨੂੰ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?}one{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਈਟਮ ਨੂੰ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?}other{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਈਟਮਾਂ ਨੂੰ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਣ ਦੇਣਾ ਹੈ?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{ਆਈਟਮ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਈ ਜਾ ਰਹੀ ਹੈ…}one{<xliff:g id="COUNT">^1</xliff:g> ਆਈਟਮ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਈ ਜਾ ਰਹੀ ਹੈ…}other{<xliff:g id="COUNT">^1</xliff:g> ਆਈਟਮਾਂ ਰੱਦੀ ਤੋਂ ਬਾਹਰ ਲਿਜਾਈਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{ਕੀ <xliff:g id="APP_NAME_0">^1</xliff:g> ਨੂੰ ਇਸ ਆਡੀਓ ਫ਼ਾਈਲ ਨੂੰ ਮਿਟਾਉਣ ਦੇਣਾ ਹੈ?}one{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲ ਨੂੰ ਮਿਟਾਉਣ ਦੇਣਾ ਹੈ?}other{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲਾਂ ਨੂੰ ਮਿਟਾਉਣ ਦੇਣਾ ਹੈ?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{ਆਡੀਓ ਫ਼ਾਈਲ ਮਿਟਾਈ ਜਾ ਰਹੀ ਹੈ…}one{<xliff:g id="COUNT">^1</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲ ਮਿਟਾਈ ਜਾ ਰਹੀ ਹੈ…}other{<xliff:g id="COUNT">^1</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲਾਂ ਮਿਟਾਈਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{ਕੀ <xliff:g id="APP_NAME_0">^1</xliff:g> ਨੂੰ ਇਸ ਵੀਡੀਓ ਨੂੰ ਮਿਟਾਉਣ ਦੇਣਾ ਹੈ?}one{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਵੀਡੀਓ ਨੂੰ ਮਿਟਾਉਣ ਦੇਣਾ ਹੈ?}other{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਵੀਡੀਓ ਨੂੰ ਮਿਟਾਉਣ ਦੇਣਾ ਹੈ?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{ਵੀਡੀਓ ਮਿਟਾਇਆ ਜਾ ਰਿਹਾ ਹੈ…}one{<xliff:g id="COUNT">^1</xliff:g> ਵੀਡੀਓ ਮਿਟਾਇਆ ਜਾ ਰਿਹਾ ਹੈ…}other{<xliff:g id="COUNT">^1</xliff:g> ਵੀਡੀਓ ਮਿਟਾਏ ਜਾ ਰਹੇ ਹਨ…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{ਕੀ <xliff:g id="APP_NAME_0">^1</xliff:g> ਨੂੰ ਇਸ ਫ਼ੋਟੋ ਨੂੰ ਮਿਟਾਉਣ ਦੇਣਾ ਹੈ?}one{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਫ਼ੋਟੋ ਨੂੰ ਮਿਟਾਉਣ ਦੇਣਾ ਹੈ?}other{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਫ਼ੋਟੋਆਂ ਨੂੰ ਮਿਟਾਉਣ ਦੇਣਾ ਹੈ?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{ਫ਼ੋਟੋ ਮਿਟਾਈ ਜਾ ਰਹੀ ਹੈ…}one{<xliff:g id="COUNT">^1</xliff:g> ਫ਼ੋਟੋ ਮਿਟਾਈ ਜਾ ਰਹੀ ਹੈ…}other{<xliff:g id="COUNT">^1</xliff:g> ਫ਼ੋਟੋਆਂ ਮਿਟਾਈਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{ਕੀ <xliff:g id="APP_NAME_0">^1</xliff:g> ਨੂੰ ਇਸ ਆਈਟਮ ਨੂੰ ਮਿਟਾਉਣ ਦੇਣਾ ਹੈ?}one{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਈਟਮ ਨੂੰ ਮਿਟਾਉਣ ਦੇਣਾ ਹੈ?}other{ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਈਟਮਾਂ ਨੂੰ ਮਿਟਾਉਣ ਦੇਣਾ ਹੈ?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{ਆਈਟਮ ਮਿਟਾਈ ਜਾ ਰਹੀ ਹੈ…}one{<xliff:g id="COUNT">^1</xliff:g> ਆਈਟਮ ਮਿਟਾਈ ਜਾ ਰਹੀ ਹੈ…}other{<xliff:g id="COUNT">^1</xliff:g> ਆਈਟਮਾਂ ਮਿਟਾਈਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> ਐਪ ਮੀਡੀਆ ਫ਼ਾਈਲਾਂ \'ਤੇ ਪ੍ਰਕਿਰਿਆ ਨਹੀਂ ਕਰ ਸਕਦੀ"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"ਮੀਡੀਆ \'ਤੇ ਪ੍ਰਕਿਰਿਆ ਨੂੰ ਰੱਦ ਕੀਤਾ ਗਿਆ"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"ਮੀਡੀਆ \'ਤੇ ਪ੍ਰਕਿਰਿਆ ਸੰਬੰਧੀ ਗੜਬੜ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index d308c74..f9ad1b8 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Selektor zdjęć"</string>
<string name="artist_label" msgid="8105600993099120273">"Wykonawca"</string>
<string name="unknown" msgid="2059049215682829375">"Nieznany"</string>
<string name="root_images" msgid="5861633549189045666">"Obrazy"</string>
@@ -29,216 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Dalej"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Zezwól"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Odmów"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="few">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="many">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="few">I <xliff:g id="COUNT_1">^1</xliff:g> dodatkowe elementy</item>
- <item quantity="many">I <xliff:g id="COUNT_1">^1</xliff:g> dodatkowych elementów</item>
- <item quantity="other">I <xliff:g id="COUNT_1">^1</xliff:g> dodatkowego elementu</item>
- <item quantity="one">I <xliff:g id="COUNT_0">^1</xliff:g> dodatkowy element</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+ <xliff:g id="COUNT_0">^1</xliff:g>}few{+ <xliff:g id="COUNT_1">^1</xliff:g>}many{+ <xliff:g id="COUNT_1">^1</xliff:g>}other{+ <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{I <xliff:g id="COUNT_0">^1</xliff:g> dodatkowy element}few{I <xliff:g id="COUNT_1">^1</xliff:g> dodatkowe elementy}many{I <xliff:g id="COUNT_1">^1</xliff:g> dodatkowych elementów}other{I <xliff:g id="COUNT_1">^1</xliff:g> dodatkowego elementu}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Usuwanie tymczasowych plików aplikacji"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"Aplikacja <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> chce usunąć niektóre pliki tymczasowe. Może to spowodować zwiększenie wykorzystania baterii lub komórkowej transmisji danych."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Usuwam tymczasowe pliki aplikacji…"</string>
<string name="clear" msgid="5524638938415865915">"Wyczyść"</string>
<string name="allow" msgid="8885707816848569619">"Zezwól"</string>
<string name="deny" msgid="6040983710442068936">"Odmów"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="few">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na zmodyfikowanie <xliff:g id="COUNT">^2</xliff:g> plików audio?</item>
- <item quantity="many">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na zmodyfikowanie <xliff:g id="COUNT">^2</xliff:g> plików audio?</item>
- <item quantity="other">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na zmodyfikowanie <xliff:g id="COUNT">^2</xliff:g> pliku audio?</item>
- <item quantity="one">Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na zmodyfikowanie tego pliku audio?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="few">Modyfikuję <xliff:g id="COUNT">^1</xliff:g> pliki audio…</item>
- <item quantity="many">Modyfikuję <xliff:g id="COUNT">^1</xliff:g> plików audio…</item>
- <item quantity="other">Modyfikuję <xliff:g id="COUNT">^1</xliff:g> pliku audio…</item>
- <item quantity="one">Modyfikuję plik audio…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="few">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na zmodyfikowanie <xliff:g id="COUNT">^2</xliff:g> filmów?</item>
- <item quantity="many">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na zmodyfikowanie <xliff:g id="COUNT">^2</xliff:g> filmów?</item>
- <item quantity="other">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na zmodyfikowanie <xliff:g id="COUNT">^2</xliff:g> filmu?</item>
- <item quantity="one">Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na zmodyfikowanie tego filmu?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="few">Modyfikuję <xliff:g id="COUNT">^1</xliff:g> filmy…</item>
- <item quantity="many">Modyfikuję <xliff:g id="COUNT">^1</xliff:g> filmów…</item>
- <item quantity="other">Modyfikuję <xliff:g id="COUNT">^1</xliff:g> filmu…</item>
- <item quantity="one">Modyfikuję film…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="few">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na zmodyfikowanie <xliff:g id="COUNT">^2</xliff:g> zdjęć?</item>
- <item quantity="many">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na zmodyfikowanie <xliff:g id="COUNT">^2</xliff:g> zdjęć?</item>
- <item quantity="other">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na zmodyfikowanie <xliff:g id="COUNT">^2</xliff:g> zdjęcia?</item>
- <item quantity="one">Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na zmodyfikowanie tego zdjęcia?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="few">Modyfikuję <xliff:g id="COUNT">^1</xliff:g> zdjęcia…</item>
- <item quantity="many">Modyfikuję <xliff:g id="COUNT">^1</xliff:g> zdjęć…</item>
- <item quantity="other">Modyfikuję <xliff:g id="COUNT">^1</xliff:g> zdjęcia…</item>
- <item quantity="one">Modyfikuję zdjęcie…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="few">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na zmodyfikowanie <xliff:g id="COUNT">^2</xliff:g> elementów?</item>
- <item quantity="many">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na zmodyfikowanie <xliff:g id="COUNT">^2</xliff:g> elementów?</item>
- <item quantity="other">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na zmodyfikowanie <xliff:g id="COUNT">^2</xliff:g> elementu?</item>
- <item quantity="one">Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na zmodyfikowanie tego elementu?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="few">Modyfikuję <xliff:g id="COUNT">^1</xliff:g> elementy…</item>
- <item quantity="many">Modyfikuję <xliff:g id="COUNT">^1</xliff:g> elementów…</item>
- <item quantity="other">Modyfikuję <xliff:g id="COUNT">^1</xliff:g> elementu…</item>
- <item quantity="one">Modyfikuję element…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="few">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> plików audio do kosza?</item>
- <item quantity="many">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> plików audio do kosza?</item>
- <item quantity="other">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> pliku audio do kosza?</item>
- <item quantity="one">Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na przeniesienie tego pliku audio do kosza?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="few">Przenoszę <xliff:g id="COUNT">^1</xliff:g> pliki audio do kosza…</item>
- <item quantity="many">Przenoszę <xliff:g id="COUNT">^1</xliff:g> plików audio do kosza…</item>
- <item quantity="other">Przenoszę <xliff:g id="COUNT">^1</xliff:g> pliku audio do kosza…</item>
- <item quantity="one">Przenoszę plik audio do kosza…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="few">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> filmów do kosza?</item>
- <item quantity="many">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> filmów do kosza?</item>
- <item quantity="other">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> filmu do kosza?</item>
- <item quantity="one">Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na przeniesienie tego filmu do kosza?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="few">Przenoszę <xliff:g id="COUNT">^1</xliff:g> filmy do kosza…</item>
- <item quantity="many">Przenoszę <xliff:g id="COUNT">^1</xliff:g> filmów do kosza…</item>
- <item quantity="other">Przenoszę <xliff:g id="COUNT">^1</xliff:g> filmu do kosza…</item>
- <item quantity="one">Przenoszę film do kosza…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="few">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> zdjęć do kosza?</item>
- <item quantity="many">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> zdjęć do kosza?</item>
- <item quantity="other">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> zdjęcia do kosza?</item>
- <item quantity="one">Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na przeniesienie tego zdjęcia do kosza?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="few">Przenoszę <xliff:g id="COUNT">^1</xliff:g> zdjęcia do kosza…</item>
- <item quantity="many">Przenoszę <xliff:g id="COUNT">^1</xliff:g> zdjęć do kosza…</item>
- <item quantity="other">Przenoszę <xliff:g id="COUNT">^1</xliff:g> zdjęcia do kosza…</item>
- <item quantity="one">Przenoszę zdjęcie do kosza…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="few">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> elementów do kosza?</item>
- <item quantity="many">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> elementów do kosza?</item>
- <item quantity="other">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> elementu do kosza?</item>
- <item quantity="one">Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na przeniesienie tego elementu do kosza?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="few">Przenoszę <xliff:g id="COUNT">^1</xliff:g> elementy do kosza…</item>
- <item quantity="many">Przenoszę <xliff:g id="COUNT">^1</xliff:g> elementów do kosza…</item>
- <item quantity="other">Przenoszę <xliff:g id="COUNT">^1</xliff:g> elementu do kosza…</item>
- <item quantity="one">Przenoszę element do kosza…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="few">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> plików audio z kosza?</item>
- <item quantity="many">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> plików audio z kosza?</item>
- <item quantity="other">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> pliku audio z kosza?</item>
- <item quantity="one">Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na przeniesienie tego pliku audio z kosza?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="few">Przenoszę <xliff:g id="COUNT">^1</xliff:g> pliki audio z kosza…</item>
- <item quantity="many">Przenoszę <xliff:g id="COUNT">^1</xliff:g> plików audio z kosza…</item>
- <item quantity="other">Przenoszę <xliff:g id="COUNT">^1</xliff:g> pliku audio z kosza…</item>
- <item quantity="one">Przenoszę plik audio z kosza…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="few">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> filmów z kosza?</item>
- <item quantity="many">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> filmów z kosza?</item>
- <item quantity="other">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> filmu z kosza?</item>
- <item quantity="one">Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na przeniesienie tego filmu z kosza?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="few">Przenoszę <xliff:g id="COUNT">^1</xliff:g> filmy z kosza…</item>
- <item quantity="many">Przenoszę <xliff:g id="COUNT">^1</xliff:g> filmów z kosza…</item>
- <item quantity="other">Przenoszę <xliff:g id="COUNT">^1</xliff:g> filmu z kosza…</item>
- <item quantity="one">Przenoszę film z kosza…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="few">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> zdjęć z kosza?</item>
- <item quantity="many">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> zdjęć z kosza?</item>
- <item quantity="other">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> zdjęcia z kosza?</item>
- <item quantity="one">Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na przeniesienie tego zdjęcia z kosza?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="few">Przenoszę <xliff:g id="COUNT">^1</xliff:g> zdjęcia z kosza…</item>
- <item quantity="many">Przenoszę <xliff:g id="COUNT">^1</xliff:g> zdjęć z kosza…</item>
- <item quantity="other">Przenoszę <xliff:g id="COUNT">^1</xliff:g> zdjęcia z kosza…</item>
- <item quantity="one">Przenoszę zdjęcie z kosza…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="few">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> elementów z kosza?</item>
- <item quantity="many">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> elementów z kosza?</item>
- <item quantity="other">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> elementu z kosza?</item>
- <item quantity="one">Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na przeniesienie tego elementu z kosza?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="few">Przenoszę <xliff:g id="COUNT">^1</xliff:g> elementy z kosza…</item>
- <item quantity="many">Przenoszę <xliff:g id="COUNT">^1</xliff:g> elementów z kosza…</item>
- <item quantity="other">Przenoszę <xliff:g id="COUNT">^1</xliff:g> elementu z kosza…</item>
- <item quantity="one">Przenoszę element z kosza…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="few">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na usunięcie <xliff:g id="COUNT">^2</xliff:g> plików audio?</item>
- <item quantity="many">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na usunięcie <xliff:g id="COUNT">^2</xliff:g> plików audio?</item>
- <item quantity="other">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na usunięcie <xliff:g id="COUNT">^2</xliff:g> pliku audio?</item>
- <item quantity="one">Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na usunięcie tego pliku audio?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="few">Usuwam <xliff:g id="COUNT">^1</xliff:g> pliki audio…</item>
- <item quantity="many">Usuwam <xliff:g id="COUNT">^1</xliff:g> plików audio…</item>
- <item quantity="other">Usuwam <xliff:g id="COUNT">^1</xliff:g> pliku audio…</item>
- <item quantity="one">Usuwam plik audio…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="few">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na usunięcie <xliff:g id="COUNT">^2</xliff:g> filmów?</item>
- <item quantity="many">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na usunięcie <xliff:g id="COUNT">^2</xliff:g> filmów?</item>
- <item quantity="other">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na usunięcie <xliff:g id="COUNT">^2</xliff:g> filmu?</item>
- <item quantity="one">Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na usunięcie tego filmu?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="few">Usuwam <xliff:g id="COUNT">^1</xliff:g> filmy…</item>
- <item quantity="many">Usuwam <xliff:g id="COUNT">^1</xliff:g> filmów…</item>
- <item quantity="other">Usuwam <xliff:g id="COUNT">^1</xliff:g> filmu…</item>
- <item quantity="one">Usuwam film…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="few">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na usunięcie <xliff:g id="COUNT">^2</xliff:g> zdjęć?</item>
- <item quantity="many">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na usunięcie <xliff:g id="COUNT">^2</xliff:g> zdjęć?</item>
- <item quantity="other">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na usunięcie <xliff:g id="COUNT">^2</xliff:g> zdjęcia?</item>
- <item quantity="one">Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na usunięcie tego zdjęcia?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="few">Usuwam <xliff:g id="COUNT">^1</xliff:g> zdjęcia…</item>
- <item quantity="many">Usuwam <xliff:g id="COUNT">^1</xliff:g> zdjęć…</item>
- <item quantity="other">Usuwam <xliff:g id="COUNT">^1</xliff:g> zdjęcia…</item>
- <item quantity="one">Usuwam zdjęcie…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="few">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na usunięcie <xliff:g id="COUNT">^2</xliff:g> elementów?</item>
- <item quantity="many">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na usunięcie <xliff:g id="COUNT">^2</xliff:g> elementów?</item>
- <item quantity="other">Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na usunięcie <xliff:g id="COUNT">^2</xliff:g> elementu?</item>
- <item quantity="one">Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na usunięcie tego elementu?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="few">Usuwam <xliff:g id="COUNT">^1</xliff:g> elementy…</item>
- <item quantity="many">Usuwam <xliff:g id="COUNT">^1</xliff:g> elementów…</item>
- <item quantity="other">Usuwam <xliff:g id="COUNT">^1</xliff:g> elementu…</item>
- <item quantity="one">Usuwam element…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Dodaj"</string>
+ <string name="deselect" msgid="4297825044827769490">"Odznacz"</string>
+ <string name="deselected" msgid="8488133193326208475">"Usunięto wybór"</string>
+ <string name="select" msgid="2704765470563027689">"Zaznacz"</string>
+ <string name="selected" msgid="9151797369975828124">"Wybrano"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Wybierz maksymalnie <xliff:g id="COUNT_0">^1</xliff:g> element}few{Wybierz maksymalnie <xliff:g id="COUNT_1">^1</xliff:g> elementy}many{Wybierz maksymalnie <xliff:g id="COUNT_1">^1</xliff:g> elementów}other{Wybierz maksymalnie <xliff:g id="COUNT_1">^1</xliff:g> elementu}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Ostatnie"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Brak zdjęć i filmów"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Brak albumów"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Wyświetl wybrane"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Zdjęcia"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albumy"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Podgląd"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Włącz profil służbowy"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Włącz profil osobisty"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Czynność zablokowana przez administratora"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Nie można korzystać z danych służbowych w aplikacji osobistej"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Nie można korzystać z danych osobistych w aplikacji służbowej"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Aplikacje służbowe zostały wstrzymane"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Aby otworzyć zdjęcia służbowe, włącz aplikacje służbowe i spróbuj ponownie"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Ta aplikacja ma dostęp tylko do wybranych przez Ciebie zdjęć"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> element}few{<xliff:g id="COUNT_1">^1</xliff:g> elementy}many{<xliff:g id="COUNT_1">^1</xliff:g> elementów}other{<xliff:g id="COUNT_1">^1</xliff:g> elementu}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Dodaj (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Aparat"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Pobrane"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Ulubione"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Zrzuty ekranu"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Zdjęcie ruchome"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> z <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Film nagrany <xliff:g id="TIME">%1$s</xliff:g>, o długości <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Zdjęcie"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Zdjęcie ruchome"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Wycisz wideo"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Wyłącz wyciszenie wideo"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Odtwórz film"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Wstrzymaj film"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Multimedia w chmurze są teraz dostępne z poziomu aplikacji <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"nie wybrano"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na zmodyfikowanie tego pliku audio?}few{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na zmodyfikowanie <xliff:g id="COUNT">^2</xliff:g> plików audio?}many{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na zmodyfikowanie <xliff:g id="COUNT">^2</xliff:g> plików audio?}other{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na zmodyfikowanie <xliff:g id="COUNT">^2</xliff:g> pliku audio?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Modyfikuję plik audio…}few{Modyfikuję <xliff:g id="COUNT">^1</xliff:g> pliki audio…}many{Modyfikuję <xliff:g id="COUNT">^1</xliff:g> plików audio…}other{Modyfikuję <xliff:g id="COUNT">^1</xliff:g> pliku audio…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na zmodyfikowanie tego filmu?}few{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na zmodyfikowanie <xliff:g id="COUNT">^2</xliff:g> filmów?}many{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na zmodyfikowanie <xliff:g id="COUNT">^2</xliff:g> filmów?}other{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na zmodyfikowanie <xliff:g id="COUNT">^2</xliff:g> filmu?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Modyfikuję film…}few{Modyfikuję <xliff:g id="COUNT">^1</xliff:g> filmy…}many{Modyfikuję <xliff:g id="COUNT">^1</xliff:g> filmów…}other{Modyfikuję <xliff:g id="COUNT">^1</xliff:g> filmu…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na zmodyfikowanie tego zdjęcia?}few{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na zmodyfikowanie <xliff:g id="COUNT">^2</xliff:g> zdjęć?}many{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na zmodyfikowanie <xliff:g id="COUNT">^2</xliff:g> zdjęć?}other{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na zmodyfikowanie <xliff:g id="COUNT">^2</xliff:g> zdjęcia?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Modyfikuję zdjęcie…}few{Modyfikuję <xliff:g id="COUNT">^1</xliff:g> zdjęcia…}many{Modyfikuję <xliff:g id="COUNT">^1</xliff:g> zdjęć…}other{Modyfikuję <xliff:g id="COUNT">^1</xliff:g> zdjęcia…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na zmodyfikowanie tego elementu?}few{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na zmodyfikowanie <xliff:g id="COUNT">^2</xliff:g> elementów?}many{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na zmodyfikowanie <xliff:g id="COUNT">^2</xliff:g> elementów?}other{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na zmodyfikowanie <xliff:g id="COUNT">^2</xliff:g> elementu?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Modyfikuję element…}few{Modyfikuję <xliff:g id="COUNT">^1</xliff:g> elementy…}many{Modyfikuję <xliff:g id="COUNT">^1</xliff:g> elementów…}other{Modyfikuję <xliff:g id="COUNT">^1</xliff:g> elementu…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na przeniesienie tego pliku audio do kosza?}few{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> plików audio do kosza?}many{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> plików audio do kosza?}other{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> pliku audio do kosza?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Przenoszę plik audio do kosza…}few{Przenoszę <xliff:g id="COUNT">^1</xliff:g> pliki audio do kosza…}many{Przenoszę <xliff:g id="COUNT">^1</xliff:g> plików audio do kosza…}other{Przenoszę <xliff:g id="COUNT">^1</xliff:g> pliku audio do kosza…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na przeniesienie tego filmu do kosza?}few{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> filmów do kosza?}many{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> filmów do kosza?}other{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> filmu do kosza?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Przenoszę film do kosza…}few{Przenoszę <xliff:g id="COUNT">^1</xliff:g> filmy do kosza…}many{Przenoszę <xliff:g id="COUNT">^1</xliff:g> filmów do kosza…}other{Przenoszę <xliff:g id="COUNT">^1</xliff:g> filmu do kosza…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na przeniesienie tego zdjęcia do kosza?}few{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> zdjęć do kosza?}many{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> zdjęć do kosza?}other{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> zdjęcia do kosza?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Przenoszę zdjęcie do kosza…}few{Przenoszę <xliff:g id="COUNT">^1</xliff:g> zdjęcia do kosza…}many{Przenoszę <xliff:g id="COUNT">^1</xliff:g> zdjęć do kosza…}other{Przenoszę <xliff:g id="COUNT">^1</xliff:g> zdjęcia do kosza…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na przeniesienie tego elementu do kosza?}few{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> elementów do kosza?}many{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> elementów do kosza?}other{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> elementu do kosza?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Przenoszę element do kosza…}few{Przenoszę <xliff:g id="COUNT">^1</xliff:g> elementy do kosza…}many{Przenoszę <xliff:g id="COUNT">^1</xliff:g> elementów do kosza…}other{Przenoszę <xliff:g id="COUNT">^1</xliff:g> elementu do kosza…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na przeniesienie tego pliku audio z kosza?}few{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> plików audio z kosza?}many{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> plików audio z kosza?}other{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> pliku audio z kosza?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Przenoszę plik audio z kosza…}few{Przenoszę <xliff:g id="COUNT">^1</xliff:g> pliki audio z kosza…}many{Przenoszę <xliff:g id="COUNT">^1</xliff:g> plików audio z kosza…}other{Przenoszę <xliff:g id="COUNT">^1</xliff:g> pliku audio z kosza…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na przeniesienie tego filmu z kosza?}few{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> filmów z kosza?}many{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> filmów z kosza?}other{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> filmu z kosza?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Przenoszę film z kosza…}few{Przenoszę <xliff:g id="COUNT">^1</xliff:g> filmy z kosza…}many{Przenoszę <xliff:g id="COUNT">^1</xliff:g> filmów z kosza…}other{Przenoszę <xliff:g id="COUNT">^1</xliff:g> filmu z kosza…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na przeniesienie tego zdjęcia z kosza?}few{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> zdjęć z kosza?}many{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> zdjęć z kosza?}other{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> zdjęcia z kosza?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Przenoszę zdjęcie z kosza…}few{Przenoszę <xliff:g id="COUNT">^1</xliff:g> zdjęcia z kosza…}many{Przenoszę <xliff:g id="COUNT">^1</xliff:g> zdjęć z kosza…}other{Przenoszę <xliff:g id="COUNT">^1</xliff:g> zdjęcia z kosza…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na przeniesienie tego elementu z kosza?}few{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> elementów z kosza?}many{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> elementów z kosza?}other{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na przeniesienie <xliff:g id="COUNT">^2</xliff:g> elementu z kosza?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Przenoszę element z kosza…}few{Przenoszę <xliff:g id="COUNT">^1</xliff:g> elementy z kosza…}many{Przenoszę <xliff:g id="COUNT">^1</xliff:g> elementów z kosza…}other{Przenoszę <xliff:g id="COUNT">^1</xliff:g> elementu z kosza…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na usunięcie tego pliku audio?}few{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na usunięcie <xliff:g id="COUNT">^2</xliff:g> plików audio?}many{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na usunięcie <xliff:g id="COUNT">^2</xliff:g> plików audio?}other{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na usunięcie <xliff:g id="COUNT">^2</xliff:g> pliku audio?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Usuwam plik audio…}few{Usuwam <xliff:g id="COUNT">^1</xliff:g> pliki audio…}many{Usuwam <xliff:g id="COUNT">^1</xliff:g> plików audio…}other{Usuwam <xliff:g id="COUNT">^1</xliff:g> pliku audio…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na usunięcie tego filmu?}few{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na usunięcie <xliff:g id="COUNT">^2</xliff:g> filmów?}many{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na usunięcie <xliff:g id="COUNT">^2</xliff:g> filmów?}other{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na usunięcie <xliff:g id="COUNT">^2</xliff:g> filmu?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Usuwam film…}few{Usuwam <xliff:g id="COUNT">^1</xliff:g> filmy…}many{Usuwam <xliff:g id="COUNT">^1</xliff:g> filmów…}other{Usuwam <xliff:g id="COUNT">^1</xliff:g> filmu…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na usunięcie tego zdjęcia?}few{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na usunięcie <xliff:g id="COUNT">^2</xliff:g> zdjęć?}many{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na usunięcie <xliff:g id="COUNT">^2</xliff:g> zdjęć?}other{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na usunięcie <xliff:g id="COUNT">^2</xliff:g> zdjęcia?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Usuwam zdjęcie…}few{Usuwam <xliff:g id="COUNT">^1</xliff:g> zdjęcia…}many{Usuwam <xliff:g id="COUNT">^1</xliff:g> zdjęć…}other{Usuwam <xliff:g id="COUNT">^1</xliff:g> zdjęcia…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Zezwolić aplikacji <xliff:g id="APP_NAME_0">^1</xliff:g> na usunięcie tego elementu?}few{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na usunięcie <xliff:g id="COUNT">^2</xliff:g> elementów?}many{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na usunięcie <xliff:g id="COUNT">^2</xliff:g> elementów?}other{Zezwolić aplikacji <xliff:g id="APP_NAME_1">^1</xliff:g> na usunięcie <xliff:g id="COUNT">^2</xliff:g> elementu?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Usuwam element…}few{Usuwam <xliff:g id="COUNT">^1</xliff:g> elementy…}many{Usuwam <xliff:g id="COUNT">^1</xliff:g> elementów…}other{Usuwam <xliff:g id="COUNT">^1</xliff:g> elementu…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"Aplikacja <xliff:g id="APP_NAME">%s</xliff:g> nie może przetworzyć plików multimediów"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Anulowano przetwarzanie multimediów"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Błąd przetwarzania multimediów"</string>
diff --git a/res/values-pt-rBR/strings.xml b/res/values-pt-rBR/strings.xml
index 82f8b43..b394f90 100644
--- a/res/values-pt-rBR/strings.xml
+++ b/res/values-pt-rBR/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Mídia"</string>
<string name="storage_description" msgid="4081716890357580107">"Armazenamento local"</string>
<string name="app_label" msgid="9035307001052716210">"Armazenamento de mídia"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"Seletor de fotos"</string>
<string name="artist_label" msgid="8105600993099120273">"Artista"</string>
<string name="unknown" msgid="2059049215682829375">"Desconhecido"</string>
<string name="root_images" msgid="5861633549189045666">"Imagens"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Continuar"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Permitir"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Negar"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">Mais <xliff:g id="COUNT_1">^1</xliff:g> item extra</item>
- <item quantity="other">Mais <xliff:g id="COUNT_1">^1</xliff:g> itens extras</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}one{+<xliff:g id="COUNT_1">^1</xliff:g>}many{+<xliff:g id="COUNT_1">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Mais <xliff:g id="COUNT_0">^1</xliff:g> item extra}one{Mais <xliff:g id="COUNT_1">^1</xliff:g> item extra}many{Plus <xliff:g id="COUNT_1">^1</xliff:g> additional items}other{Mais <xliff:g id="COUNT_1">^1</xliff:g> itens extras}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Limpar arquivos temporários de apps"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"O app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> quer apagar alguns arquivos temporários. Isso pode aumentar o uso de bateria ou de dados da rede celular."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Limpando arquivos temporários de apps…"</string>
<string name="clear" msgid="5524638938415865915">"Limpar"</string>
<string name="allow" msgid="8885707816848569619">"Permitir"</string>
<string name="deny" msgid="6040983710442068936">"Negar"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> arquivo de áudio?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> arquivos de áudio?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one">Modificando <xliff:g id="COUNT">^1</xliff:g> arquivo de áudio…</item>
- <item quantity="other">Modificando <xliff:g id="COUNT">^1</xliff:g> arquivos de áudio…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> vídeo?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> vídeos?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one">Modificando <xliff:g id="COUNT">^1</xliff:g> vídeo…</item>
- <item quantity="other">Modificando <xliff:g id="COUNT">^1</xliff:g> vídeos…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> foto?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> fotos?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one">Modificando <xliff:g id="COUNT">^1</xliff:g> foto…</item>
- <item quantity="other">Modificando <xliff:g id="COUNT">^1</xliff:g> fotos…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> item?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> itens?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one">Modificando <xliff:g id="COUNT">^1</xliff:g> item…</item>
- <item quantity="other">Modificando <xliff:g id="COUNT">^1</xliff:g> itens…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> arquivo de áudio para a lixeira?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> arquivos de áudio para a lixeira?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one">Movendo <xliff:g id="COUNT">^1</xliff:g> arquivo de áudio para a lixeira…</item>
- <item quantity="other">Movendo <xliff:g id="COUNT">^1</xliff:g> arquivos de áudio para a lixeira…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> vídeo para a lixeira?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> vídeos para a lixeira?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one">Movendo <xliff:g id="COUNT">^1</xliff:g> vídeo para a lixeira…</item>
- <item quantity="other">Movendo <xliff:g id="COUNT">^1</xliff:g> vídeos para a lixeira…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> foto para a lixeira?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> fotos para a lixeira?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one">Movendo <xliff:g id="COUNT">^1</xliff:g> foto para a lixeira…</item>
- <item quantity="other">Movendo <xliff:g id="COUNT">^1</xliff:g> fotos para a lixeira…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> item para a lixeira?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> itens para a lixeira?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one">Movendo <xliff:g id="COUNT">^1</xliff:g> item para a lixeira…</item>
- <item quantity="other">Movendo <xliff:g id="COUNT">^1</xliff:g> itens para a lixeira…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> arquivo de áudio da lixeira?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> arquivos de áudio da lixeira?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one">Retirando <xliff:g id="COUNT">^1</xliff:g> arquivo de áudio da lixeira…</item>
- <item quantity="other">Retirando <xliff:g id="COUNT">^1</xliff:g> arquivos de áudio da lixeira…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> vídeo da lixeira?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> vídeos da lixeira?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one">Retirando <xliff:g id="COUNT">^1</xliff:g> vídeo da lixeira…</item>
- <item quantity="other">Retirando <xliff:g id="COUNT">^1</xliff:g> vídeos da lixeira…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> foto da lixeira?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> fotos da lixeira?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one">Retirando <xliff:g id="COUNT">^1</xliff:g> foto da lixeira…</item>
- <item quantity="other">Retirando <xliff:g id="COUNT">^1</xliff:g> fotos da lixeira…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> item da lixeira?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> itens da lixeira?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one">Retirando <xliff:g id="COUNT">^1</xliff:g> item da lixeira…</item>
- <item quantity="other">Retirando <xliff:g id="COUNT">^1</xliff:g> itens da lixeira…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> arquivo de áudio?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> arquivos de áudio?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one">Excluindo <xliff:g id="COUNT">^1</xliff:g> arquivo de áudio…</item>
- <item quantity="other">Excluindo <xliff:g id="COUNT">^1</xliff:g> arquivos de áudio…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> vídeo?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> vídeos?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one">Excluindo <xliff:g id="COUNT">^1</xliff:g> vídeo…</item>
- <item quantity="other">Excluindo <xliff:g id="COUNT">^1</xliff:g> vídeos…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> foto?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> fotos?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one">Excluindo <xliff:g id="COUNT">^1</xliff:g> foto…</item>
- <item quantity="other">Excluindo <xliff:g id="COUNT">^1</xliff:g> fotos…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> item?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> itens?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one">Excluindo <xliff:g id="COUNT">^1</xliff:g> item…</item>
- <item quantity="other">Excluindo <xliff:g id="COUNT">^1</xliff:g> itens…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Adicionar"</string>
+ <string name="deselect" msgid="4297825044827769490">"Desmarcar"</string>
+ <string name="deselected" msgid="8488133193326208475">"Desmarcada"</string>
+ <string name="select" msgid="2704765470563027689">"Selecionar"</string>
+ <string name="selected" msgid="9151797369975828124">"Selecionada"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Selecione até <xliff:g id="COUNT_0">^1</xliff:g> item}one{Selecione até <xliff:g id="COUNT_1">^1</xliff:g> item}many{Select up to <xliff:g id="COUNT_1">^1</xliff:g> items}other{Selecione até <xliff:g id="COUNT_1">^1</xliff:g> itens}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Recentes"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Sem fotos ou vídeos"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Sem álbuns"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Ver selecionados"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Fotos"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Álbuns"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Visualização"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Mudar para \"Trabalho\""</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Mudar para \"Pessoal\""</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Bloqueado pelo administrador"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Não é permitido o acesso a dados de trabalho em um app pessoal"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Não é permitido o acesso a dados pessoais em um app de trabalho"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Os apps de trabalho foram pausados"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Para abrir as fotos de trabalho, ative os apps de trabalho e tente novamente"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Este app só pode acessar as fotos que você selecionar"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}one{<xliff:g id="COUNT_1">^1</xliff:g> item}many{<xliff:g id="COUNT_1">^1</xliff:g> items}other{<xliff:g id="COUNT_1">^1</xliff:g> itens}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Adicionar (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Câmera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Downloads"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Favoritos"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Capturas de tela"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Foto com movimento"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"Item <xliff:g id="ITEM_NAME">%1$s</xliff:g> criado em <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Vídeo gravado em <xliff:g id="TIME">%1$s</xliff:g>, com <xliff:g id="DURATION">%2$s</xliff:g> de duração"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Foto"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Foto com movimento"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Desativar o som do vídeo"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Ativar o som do vídeo"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Iniciar vídeo"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Pausar vídeo"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Mídia em nuvem agora disponível no app <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"não selecionado"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> modifique esse arquivo de áudio?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> arquivo de áudio?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> audio files?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> arquivos de áudio?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Modificando o arquivo de áudio…}one{Modificando <xliff:g id="COUNT">^1</xliff:g> arquivo de áudio…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> audio files…}other{Modificando <xliff:g id="COUNT">^1</xliff:g> arquivos de áudio…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> modifique esse vídeo?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> vídeo?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> videos?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> vídeos?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Modificando o vídeo…}one{Modificando <xliff:g id="COUNT">^1</xliff:g> vídeo…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> videos…}other{Modificando <xliff:g id="COUNT">^1</xliff:g> vídeos…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> modifique essa foto?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> foto?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> photos?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> fotos?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Modificando a foto…}one{Modificando <xliff:g id="COUNT">^1</xliff:g> foto…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> photos…}other{Modificando <xliff:g id="COUNT">^1</xliff:g> fotos…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> modifique esse item?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> item?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> items?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> itens?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Modificando o item…}one{Modificando <xliff:g id="COUNT">^1</xliff:g> item…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> items…}other{Modificando <xliff:g id="COUNT">^1</xliff:g> itens…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> mova esse arquivo de áudio para a lixeira?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> arquivo de áudio para a lixeira?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files to trash?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> arquivos de áudio para a lixeira?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Movendo o arquivo de áudio para a lixeira…}one{Movendo <xliff:g id="COUNT">^1</xliff:g> arquivo de áudio para a lixeira…}many{Moving <xliff:g id="COUNT">^1</xliff:g> audio files to trash…}other{Movendo <xliff:g id="COUNT">^1</xliff:g> arquivos de áudio para a lixeira…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> mova esse vídeo para a lixeira?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> vídeo para a lixeira?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos to trash?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> vídeos para a lixeira?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Movendo o vídeo para a lixeira…}one{Movendo <xliff:g id="COUNT">^1</xliff:g> vídeo para a lixeira…}many{Moving <xliff:g id="COUNT">^1</xliff:g> videos to trash…}other{Movendo <xliff:g id="COUNT">^1</xliff:g> vídeos para a lixeira…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> mova essa foto para a lixeira?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> foto para a lixeira?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos to trash?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> fotos para a lixeira?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Movendo a foto para a lixeira…}one{Movendo <xliff:g id="COUNT">^1</xliff:g> foto para a lixeira…}many{Moving <xliff:g id="COUNT">^1</xliff:g> photos to trash…}other{Movendo <xliff:g id="COUNT">^1</xliff:g> fotos para a lixeira…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> mova esse item para a lixeira?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> item para a lixeira?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items to trash?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> itens para a lixeira?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Movendo o item para a lixeira…}one{Movendo <xliff:g id="COUNT">^1</xliff:g> item para a lixeira…}many{Moving <xliff:g id="COUNT">^1</xliff:g> items to trash…}other{Movendo <xliff:g id="COUNT">^1</xliff:g> itens para a lixeira…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> retire esse arquivo de áudio da lixeira?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> arquivo de áudio da lixeira?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files out of trash?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> arquivos de áudio da lixeira?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Retirando o arquivo de áudio da lixeira…}one{Retirando <xliff:g id="COUNT">^1</xliff:g> arquivo de áudio da lixeira…}many{Moving <xliff:g id="COUNT">^1</xliff:g> audio files out of trash…}other{Retirando <xliff:g id="COUNT">^1</xliff:g> arquivos de áudio da lixeira…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> retire esse vídeo da lixeira?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> vídeo da lixeira?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos out of trash?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> vídeos da lixeira?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Retirando o vídeo da lixeira…}one{Retirando <xliff:g id="COUNT">^1</xliff:g> vídeo da lixeira…}many{Moving <xliff:g id="COUNT">^1</xliff:g> videos out of trash…}other{Retirando <xliff:g id="COUNT">^1</xliff:g> vídeos da lixeira…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> retire essa foto da lixeira?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> foto da lixeira?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos out of trash?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> fotos da lixeira?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Retirando a foto da lixeira…}one{Retirando <xliff:g id="COUNT">^1</xliff:g> foto da lixeira…}many{Moving <xliff:g id="COUNT">^1</xliff:g> photos out of trash…}other{Retirando <xliff:g id="COUNT">^1</xliff:g> fotos da lixeira…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> retire esse item da lixeira?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> item da lixeira?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items out of trash?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> itens da lixeira?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Retirando o item da lixeira…}one{Retirando <xliff:g id="COUNT">^1</xliff:g> item da lixeira…}many{Moving <xliff:g id="COUNT">^1</xliff:g> items out of trash…}other{Retirando <xliff:g id="COUNT">^1</xliff:g> itens da lixeira…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> exclua esse arquivo de áudio?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> arquivo de áudio?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> audio files?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> arquivos de áudio?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Excluindo o arquivo de áudio…}one{Excluindo <xliff:g id="COUNT">^1</xliff:g> arquivo de áudio…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> audio files…}other{Excluindo <xliff:g id="COUNT">^1</xliff:g> arquivos de áudio…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> exclua esse vídeo?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> vídeo?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> videos?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> vídeos?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Excluindo o vídeo…}one{Excluindo <xliff:g id="COUNT">^1</xliff:g> vídeo…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> videos…}other{Excluindo <xliff:g id="COUNT">^1</xliff:g> vídeos…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> exclua essa foto?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> foto?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> photos?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> fotos?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Excluindo a foto…}one{Excluindo <xliff:g id="COUNT">^1</xliff:g> foto…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> photos…}other{Excluindo <xliff:g id="COUNT">^1</xliff:g> fotos…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> exclua esse item?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> item?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> items?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> itens?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Excluindo o item…}one{Excluindo <xliff:g id="COUNT">^1</xliff:g> item…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> items…}other{Excluindo <xliff:g id="COUNT">^1</xliff:g> itens…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"Não é possível processar arquivos de mídia no app <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Processamento de mídia cancelado"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Erro no processamento de mídia"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 058b69b..efeadfe 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Selecionador de fotos"</string>
<string name="artist_label" msgid="8105600993099120273">"Artista"</string>
<string name="unknown" msgid="2059049215682829375">"Desconhecido"</string>
<string name="root_images" msgid="5861633549189045666">"Imagens"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Continuar"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Permitir"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Recusar"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">E <xliff:g id="COUNT_1">^1</xliff:g> itens adicionais</item>
- <item quantity="one">E <xliff:g id="COUNT_0">^1</xliff:g> item adicional</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}many{+<xliff:g id="COUNT_1">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{E <xliff:g id="COUNT_0">^1</xliff:g> item adicional}many{Plus <xliff:g id="COUNT_1">^1</xliff:g> additional items}other{E <xliff:g id="COUNT_1">^1</xliff:g> itens adicionais}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Limpe ficheiros de apps temporários"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"A app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> pretende limpar alguns ficheiros temporários. Isto pode resultar num aumento da utilização da bateria ou dos dados móveis."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"A limpar ficheiros temporários da app…"</string>
<string name="clear" msgid="5524638938415865915">"Limpar"</string>
<string name="allow" msgid="8885707816848569619">"Permitir"</string>
<string name="deny" msgid="6040983710442068936">"Recusar"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> ficheiros de áudio?</item>
- <item quantity="one">Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este ficheiro de áudio?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">A modificar <xliff:g id="COUNT">^1</xliff:g> ficheiros de áudio…</item>
- <item quantity="one">A modificar o ficheiro de áudio…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> vídeos?</item>
- <item quantity="one">Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este vídeo?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">A modificar <xliff:g id="COUNT">^1</xliff:g> vídeos…</item>
- <item quantity="one">A modificar o vídeo…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> fotos?</item>
- <item quantity="one">Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> modifique esta foto?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">A modificar <xliff:g id="COUNT">^1</xliff:g> fotos…</item>
- <item quantity="one">A modificar a foto…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> itens?</item>
- <item quantity="one">Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este item?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">A modificar <xliff:g id="COUNT">^1</xliff:g> itens…</item>
- <item quantity="one">A modificar o item…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> ficheiros de áudio para o lixo?</item>
- <item quantity="one">Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> mova este ficheiro de áudio para o lixo?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">A mover <xliff:g id="COUNT">^1</xliff:g> ficheiros de áudio para o lixo…</item>
- <item quantity="one">A mover o ficheiro de áudio para o lixo…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> vídeos para o lixo?</item>
- <item quantity="one">Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> mova este vídeo para o lixo?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">A mover <xliff:g id="COUNT">^1</xliff:g> vídeos para o lixo…</item>
- <item quantity="one">A mover o vídeo para o lixo…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> fotos para o lixo?</item>
- <item quantity="one">Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> mova esta foto para o lixo?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">A mover <xliff:g id="COUNT">^1</xliff:g> fotos para o lixo…</item>
- <item quantity="one">A mover a foto para o lixo…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> itens para o lixo?</item>
- <item quantity="one">Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> mova este item para o lixo?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">A mover <xliff:g id="COUNT">^1</xliff:g> itens para o lixo…</item>
- <item quantity="one">A mover o item para o lixo…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> ficheiros de áudio do lixo?</item>
- <item quantity="one">Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> retire este ficheiro de áudio do lixo?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">A retirar <xliff:g id="COUNT">^1</xliff:g> ficheiros de áudio do lixo…</item>
- <item quantity="one">A retirar o ficheiro de áudio do lixo…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> vídeos do lixo?</item>
- <item quantity="one">Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> retire este vídeo do lixo?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">A retirar <xliff:g id="COUNT">^1</xliff:g> vídeos do lixo…</item>
- <item quantity="one">A retirar o vídeo do lixo…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> fotos do lixo?</item>
- <item quantity="one">Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> retire esta foto do lixo?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">A retirar <xliff:g id="COUNT">^1</xliff:g> fotos do lixo…</item>
- <item quantity="one">A retirar a foto do lixo…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> itens do lixo?</item>
- <item quantity="one">Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> retire este item do lixo?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">A retirar <xliff:g id="COUNT">^1</xliff:g> itens do lixo…</item>
- <item quantity="one">A retirar o item do lixo…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> elimine <xliff:g id="COUNT">^2</xliff:g> ficheiros de áudio?</item>
- <item quantity="one">Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> elimine este ficheiro de áudio?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">A eliminar <xliff:g id="COUNT">^1</xliff:g> ficheiros de áudio…</item>
- <item quantity="one">A eliminar o ficheiro de áudio…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> elimine <xliff:g id="COUNT">^2</xliff:g> vídeos?</item>
- <item quantity="one">Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> elimine este vídeo?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">A eliminar <xliff:g id="COUNT">^1</xliff:g> vídeos…</item>
- <item quantity="one">A eliminar o vídeo…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> elimine <xliff:g id="COUNT">^2</xliff:g> fotos?</item>
- <item quantity="one">Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> elimine esta foto?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">A eliminar <xliff:g id="COUNT">^1</xliff:g> fotos…</item>
- <item quantity="one">A eliminar a foto…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> elimine <xliff:g id="COUNT">^2</xliff:g> itens?</item>
- <item quantity="one">Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> elimine este item?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">A eliminar <xliff:g id="COUNT">^1</xliff:g> itens…</item>
- <item quantity="one">A eliminar o item…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Adicionar"</string>
+ <string name="deselect" msgid="4297825044827769490">"Desselecionar"</string>
+ <string name="deselected" msgid="8488133193326208475">"Desmarcado"</string>
+ <string name="select" msgid="2704765470563027689">"Selecionar"</string>
+ <string name="selected" msgid="9151797369975828124">"Selecionado"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Selecione até <xliff:g id="COUNT_0">^1</xliff:g> item}many{Select up to <xliff:g id="COUNT_1">^1</xliff:g> items}other{Selecione até <xliff:g id="COUNT_1">^1</xliff:g> itens}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Recentes"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Nenhum vídeo ou foto"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Nenhum álbum"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Ver selecionado(s)"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Fotos"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Álbuns"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Pré-visualizar"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Mudar para trabalho"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Mudar para pessoal"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Bloqueado pelo administrador"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"O acesso aos dados de trabalho a partir de uma app pessoal não é permitido"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"O acesso aos dados pessoais a partir de uma app de trabalho não é permitido"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"As apps de trabalho estão em pausa"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Para abrir fotos do trabalho, ative as apps de trabalho e tente novamente"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Esta app só pode aceder às fotos que selecionar"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}many{<xliff:g id="COUNT_1">^1</xliff:g> items}other{<xliff:g id="COUNT_1">^1</xliff:g> itens}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Adicionar (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Câmara"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Transferências"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Favoritos"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Capturas de ecrã"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Foto em movimento"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> criado(a) em <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Vídeo filmado à(s) <xliff:g id="TIME">%1$s</xliff:g> com a duração de <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Foto"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Foto em movimento"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Desative o som do vídeo"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Reative o som do vídeo"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Reproduzir vídeo"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Pausar vídeo"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Multimédia da nuvem já disponível da app <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"não selecionado"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este ficheiro de áudio?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> audio files?}other{Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> ficheiros de áudio?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{A modificar o ficheiro de áudio…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> audio files…}other{A modificar <xliff:g id="COUNT">^1</xliff:g> ficheiro(s) de áudio…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este vídeo?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> videos?}other{Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> vídeos?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{A modificar o vídeo…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> videos…}other{A modificar <xliff:g id="COUNT">^1</xliff:g> vídeos…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> modifique esta foto?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> photos?}other{Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> fotos?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{A modificar a foto…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> photos…}other{A modificar <xliff:g id="COUNT">^1</xliff:g> fotos…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este item?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> items?}other{Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> itens?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{A modificar o item…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> items…}other{A modificar <xliff:g id="COUNT">^1</xliff:g> itens…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> mova este ficheiro de áudio para o lixo?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files to trash?}other{Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> ficheiros de áudio para o lixo?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{A mover o ficheiro de áudio para o lixo…}many{Moving <xliff:g id="COUNT">^1</xliff:g> audio files to trash…}other{A mover <xliff:g id="COUNT">^1</xliff:g> ficheiros de áudio para o lixo…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> mova este vídeo para o lixo?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos to trash?}other{Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> vídeos para o lixo?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{A mover o vídeo para o lixo…}many{Moving <xliff:g id="COUNT">^1</xliff:g> videos to trash…}other{A mover <xliff:g id="COUNT">^1</xliff:g> vídeos para o lixo…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> mova esta foto para o lixo?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos to trash?}other{Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> fotos para o lixo?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{A mover a foto para o lixo…}many{Moving <xliff:g id="COUNT">^1</xliff:g> photos to trash…}other{A mover <xliff:g id="COUNT">^1</xliff:g> fotos para o lixo…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> mova este item para o lixo?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items to trash?}other{Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> itens para o lixo?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{A mover o item para o lixo…}many{Moving <xliff:g id="COUNT">^1</xliff:g> items to trash…}other{A mover <xliff:g id="COUNT">^1</xliff:g> itens para o lixo…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> retire este ficheiro de áudio do lixo?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files out of trash?}other{Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> ficheiros de áudio do lixo?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{A retirar o ficheiro de áudio do lixo…}many{Moving <xliff:g id="COUNT">^1</xliff:g> audio files out of trash…}other{A retirar <xliff:g id="COUNT">^1</xliff:g> ficheiros de áudio do lixo…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> retire este vídeo do lixo?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos out of trash?}other{Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> vídeos do lixo?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{A retirar o vídeo do lixo…}many{Moving <xliff:g id="COUNT">^1</xliff:g> videos out of trash…}other{A retirar <xliff:g id="COUNT">^1</xliff:g> vídeos do lixo…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> retire esta foto do lixo?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos out of trash?}other{Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> fotos do lixo?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{A retirar a foto do lixo…}many{Moving <xliff:g id="COUNT">^1</xliff:g> photos out of trash…}other{A retirar <xliff:g id="COUNT">^1</xliff:g> fotos do lixo…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> retire este item do lixo?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items out of trash?}other{Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> itens do lixo?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{A retirar o item do lixo…}many{Moving <xliff:g id="COUNT">^1</xliff:g> items out of trash…}other{A retirar <xliff:g id="COUNT">^1</xliff:g> itens do lixo…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> elimine este ficheiro de áudio?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> audio files?}other{Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> elimine <xliff:g id="COUNT">^2</xliff:g> ficheiros de áudio?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{A eliminar o ficheiro de áudio…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> audio files…}other{A eliminar <xliff:g id="COUNT">^1</xliff:g> ficheiros de áudio…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> elimine este vídeo?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> videos?}other{Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> elimine <xliff:g id="COUNT">^2</xliff:g> vídeos?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{A eliminar o vídeo…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> videos…}other{A eliminar <xliff:g id="COUNT">^1</xliff:g> vídeos…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> elimine esta foto?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> photos?}other{Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> elimine <xliff:g id="COUNT">^2</xliff:g> fotos?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{A eliminar a foto…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> photos…}other{A eliminar <xliff:g id="COUNT">^1</xliff:g> fotos…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> elimine este item?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> items?}other{Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> elimine <xliff:g id="COUNT">^2</xliff:g> itens?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{A eliminar o item…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> items…}other{A eliminar <xliff:g id="COUNT">^1</xliff:g> itens…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"A app <xliff:g id="APP_NAME">%s</xliff:g> não pode processar ficheiros multimédia"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Processamento de multimédia cancelado"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Erro de processamento de multimédia"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 82f8b43..b394f90 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Mídia"</string>
<string name="storage_description" msgid="4081716890357580107">"Armazenamento local"</string>
<string name="app_label" msgid="9035307001052716210">"Armazenamento de mídia"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"Seletor de fotos"</string>
<string name="artist_label" msgid="8105600993099120273">"Artista"</string>
<string name="unknown" msgid="2059049215682829375">"Desconhecido"</string>
<string name="root_images" msgid="5861633549189045666">"Imagens"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Continuar"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Permitir"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Negar"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">Mais <xliff:g id="COUNT_1">^1</xliff:g> item extra</item>
- <item quantity="other">Mais <xliff:g id="COUNT_1">^1</xliff:g> itens extras</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}one{+<xliff:g id="COUNT_1">^1</xliff:g>}many{+<xliff:g id="COUNT_1">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Mais <xliff:g id="COUNT_0">^1</xliff:g> item extra}one{Mais <xliff:g id="COUNT_1">^1</xliff:g> item extra}many{Plus <xliff:g id="COUNT_1">^1</xliff:g> additional items}other{Mais <xliff:g id="COUNT_1">^1</xliff:g> itens extras}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Limpar arquivos temporários de apps"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"O app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> quer apagar alguns arquivos temporários. Isso pode aumentar o uso de bateria ou de dados da rede celular."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Limpando arquivos temporários de apps…"</string>
<string name="clear" msgid="5524638938415865915">"Limpar"</string>
<string name="allow" msgid="8885707816848569619">"Permitir"</string>
<string name="deny" msgid="6040983710442068936">"Negar"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> arquivo de áudio?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> arquivos de áudio?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one">Modificando <xliff:g id="COUNT">^1</xliff:g> arquivo de áudio…</item>
- <item quantity="other">Modificando <xliff:g id="COUNT">^1</xliff:g> arquivos de áudio…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> vídeo?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> vídeos?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one">Modificando <xliff:g id="COUNT">^1</xliff:g> vídeo…</item>
- <item quantity="other">Modificando <xliff:g id="COUNT">^1</xliff:g> vídeos…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> foto?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> fotos?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one">Modificando <xliff:g id="COUNT">^1</xliff:g> foto…</item>
- <item quantity="other">Modificando <xliff:g id="COUNT">^1</xliff:g> fotos…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> item?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> itens?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one">Modificando <xliff:g id="COUNT">^1</xliff:g> item…</item>
- <item quantity="other">Modificando <xliff:g id="COUNT">^1</xliff:g> itens…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> arquivo de áudio para a lixeira?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> arquivos de áudio para a lixeira?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one">Movendo <xliff:g id="COUNT">^1</xliff:g> arquivo de áudio para a lixeira…</item>
- <item quantity="other">Movendo <xliff:g id="COUNT">^1</xliff:g> arquivos de áudio para a lixeira…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> vídeo para a lixeira?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> vídeos para a lixeira?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one">Movendo <xliff:g id="COUNT">^1</xliff:g> vídeo para a lixeira…</item>
- <item quantity="other">Movendo <xliff:g id="COUNT">^1</xliff:g> vídeos para a lixeira…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> foto para a lixeira?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> fotos para a lixeira?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one">Movendo <xliff:g id="COUNT">^1</xliff:g> foto para a lixeira…</item>
- <item quantity="other">Movendo <xliff:g id="COUNT">^1</xliff:g> fotos para a lixeira…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> item para a lixeira?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> itens para a lixeira?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one">Movendo <xliff:g id="COUNT">^1</xliff:g> item para a lixeira…</item>
- <item quantity="other">Movendo <xliff:g id="COUNT">^1</xliff:g> itens para a lixeira…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> arquivo de áudio da lixeira?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> arquivos de áudio da lixeira?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one">Retirando <xliff:g id="COUNT">^1</xliff:g> arquivo de áudio da lixeira…</item>
- <item quantity="other">Retirando <xliff:g id="COUNT">^1</xliff:g> arquivos de áudio da lixeira…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> vídeo da lixeira?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> vídeos da lixeira?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one">Retirando <xliff:g id="COUNT">^1</xliff:g> vídeo da lixeira…</item>
- <item quantity="other">Retirando <xliff:g id="COUNT">^1</xliff:g> vídeos da lixeira…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> foto da lixeira?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> fotos da lixeira?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one">Retirando <xliff:g id="COUNT">^1</xliff:g> foto da lixeira…</item>
- <item quantity="other">Retirando <xliff:g id="COUNT">^1</xliff:g> fotos da lixeira…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> item da lixeira?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> itens da lixeira?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one">Retirando <xliff:g id="COUNT">^1</xliff:g> item da lixeira…</item>
- <item quantity="other">Retirando <xliff:g id="COUNT">^1</xliff:g> itens da lixeira…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> arquivo de áudio?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> arquivos de áudio?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one">Excluindo <xliff:g id="COUNT">^1</xliff:g> arquivo de áudio…</item>
- <item quantity="other">Excluindo <xliff:g id="COUNT">^1</xliff:g> arquivos de áudio…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> vídeo?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> vídeos?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one">Excluindo <xliff:g id="COUNT">^1</xliff:g> vídeo…</item>
- <item quantity="other">Excluindo <xliff:g id="COUNT">^1</xliff:g> vídeos…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> foto?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> fotos?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one">Excluindo <xliff:g id="COUNT">^1</xliff:g> foto…</item>
- <item quantity="other">Excluindo <xliff:g id="COUNT">^1</xliff:g> fotos…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> item?</item>
- <item quantity="other">Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> itens?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one">Excluindo <xliff:g id="COUNT">^1</xliff:g> item…</item>
- <item quantity="other">Excluindo <xliff:g id="COUNT">^1</xliff:g> itens…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Adicionar"</string>
+ <string name="deselect" msgid="4297825044827769490">"Desmarcar"</string>
+ <string name="deselected" msgid="8488133193326208475">"Desmarcada"</string>
+ <string name="select" msgid="2704765470563027689">"Selecionar"</string>
+ <string name="selected" msgid="9151797369975828124">"Selecionada"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Selecione até <xliff:g id="COUNT_0">^1</xliff:g> item}one{Selecione até <xliff:g id="COUNT_1">^1</xliff:g> item}many{Select up to <xliff:g id="COUNT_1">^1</xliff:g> items}other{Selecione até <xliff:g id="COUNT_1">^1</xliff:g> itens}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Recentes"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Sem fotos ou vídeos"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Sem álbuns"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Ver selecionados"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Fotos"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Álbuns"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Visualização"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Mudar para \"Trabalho\""</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Mudar para \"Pessoal\""</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Bloqueado pelo administrador"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Não é permitido o acesso a dados de trabalho em um app pessoal"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Não é permitido o acesso a dados pessoais em um app de trabalho"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Os apps de trabalho foram pausados"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Para abrir as fotos de trabalho, ative os apps de trabalho e tente novamente"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Este app só pode acessar as fotos que você selecionar"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}one{<xliff:g id="COUNT_1">^1</xliff:g> item}many{<xliff:g id="COUNT_1">^1</xliff:g> items}other{<xliff:g id="COUNT_1">^1</xliff:g> itens}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Adicionar (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Câmera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Downloads"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Favoritos"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Capturas de tela"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Foto com movimento"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"Item <xliff:g id="ITEM_NAME">%1$s</xliff:g> criado em <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Vídeo gravado em <xliff:g id="TIME">%1$s</xliff:g>, com <xliff:g id="DURATION">%2$s</xliff:g> de duração"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Foto"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Foto com movimento"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Desativar o som do vídeo"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Ativar o som do vídeo"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Iniciar vídeo"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Pausar vídeo"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Mídia em nuvem agora disponível no app <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"não selecionado"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> modifique esse arquivo de áudio?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> arquivo de áudio?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> audio files?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> arquivos de áudio?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Modificando o arquivo de áudio…}one{Modificando <xliff:g id="COUNT">^1</xliff:g> arquivo de áudio…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> audio files…}other{Modificando <xliff:g id="COUNT">^1</xliff:g> arquivos de áudio…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> modifique esse vídeo?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> vídeo?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> videos?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> vídeos?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Modificando o vídeo…}one{Modificando <xliff:g id="COUNT">^1</xliff:g> vídeo…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> videos…}other{Modificando <xliff:g id="COUNT">^1</xliff:g> vídeos…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> modifique essa foto?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> foto?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> photos?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> fotos?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Modificando a foto…}one{Modificando <xliff:g id="COUNT">^1</xliff:g> foto…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> photos…}other{Modificando <xliff:g id="COUNT">^1</xliff:g> fotos…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> modifique esse item?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> item?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> items?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> itens?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Modificando o item…}one{Modificando <xliff:g id="COUNT">^1</xliff:g> item…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> items…}other{Modificando <xliff:g id="COUNT">^1</xliff:g> itens…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> mova esse arquivo de áudio para a lixeira?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> arquivo de áudio para a lixeira?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files to trash?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> arquivos de áudio para a lixeira?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Movendo o arquivo de áudio para a lixeira…}one{Movendo <xliff:g id="COUNT">^1</xliff:g> arquivo de áudio para a lixeira…}many{Moving <xliff:g id="COUNT">^1</xliff:g> audio files to trash…}other{Movendo <xliff:g id="COUNT">^1</xliff:g> arquivos de áudio para a lixeira…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> mova esse vídeo para a lixeira?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> vídeo para a lixeira?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos to trash?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> vídeos para a lixeira?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Movendo o vídeo para a lixeira…}one{Movendo <xliff:g id="COUNT">^1</xliff:g> vídeo para a lixeira…}many{Moving <xliff:g id="COUNT">^1</xliff:g> videos to trash…}other{Movendo <xliff:g id="COUNT">^1</xliff:g> vídeos para a lixeira…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> mova essa foto para a lixeira?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> foto para a lixeira?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos to trash?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> fotos para a lixeira?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Movendo a foto para a lixeira…}one{Movendo <xliff:g id="COUNT">^1</xliff:g> foto para a lixeira…}many{Moving <xliff:g id="COUNT">^1</xliff:g> photos to trash…}other{Movendo <xliff:g id="COUNT">^1</xliff:g> fotos para a lixeira…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> mova esse item para a lixeira?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> item para a lixeira?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items to trash?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> mova <xliff:g id="COUNT">^2</xliff:g> itens para a lixeira?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Movendo o item para a lixeira…}one{Movendo <xliff:g id="COUNT">^1</xliff:g> item para a lixeira…}many{Moving <xliff:g id="COUNT">^1</xliff:g> items to trash…}other{Movendo <xliff:g id="COUNT">^1</xliff:g> itens para a lixeira…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> retire esse arquivo de áudio da lixeira?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> arquivo de áudio da lixeira?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files out of trash?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> arquivos de áudio da lixeira?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Retirando o arquivo de áudio da lixeira…}one{Retirando <xliff:g id="COUNT">^1</xliff:g> arquivo de áudio da lixeira…}many{Moving <xliff:g id="COUNT">^1</xliff:g> audio files out of trash…}other{Retirando <xliff:g id="COUNT">^1</xliff:g> arquivos de áudio da lixeira…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> retire esse vídeo da lixeira?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> vídeo da lixeira?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos out of trash?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> vídeos da lixeira?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Retirando o vídeo da lixeira…}one{Retirando <xliff:g id="COUNT">^1</xliff:g> vídeo da lixeira…}many{Moving <xliff:g id="COUNT">^1</xliff:g> videos out of trash…}other{Retirando <xliff:g id="COUNT">^1</xliff:g> vídeos da lixeira…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> retire essa foto da lixeira?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> foto da lixeira?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos out of trash?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> fotos da lixeira?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Retirando a foto da lixeira…}one{Retirando <xliff:g id="COUNT">^1</xliff:g> foto da lixeira…}many{Moving <xliff:g id="COUNT">^1</xliff:g> photos out of trash…}other{Retirando <xliff:g id="COUNT">^1</xliff:g> fotos da lixeira…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> retire esse item da lixeira?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> item da lixeira?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items out of trash?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> retire <xliff:g id="COUNT">^2</xliff:g> itens da lixeira?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Retirando o item da lixeira…}one{Retirando <xliff:g id="COUNT">^1</xliff:g> item da lixeira…}many{Moving <xliff:g id="COUNT">^1</xliff:g> items out of trash…}other{Retirando <xliff:g id="COUNT">^1</xliff:g> itens da lixeira…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> exclua esse arquivo de áudio?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> arquivo de áudio?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> audio files?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> arquivos de áudio?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Excluindo o arquivo de áudio…}one{Excluindo <xliff:g id="COUNT">^1</xliff:g> arquivo de áudio…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> audio files…}other{Excluindo <xliff:g id="COUNT">^1</xliff:g> arquivos de áudio…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> exclua esse vídeo?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> vídeo?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> videos?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> vídeos?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Excluindo o vídeo…}one{Excluindo <xliff:g id="COUNT">^1</xliff:g> vídeo…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> videos…}other{Excluindo <xliff:g id="COUNT">^1</xliff:g> vídeos…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> exclua essa foto?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> foto?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> photos?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> fotos?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Excluindo a foto…}one{Excluindo <xliff:g id="COUNT">^1</xliff:g> foto…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> photos…}other{Excluindo <xliff:g id="COUNT">^1</xliff:g> fotos…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Permitir que o app <xliff:g id="APP_NAME_0">^1</xliff:g> exclua esse item?}one{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> item?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> items?}other{Permitir que o app <xliff:g id="APP_NAME_1">^1</xliff:g> exclua <xliff:g id="COUNT">^2</xliff:g> itens?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Excluindo o item…}one{Excluindo <xliff:g id="COUNT">^1</xliff:g> item…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> items…}other{Excluindo <xliff:g id="COUNT">^1</xliff:g> itens…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"Não é possível processar arquivos de mídia no app <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Processamento de mídia cancelado"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Erro no processamento de mídia"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 4bd0b43..5b003ba 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -19,198 +19,104 @@
<string name="uid_label" msgid="8421971615411294156">"Conținut media"</string>
<string name="storage_description" msgid="4081716890357580107">"Stocare locală"</string>
<string name="app_label" msgid="9035307001052716210">"Stocarea conținutului media"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"Selector de fotografii"</string>
<string name="artist_label" msgid="8105600993099120273">"Artist"</string>
<string name="unknown" msgid="2059049215682829375">"Necunoscut"</string>
<string name="root_images" msgid="5861633549189045666">"Imagini"</string>
<string name="root_videos" msgid="8792703517064649453">"Videoclipuri"</string>
<string name="root_audio" msgid="3505830755201326018">"Conținut audio"</string>
<string name="root_documents" msgid="3829103301363849237">"Documente"</string>
- <string name="permission_required" msgid="1460820436132943754">"Este necesară permisiunea ca să modificați sau să ștergeți acest element."</string>
- <string name="permission_required_action" msgid="706370952366113539">"Continuați"</string>
- <string name="grant_dialog_button_allow" msgid="1644287024501033471">"Permiteți"</string>
- <string name="grant_dialog_button_deny" msgid="6190589471415815741">"Refuzați"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="few">+ <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+ <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+ <xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="few">Și încă <xliff:g id="COUNT_1">^1</xliff:g> elemente</item>
- <item quantity="other">Și încă <xliff:g id="COUNT_1">^1</xliff:g> de elemente</item>
- <item quantity="one">Și încă <xliff:g id="COUNT_0">^1</xliff:g> element</item>
- </plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Ștergeți fișierele temporare ale aplicațiilor"</string>
+ <string name="permission_required" msgid="1460820436132943754">"Este necesară permisiunea ca să modifici sau să ștergi acest element."</string>
+ <string name="permission_required_action" msgid="706370952366113539">"Continuă"</string>
+ <string name="grant_dialog_button_allow" msgid="1644287024501033471">"Permite"</string>
+ <string name="grant_dialog_button_deny" msgid="6190589471415815741">"Refuz"</string>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+ <xliff:g id="COUNT_0">^1</xliff:g>}few{+ <xliff:g id="COUNT_1">^1</xliff:g>}other{+ <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Și încă <xliff:g id="COUNT_0">^1</xliff:g> element}few{Și încă <xliff:g id="COUNT_1">^1</xliff:g> elemente}other{Și încă <xliff:g id="COUNT_1">^1</xliff:g> de elemente}}"</string>
+ <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Șterge fișierele temporare ale aplicațiilor"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vrea să șteargă fișiere temporare. Aceasta poate duce la creșterea gradului de utilizare a bateriei sau a datelor mobile."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Se șterg fișierele temporare ale aplicațiilor…"</string>
- <string name="clear" msgid="5524638938415865915">"Ștergeți"</string>
- <string name="allow" msgid="8885707816848569619">"Permiteți"</string>
- <string name="deny" msgid="6040983710442068936">"Refuzați"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="few">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să modifice <xliff:g id="COUNT">^2</xliff:g> fișiere audio?</item>
- <item quantity="other">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să modifice <xliff:g id="COUNT">^2</xliff:g> de fișiere audio?</item>
- <item quantity="one">Permiteți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să modifice acest fișier audio?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="few">Se modifică <xliff:g id="COUNT">^1</xliff:g> fișiere audio…</item>
- <item quantity="other">Se modifică <xliff:g id="COUNT">^1</xliff:g> de fișiere audio…</item>
- <item quantity="one">Se modifică fișierul audio…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="few">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să modifice <xliff:g id="COUNT">^2</xliff:g> videoclipuri?</item>
- <item quantity="other">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să modifice <xliff:g id="COUNT">^2</xliff:g> de videoclipuri?</item>
- <item quantity="one">Permiteți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să modifice acest videoclip?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="few">Se modifică <xliff:g id="COUNT">^1</xliff:g> videoclipuri…</item>
- <item quantity="other">Se modifică <xliff:g id="COUNT">^1</xliff:g> de videoclipuri…</item>
- <item quantity="one">Se modifică videoclipul…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="few">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să modifice <xliff:g id="COUNT">^2</xliff:g> fotografii?</item>
- <item quantity="other">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să modifice <xliff:g id="COUNT">^2</xliff:g> de fotografii?</item>
- <item quantity="one">Permiteți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să modifice această fotografie?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="few">Se modifică <xliff:g id="COUNT">^1</xliff:g> fotografii…</item>
- <item quantity="other">Se modifică <xliff:g id="COUNT">^1</xliff:g> de fotografii…</item>
- <item quantity="one">Se modifică fotografia…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="few">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să modifice <xliff:g id="COUNT">^2</xliff:g> elemente?</item>
- <item quantity="other">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să modifice <xliff:g id="COUNT">^2</xliff:g> de elemente?</item>
- <item quantity="one">Permiteți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să modifice acest element?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="few">Se modifică <xliff:g id="COUNT">^1</xliff:g> elemente…</item>
- <item quantity="other">Se modifică <xliff:g id="COUNT">^1</xliff:g> de elemente…</item>
- <item quantity="one">Se modifică un element…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="few">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să mute <xliff:g id="COUNT">^2</xliff:g> fișiere audio în coșul de gunoi?</item>
- <item quantity="other">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să mute <xliff:g id="COUNT">^2</xliff:g> de fișiere audio în coșul de gunoi?</item>
- <item quantity="one">Permiteți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să mute acest fișier audio în coșul de gunoi?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="few">Se mută <xliff:g id="COUNT">^1</xliff:g> fișiere audio în coșul de gunoi…</item>
- <item quantity="other">Se mută <xliff:g id="COUNT">^1</xliff:g> de fișiere audio în coșul de gunoi…</item>
- <item quantity="one">Se mută fișierul audio în coșul de gunoi…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="few">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să mute <xliff:g id="COUNT">^2</xliff:g> videoclipuri în coșul de gunoi?</item>
- <item quantity="other">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să mute <xliff:g id="COUNT">^2</xliff:g> de videoclipuri în coșul de gunoi?</item>
- <item quantity="one">Permiteți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să mute acest videoclip în coșul de gunoi?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="few">Se mută <xliff:g id="COUNT">^1</xliff:g> videoclipuri în coșul de gunoi…</item>
- <item quantity="other">Se mută <xliff:g id="COUNT">^1</xliff:g> de videoclipuri în coșul de gunoi…</item>
- <item quantity="one">Se mută videoclipul în coșul de gunoi…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="few">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să mute <xliff:g id="COUNT">^2</xliff:g> fotografii în coșul de gunoi?</item>
- <item quantity="other">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să mute <xliff:g id="COUNT">^2</xliff:g> de fotografii în coșul de gunoi?</item>
- <item quantity="one">Permiteți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să mute această fotografie în coșul de gunoi?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="few">Se mută <xliff:g id="COUNT">^1</xliff:g> fotografii în coșul de gunoi…</item>
- <item quantity="other">Se mută <xliff:g id="COUNT">^1</xliff:g> de fotografii în coșul de gunoi…</item>
- <item quantity="one">Se mută fotografia în coșul de gunoi…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="few">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să mute <xliff:g id="COUNT">^2</xliff:g> elemente în coșul de gunoi?</item>
- <item quantity="other">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să mute <xliff:g id="COUNT">^2</xliff:g> de elemente în coșul de gunoi?</item>
- <item quantity="one">Permiteți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să mute acest element în coșul de gunoi?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="few">Se mută <xliff:g id="COUNT">^1</xliff:g> elemente în coșul de gunoi…</item>
- <item quantity="other">Se mută <xliff:g id="COUNT">^1</xliff:g> de elemente în coșul de gunoi…</item>
- <item quantity="one">Se mută elementul în coșul de gunoi…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="few">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să scoată <xliff:g id="COUNT">^2</xliff:g> fișiere audio din coșul de gunoi?</item>
- <item quantity="other">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să scoată <xliff:g id="COUNT">^2</xliff:g> de fișiere audio din coșul de gunoi?</item>
- <item quantity="one">Permiteți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să scoată acest fișier audio din coșul de gunoi?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="few">Se scot <xliff:g id="COUNT">^1</xliff:g> fișiere audio din coșul de gunoi…</item>
- <item quantity="other">Se scot <xliff:g id="COUNT">^1</xliff:g> de fișiere audio din coșul de gunoi…</item>
- <item quantity="one">Se scoate fișierul audio din coșul de gunoi…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="few">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să scoată <xliff:g id="COUNT">^2</xliff:g> videoclipuri din coșul de gunoi?</item>
- <item quantity="other">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să scoată <xliff:g id="COUNT">^2</xliff:g> de videoclipuri din coșul de gunoi?</item>
- <item quantity="one">Permiteți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să scoată acest videoclip din coșul de gunoi?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="few">Se scot <xliff:g id="COUNT">^1</xliff:g> videoclipuri din coșul de gunoi…</item>
- <item quantity="other">Se scot <xliff:g id="COUNT">^1</xliff:g> de videoclipuri din coșul de gunoi…</item>
- <item quantity="one">Se scoate videoclipul din coșul de gunoi…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="few">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să scoată <xliff:g id="COUNT">^2</xliff:g> fotografii din coșul de gunoi?</item>
- <item quantity="other">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să scoată <xliff:g id="COUNT">^2</xliff:g> de fotografii din coșul de gunoi?</item>
- <item quantity="one">Permiteți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să scoată această fotografie din coșul de gunoi?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="few">Se scot <xliff:g id="COUNT">^1</xliff:g> fotografii din coșul de gunoi…</item>
- <item quantity="other">Se scot <xliff:g id="COUNT">^1</xliff:g> de fotografii din coșul de gunoi…</item>
- <item quantity="one">Se scoate fotografia din coșul de gunoi…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="few">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să scoată <xliff:g id="COUNT">^2</xliff:g> elemente din coșul de gunoi?</item>
- <item quantity="other">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să scoată <xliff:g id="COUNT">^2</xliff:g> de elemente din coșul de gunoi?</item>
- <item quantity="one">Permiteți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să scoată acest element din coșul de gunoi?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="few">Se scot <xliff:g id="COUNT">^1</xliff:g> elemente din coșul de gunoi…</item>
- <item quantity="other">Se scot <xliff:g id="COUNT">^1</xliff:g> de elemente din coșul de gunoi…</item>
- <item quantity="one">Se scoate elementul din coșul de gunoi…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="few">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să șteargă <xliff:g id="COUNT">^2</xliff:g> fișiere audio?</item>
- <item quantity="other">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să șteargă <xliff:g id="COUNT">^2</xliff:g> de fișiere audio?</item>
- <item quantity="one">Permiteți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să șteargă acest fișier audio?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="few">Se șterg <xliff:g id="COUNT">^1</xliff:g> fișiere audio…</item>
- <item quantity="other">Se șterg <xliff:g id="COUNT">^1</xliff:g> de fișiere audio…</item>
- <item quantity="one">Se șterge fișierul audio…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="few">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să șteargă <xliff:g id="COUNT">^2</xliff:g> videoclipuri?</item>
- <item quantity="other">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să șteargă <xliff:g id="COUNT">^2</xliff:g> de videoclipuri?</item>
- <item quantity="one">Permiteți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să șteargă acest videoclip?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="few">Se șterg <xliff:g id="COUNT">^1</xliff:g> videoclipuri…</item>
- <item quantity="other">Se șterg <xliff:g id="COUNT">^1</xliff:g> de videoclipuri…</item>
- <item quantity="one">Se șterge videoclipul…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="few">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să șteargă <xliff:g id="COUNT">^2</xliff:g> fotografii?</item>
- <item quantity="other">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să șteargă <xliff:g id="COUNT">^2</xliff:g> de fotografii?</item>
- <item quantity="one">Permiteți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să șteargă această fotografie?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="few">Se șterg <xliff:g id="COUNT">^1</xliff:g> fotografii…</item>
- <item quantity="other">Se șterg <xliff:g id="COUNT">^1</xliff:g> de fotografii…</item>
- <item quantity="one">Se șterge fotografia…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="few">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să șteargă <xliff:g id="COUNT">^2</xliff:g> elemente?</item>
- <item quantity="other">Permiteți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să șteargă <xliff:g id="COUNT">^2</xliff:g> de elemente?</item>
- <item quantity="one">Permiteți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să șteargă acest element?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="few">Se șterg <xliff:g id="COUNT">^1</xliff:g> elemente…</item>
- <item quantity="other">Se șterg <xliff:g id="COUNT">^1</xliff:g> de elemente…</item>
- <item quantity="one">Se șterge elementul…</item>
- </plurals>
+ <string name="clear" msgid="5524638938415865915">"Șterge"</string>
+ <string name="allow" msgid="8885707816848569619">"Permite"</string>
+ <string name="deny" msgid="6040983710442068936">"Refuz"</string>
+ <string name="add" msgid="2894574044585549298">"Adaugă"</string>
+ <string name="deselect" msgid="4297825044827769490">"Debifează"</string>
+ <string name="deselected" msgid="8488133193326208475">"Deselectat"</string>
+ <string name="select" msgid="2704765470563027689">"Selectează"</string>
+ <string name="selected" msgid="9151797369975828124">"Selectat"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Selectează maximum <xliff:g id="COUNT_0">^1</xliff:g> element}few{Selectează maximum <xliff:g id="COUNT_1">^1</xliff:g> elemente}other{Selectează maximum <xliff:g id="COUNT_1">^1</xliff:g> de elemente}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Recente"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Nu există fotografii sau videoclipuri"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Niciun album"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Vezi elementele selectate"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Fotografii"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albume"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Previzualizare"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Comută la serviciu"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Comută la personal"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Blocat de administrator"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Accesarea datelor de lucru dintr-o aplicație personală nu este permisă"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Accesarea datelor cu caracter personal dintr-o aplicație pentru lucru nu este permisă"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Aplicațiile pentru lucru sunt întrerupte"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Ca să deschizi fotografiile de lucru, pornește aplicațiile pentru lucru și încearcă din nou"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Aplicația poate accesa doar fotografiile pe care le selectezi"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> element}few{<xliff:g id="COUNT_1">^1</xliff:g> elemente}other{<xliff:g id="COUNT_1">^1</xliff:g> de elemente}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Adaugă (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Cameră"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Descărcări"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Preferate"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Capturi de ecran"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Fotografie animată"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"S-a creat <xliff:g id="ITEM_NAME">%1$s</xliff:g> pe <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Videoclip înregistrat pe <xliff:g id="TIME">%1$s</xliff:g> cu durata de <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Fotografie"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Fotografie animată"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Dezactivează sunetul videoclipului"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Activează sunetul videoclipului"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Redă videoclipul"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Întrerupe videoclipul"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Conținutul media în cloud este acum disponibil din <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"neselectat"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Permiți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să modifice acest fișier audio?}few{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să modifice <xliff:g id="COUNT">^2</xliff:g> fișiere audio?}other{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să modifice <xliff:g id="COUNT">^2</xliff:g> de fișiere audio?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Se modifică fișierul audio…}few{Se modifică <xliff:g id="COUNT">^1</xliff:g> fișiere audio…}other{Se modifică <xliff:g id="COUNT">^1</xliff:g> de fișiere audio…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Permiți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să modifice acest videoclip?}few{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să modifice <xliff:g id="COUNT">^2</xliff:g> videoclipuri?}other{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să modifice <xliff:g id="COUNT">^2</xliff:g> de videoclipuri?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Se modifică videoclipul…}few{Se modifică <xliff:g id="COUNT">^1</xliff:g> videoclipuri…}other{Se modifică <xliff:g id="COUNT">^1</xliff:g> de videoclipuri…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Permiți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să modifice această fotografie?}few{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să modifice <xliff:g id="COUNT">^2</xliff:g> fotografii?}other{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să modifice <xliff:g id="COUNT">^2</xliff:g> de fotografii?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Se modifică fotografia…}few{Se modifică <xliff:g id="COUNT">^1</xliff:g> fotografii…}other{Se modifică <xliff:g id="COUNT">^1</xliff:g> de fotografii…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Permiți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să modifice acest element?}few{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să modifice <xliff:g id="COUNT">^2</xliff:g> elemente?}other{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să modifice <xliff:g id="COUNT">^2</xliff:g> de elemente?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Se modifică un element…}few{Se modifică <xliff:g id="COUNT">^1</xliff:g> elemente…}other{Se modifică <xliff:g id="COUNT">^1</xliff:g> de elemente…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Permiți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să mute acest fișier audio în coșul de gunoi?}few{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să mute <xliff:g id="COUNT">^2</xliff:g> fișiere audio în coșul de gunoi?}other{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să mute <xliff:g id="COUNT">^2</xliff:g> de fișiere audio în coșul de gunoi?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Se mută fișierul audio în coșul de gunoi…}few{Se mută <xliff:g id="COUNT">^1</xliff:g> fișiere audio în coșul de gunoi…}other{Se mută <xliff:g id="COUNT">^1</xliff:g> de fișiere audio în coșul de gunoi…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Permiți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să mute acest videoclip în coșul de gunoi?}few{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să mute <xliff:g id="COUNT">^2</xliff:g> videoclipuri în coșul de gunoi?}other{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să mute <xliff:g id="COUNT">^2</xliff:g> de videoclipuri în coșul de gunoi?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Se mută videoclipul în coșul de gunoi…}few{Se mută <xliff:g id="COUNT">^1</xliff:g> videoclipuri în coșul de gunoi…}other{Se mută <xliff:g id="COUNT">^1</xliff:g> de videoclipuri în coșul de gunoi…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Permiți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să mute această fotografie în coșul de gunoi?}few{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să mute <xliff:g id="COUNT">^2</xliff:g> fotografii în coșul de gunoi?}other{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să mute <xliff:g id="COUNT">^2</xliff:g> de fotografii în coșul de gunoi?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Se mută fotografia în coșul de gunoi…}few{Se mută <xliff:g id="COUNT">^1</xliff:g> fotografii în coșul de gunoi…}other{Se mută <xliff:g id="COUNT">^1</xliff:g> de fotografii în coșul de gunoi…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Permiți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să mute acest element în coșul de gunoi?}few{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să mute <xliff:g id="COUNT">^2</xliff:g> elemente în coșul de gunoi?}other{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să mute <xliff:g id="COUNT">^2</xliff:g> de elemente în coșul de gunoi?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Se mută elementul în coșul de gunoi…}few{Se mută <xliff:g id="COUNT">^1</xliff:g> elemente în coșul de gunoi…}other{Se mută <xliff:g id="COUNT">^1</xliff:g> de elemente în coșul de gunoi…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Permiți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să scoată acest fișier audio din coșul de gunoi?}few{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să scoată <xliff:g id="COUNT">^2</xliff:g> fișiere audio din coșul de gunoi?}other{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să scoată <xliff:g id="COUNT">^2</xliff:g> de fișiere audio din coșul de gunoi?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Se scoate fișierul audio din coșul de gunoi…}few{Se scot <xliff:g id="COUNT">^1</xliff:g> fișiere audio din coșul de gunoi…}other{Se scot <xliff:g id="COUNT">^1</xliff:g> de fișiere audio din coșul de gunoi…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Permiți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să scoată acest videoclip din coșul de gunoi?}few{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să scoată <xliff:g id="COUNT">^2</xliff:g> videoclipuri din coșul de gunoi?}other{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să scoată <xliff:g id="COUNT">^2</xliff:g> de videoclipuri din coșul de gunoi?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Se scoate videoclipul din coșul de gunoi…}few{Se scot <xliff:g id="COUNT">^1</xliff:g> videoclipuri din coșul de gunoi…}other{Se scot <xliff:g id="COUNT">^1</xliff:g> de videoclipuri din coșul de gunoi…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Permiți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să scoată această fotografie din coșul de gunoi?}few{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să scoată <xliff:g id="COUNT">^2</xliff:g> fotografii din coșul de gunoi?}other{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să scoată <xliff:g id="COUNT">^2</xliff:g> de fotografii din coșul de gunoi?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Se scoate fotografia din coșul de gunoi…}few{Se scot <xliff:g id="COUNT">^1</xliff:g> fotografii din coșul de gunoi…}other{Se scot <xliff:g id="COUNT">^1</xliff:g> de fotografii din coșul de gunoi…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Permiți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să scoată acest element din coșul de gunoi?}few{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să scoată <xliff:g id="COUNT">^2</xliff:g> elemente din coșul de gunoi?}other{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să scoată <xliff:g id="COUNT">^2</xliff:g> de elemente din coșul de gunoi?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Se scoate elementul din coșul de gunoi…}few{Se scot <xliff:g id="COUNT">^1</xliff:g> elemente din coșul de gunoi…}other{Se scot <xliff:g id="COUNT">^1</xliff:g> de elemente din coșul de gunoi…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Permiți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să șteargă acest fișier audio?}few{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să șteargă <xliff:g id="COUNT">^2</xliff:g> fișiere audio?}other{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să șteargă <xliff:g id="COUNT">^2</xliff:g> de fișiere audio?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Se șterge fișierul audio…}few{Se șterg <xliff:g id="COUNT">^1</xliff:g> fișiere audio…}other{Se șterg <xliff:g id="COUNT">^1</xliff:g> de fișiere audio…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Permiți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să șteargă acest videoclip?}few{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să șteargă <xliff:g id="COUNT">^2</xliff:g> videoclipuri?}other{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să șteargă <xliff:g id="COUNT">^2</xliff:g> de videoclipuri?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Se șterge videoclipul…}few{Se șterg <xliff:g id="COUNT">^1</xliff:g> videoclipuri…}other{Se șterg <xliff:g id="COUNT">^1</xliff:g> de videoclipuri…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Permiți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să șteargă această fotografie?}few{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să șteargă <xliff:g id="COUNT">^2</xliff:g> fotografii?}other{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să șteargă <xliff:g id="COUNT">^2</xliff:g> de fotografii?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Se șterge fotografia…}few{Se șterg <xliff:g id="COUNT">^1</xliff:g> fotografii…}other{Se șterg <xliff:g id="COUNT">^1</xliff:g> de fotografii…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Permiți ca <xliff:g id="APP_NAME_0">^1</xliff:g> să șteargă acest element?}few{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să șteargă <xliff:g id="COUNT">^2</xliff:g> elemente?}other{Permiți ca <xliff:g id="APP_NAME_1">^1</xliff:g> să șteargă <xliff:g id="COUNT">^2</xliff:g> de elemente?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Se șterge elementul…}few{Se șterg <xliff:g id="COUNT">^1</xliff:g> elemente…}other{Se șterg <xliff:g id="COUNT">^1</xliff:g> de elemente…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> nu poate procesa fișiere media"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Procesarea conținutului media a fost anulată"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Eroare la procesarea conținutului media"</string>
<string name="transcode_processing_success" msgid="447288876429730122">"Procesarea conținutului media s-a finalizat"</string>
<string name="transcode_processing_started" msgid="7789086308155361523">"Procesarea conținutului media a început"</string>
<string name="transcode_processing" msgid="6753136468864077258">"Se procesează conținutul media…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Anulați"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Așteptați"</string>
+ <string name="transcode_cancel" msgid="8555752601907598192">"Anulează"</string>
+ <string name="transcode_wait" msgid="8909773149560697501">"Așteaptă"</string>
</resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 054d9fc..e9c51f0 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Мультимедиа"</string>
<string name="storage_description" msgid="4081716890357580107">"Локальное хранилище"</string>
<string name="app_label" msgid="9035307001052716210">"Хранилище мультимедиа"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"Выбор фотографий"</string>
<string name="artist_label" msgid="8105600993099120273">"Исполнитель"</string>
<string name="unknown" msgid="2059049215682829375">"Неизвестно"</string>
<string name="root_images" msgid="5861633549189045666">"Изображения"</string>
@@ -29,216 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Продолжить"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Разрешить"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Запретить"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">Ещё <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="few">Ещё <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="many">Ещё <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">Ещё <xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">и ещё <xliff:g id="COUNT_1">^1</xliff:g> объект</item>
- <item quantity="few">и ещё <xliff:g id="COUNT_1">^1</xliff:g> объекта</item>
- <item quantity="many">и ещё <xliff:g id="COUNT_1">^1</xliff:g> объектов</item>
- <item quantity="other">и ещё <xliff:g id="COUNT_1">^1</xliff:g> объекта</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{Ещё <xliff:g id="COUNT_0">^1</xliff:g>}one{Ещё <xliff:g id="COUNT_1">^1</xliff:g>}few{Ещё <xliff:g id="COUNT_1">^1</xliff:g>}many{Ещё <xliff:g id="COUNT_1">^1</xliff:g>}other{Ещё <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{И ещё <xliff:g id="COUNT_0">^1</xliff:g> объект}one{И ещё <xliff:g id="COUNT_1">^1</xliff:g> объект}few{И ещё <xliff:g id="COUNT_1">^1</xliff:g> объекта}many{И ещё <xliff:g id="COUNT_1">^1</xliff:g> объектов}other{И ещё <xliff:g id="COUNT_1">^1</xliff:g> объекта}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Удалить временные файлы приложений"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"Приложение \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" запрашивает разрешение на удаление временных файлов. Это может увеличить расход заряда или трафика."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Удаление временных файлов приложения…"</string>
<string name="clear" msgid="5524638938415865915">"Удалить"</string>
<string name="allow" msgid="8885707816848569619">"Разрешить"</string>
<string name="deny" msgid="6040983710442068936">"Запретить"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> аудиофайл?</item>
- <item quantity="few">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> аудиофайла?</item>
- <item quantity="many">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> аудиофайлов?</item>
- <item quantity="other">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> аудиофайла?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one">Изменение <xliff:g id="COUNT">^1</xliff:g> аудиофайла…</item>
- <item quantity="few">Изменение <xliff:g id="COUNT">^1</xliff:g> аудиофайлов…</item>
- <item quantity="many">Изменение <xliff:g id="COUNT">^1</xliff:g> аудиофайлов…</item>
- <item quantity="other">Изменение <xliff:g id="COUNT">^1</xliff:g> аудиофайла…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> видео?</item>
- <item quantity="few">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> видео?</item>
- <item quantity="many">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> видео?</item>
- <item quantity="other">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> видео?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one">Изменение <xliff:g id="COUNT">^1</xliff:g> видео…</item>
- <item quantity="few">Изменение <xliff:g id="COUNT">^1</xliff:g> видео…</item>
- <item quantity="many">Изменение <xliff:g id="COUNT">^1</xliff:g> видео…</item>
- <item quantity="other">Изменение <xliff:g id="COUNT">^1</xliff:g> видео…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> фото?</item>
- <item quantity="few">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> фото?</item>
- <item quantity="many">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> фото?</item>
- <item quantity="other">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> фото?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one">Изменение <xliff:g id="COUNT">^1</xliff:g> фотографии…</item>
- <item quantity="few">Изменение <xliff:g id="COUNT">^1</xliff:g> фотографий…</item>
- <item quantity="many">Изменение <xliff:g id="COUNT">^1</xliff:g> фотографий…</item>
- <item quantity="other">Изменение <xliff:g id="COUNT">^1</xliff:g> фотографии…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> объект?</item>
- <item quantity="few">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> объекта?</item>
- <item quantity="many">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> объектов?</item>
- <item quantity="other">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> объекта?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one">Изменение <xliff:g id="COUNT">^1</xliff:g> объекта…</item>
- <item quantity="few">Изменение <xliff:g id="COUNT">^1</xliff:g> объектов…</item>
- <item quantity="many">Изменение <xliff:g id="COUNT">^1</xliff:g> объектов…</item>
- <item quantity="other">Изменение <xliff:g id="COUNT">^1</xliff:g> объекта…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> аудиофайл в корзину?</item>
- <item quantity="few">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> аудиофайла в корзину?</item>
- <item quantity="many">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> аудиофайлов в корзину?</item>
- <item quantity="other">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> аудиофайла в корзину?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one">Перемещение <xliff:g id="COUNT">^1</xliff:g> аудиофайла в корзину…</item>
- <item quantity="few">Перемещение <xliff:g id="COUNT">^1</xliff:g> аудиофайлов в корзину…</item>
- <item quantity="many">Перемещение <xliff:g id="COUNT">^1</xliff:g> аудиофайлов в корзину…</item>
- <item quantity="other">Перемещение <xliff:g id="COUNT">^1</xliff:g> аудиофайла в корзину…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> видео в корзину?</item>
- <item quantity="few">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> видео в корзину?</item>
- <item quantity="many">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> видео в корзину?</item>
- <item quantity="other">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> видео в корзину?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one">Перемещение <xliff:g id="COUNT">^1</xliff:g> видео в корзину…</item>
- <item quantity="few">Перемещение <xliff:g id="COUNT">^1</xliff:g> видео в корзину…</item>
- <item quantity="many">Перемещение <xliff:g id="COUNT">^1</xliff:g> видео в корзину…</item>
- <item quantity="other">Перемещение <xliff:g id="COUNT">^1</xliff:g> видео в корзину…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> фото в корзину?</item>
- <item quantity="few">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> фото в корзину?</item>
- <item quantity="many">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> фото в корзину?</item>
- <item quantity="other">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> фото в корзину?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one">Перемещение <xliff:g id="COUNT">^1</xliff:g> фотографии в корзину…</item>
- <item quantity="few">Перемещение <xliff:g id="COUNT">^1</xliff:g> фотографий в корзину…</item>
- <item quantity="many">Перемещение <xliff:g id="COUNT">^1</xliff:g> фотографий в корзину…</item>
- <item quantity="other">Перемещение <xliff:g id="COUNT">^1</xliff:g> фотографии в корзину…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> объект в корзину?</item>
- <item quantity="few">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> объекта в корзину?</item>
- <item quantity="many">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> объектов в корзину?</item>
- <item quantity="other">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> объекта в корзину?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one">Перемещение <xliff:g id="COUNT">^1</xliff:g> объекта в корзину…</item>
- <item quantity="few">Перемещение <xliff:g id="COUNT">^1</xliff:g> объектов в корзину…</item>
- <item quantity="many">Перемещение <xliff:g id="COUNT">^1</xliff:g> объектов в корзину…</item>
- <item quantity="other">Перемещение <xliff:g id="COUNT">^1</xliff:g> объекта в корзину…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> аудиофайл из корзины?</item>
- <item quantity="few">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> аудиофайла из корзины?</item>
- <item quantity="many">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> аудиофайлов из корзины?</item>
- <item quantity="other">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> аудиофайла из корзины?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one">Восстановление <xliff:g id="COUNT">^1</xliff:g> аудиофайла из корзины…</item>
- <item quantity="few">Восстановление <xliff:g id="COUNT">^1</xliff:g> аудиофайлов из корзины…</item>
- <item quantity="many">Восстановление <xliff:g id="COUNT">^1</xliff:g> аудиофайлов из корзины…</item>
- <item quantity="other">Восстановление <xliff:g id="COUNT">^1</xliff:g> аудиофайла из корзины…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> видео из корзины?</item>
- <item quantity="few">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> видео из корзины?</item>
- <item quantity="many">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> видео из корзины?</item>
- <item quantity="other">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> видео из корзины?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one">Восстановление <xliff:g id="COUNT">^1</xliff:g> видео из корзины…</item>
- <item quantity="few">Восстановление <xliff:g id="COUNT">^1</xliff:g> видео из корзины…</item>
- <item quantity="many">Восстановление <xliff:g id="COUNT">^1</xliff:g> видео из корзины…</item>
- <item quantity="other">Восстановление <xliff:g id="COUNT">^1</xliff:g> видео из корзины…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> фото из корзины?</item>
- <item quantity="few">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> фото из корзины?</item>
- <item quantity="many">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> фото из корзины?</item>
- <item quantity="other">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> фото из корзины?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one">Восстановление <xliff:g id="COUNT">^1</xliff:g> фотографии из корзины…</item>
- <item quantity="few">Восстановление <xliff:g id="COUNT">^1</xliff:g> фотографий из корзины…</item>
- <item quantity="many">Восстановление <xliff:g id="COUNT">^1</xliff:g> фотографий из корзины…</item>
- <item quantity="other">Восстановление <xliff:g id="COUNT">^1</xliff:g> фотографии из корзины…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> объект из корзины?</item>
- <item quantity="few">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> объекта из корзины?</item>
- <item quantity="many">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> объектов из корзины?</item>
- <item quantity="other">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> объекта из корзины?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one">Восстановление <xliff:g id="COUNT">^1</xliff:g> объекта из корзины…</item>
- <item quantity="few">Восстановление <xliff:g id="COUNT">^1</xliff:g> объектов из корзины…</item>
- <item quantity="many">Восстановление <xliff:g id="COUNT">^1</xliff:g> объектов из корзины…</item>
- <item quantity="other">Восстановление <xliff:g id="COUNT">^1</xliff:g> объекта из корзины…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> аудиофайл?</item>
- <item quantity="few">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> аудиофайла?</item>
- <item quantity="many">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> аудиофайлов?</item>
- <item quantity="other">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> аудиофайла?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one">Удаление <xliff:g id="COUNT">^1</xliff:g> аудиофайла…</item>
- <item quantity="few">Удаление <xliff:g id="COUNT">^1</xliff:g> аудиофайлов…</item>
- <item quantity="many">Удаление <xliff:g id="COUNT">^1</xliff:g> аудиофайлов…</item>
- <item quantity="other">Удаление <xliff:g id="COUNT">^1</xliff:g> аудиофайла…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> видео?</item>
- <item quantity="few">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> видео?</item>
- <item quantity="many">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> видео?</item>
- <item quantity="other">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> видео?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one">Удаление <xliff:g id="COUNT">^1</xliff:g> видео…</item>
- <item quantity="few">Удаление <xliff:g id="COUNT">^1</xliff:g> видео…</item>
- <item quantity="many">Удаление <xliff:g id="COUNT">^1</xliff:g> видео…</item>
- <item quantity="other">Удаление <xliff:g id="COUNT">^1</xliff:g> видео…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> фото?</item>
- <item quantity="few">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> фото?</item>
- <item quantity="many">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> фото?</item>
- <item quantity="other">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> фото?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one">Удаление <xliff:g id="COUNT">^1</xliff:g> фотографии…</item>
- <item quantity="few">Удаление <xliff:g id="COUNT">^1</xliff:g> фотографий…</item>
- <item quantity="many">Удаление <xliff:g id="COUNT">^1</xliff:g> фотографий…</item>
- <item quantity="other">Удаление <xliff:g id="COUNT">^1</xliff:g> фотографии…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> объект?</item>
- <item quantity="few">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> объекта?</item>
- <item quantity="many">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> объектов?</item>
- <item quantity="other">Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> объекта?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one">Удаление <xliff:g id="COUNT">^1</xliff:g> объекта…</item>
- <item quantity="few">Удаление <xliff:g id="COUNT">^1</xliff:g> объектов…</item>
- <item quantity="many">Удаление <xliff:g id="COUNT">^1</xliff:g> объектов…</item>
- <item quantity="other">Удаление <xliff:g id="COUNT">^1</xliff:g> объекта…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Добавить"</string>
+ <string name="deselect" msgid="4297825044827769490">"Отменить выбор"</string>
+ <string name="deselected" msgid="8488133193326208475">"Выбор отменен"</string>
+ <string name="select" msgid="2704765470563027689">"Выбрать"</string>
+ <string name="selected" msgid="9151797369975828124">"Выбрано"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Выберите не более <xliff:g id="COUNT_0">^1</xliff:g> объекта.}one{Выберите не более <xliff:g id="COUNT_1">^1</xliff:g> объекта.}few{Выберите не более <xliff:g id="COUNT_1">^1</xliff:g> объектов.}many{Выберите не более <xliff:g id="COUNT_1">^1</xliff:g> объектов.}other{Выберите не более <xliff:g id="COUNT_1">^1</xliff:g> объекта.}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Недавние"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Фото или видео нет."</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Альбомов нет."</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Смотреть выбранное"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Фотографии"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Альбомы"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Предварительный просмотр"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Перейти в рабочий профиль"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Перейти в личный профиль"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Заблокировано администратором"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Доступ к рабочим данным из личного приложения запрещен."</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Доступ к персональным данным из рабочего приложения запрещен."</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Рабочие приложения приостановлены"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Чтобы открыть рабочие фотографии, включите рабочие приложения и повторите попытку."</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"У этого приложения есть доступ только к тем фото, которые вы выбрали."</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> объект}one{<xliff:g id="COUNT_1">^1</xliff:g> объект}few{<xliff:g id="COUNT_1">^1</xliff:g> объекта}many{<xliff:g id="COUNT_1">^1</xliff:g> объектов}other{<xliff:g id="COUNT_1">^1</xliff:g> объекта}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Добавить (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Камера"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Скачанные"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Избранное"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Скриншоты"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Фото с движением"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g>, дата и время съемки: <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Время съемки: <xliff:g id="TIME">%1$s</xliff:g>. Длительность: <xliff:g id="DURATION">%2$s</xliff:g>."</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Фото"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Фото с движением"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Отключить звук видео"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Включить звук видео"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Воспроизвести видео"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Приостановить видео"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Медиаконтент из облака теперь доступен в приложении \"<xliff:g id="PKG_NAME">%1$s</xliff:g>\"."</string>
+ <string name="not_selected" msgid="2244008151669896758">"не выбрано"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Разрешить приложению \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" изменить этот аудиофайл?}one{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> аудиофайл?}few{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> аудиофайла?}many{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> аудиофайлов?}other{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> аудиофайла?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Изменение аудиофайла…}one{Изменение <xliff:g id="COUNT">^1</xliff:g> аудиофайла…}few{Изменение <xliff:g id="COUNT">^1</xliff:g> аудиофайлов…}many{Изменение <xliff:g id="COUNT">^1</xliff:g> аудиофайлов…}other{Изменение <xliff:g id="COUNT">^1</xliff:g> аудиофайла…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Разрешить приложению \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" изменить это видео?}one{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> видео?}few{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> видео?}many{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> видео?}other{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> видео?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Изменение видео…}one{Изменение <xliff:g id="COUNT">^1</xliff:g> видео…}few{Изменение <xliff:g id="COUNT">^1</xliff:g> видео…}many{Изменение <xliff:g id="COUNT">^1</xliff:g> видео…}other{Изменение <xliff:g id="COUNT">^1</xliff:g> видео…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Разрешить приложению \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" изменить это фото?}one{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> фото?}few{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> фото?}many{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> фото?}other{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> фото?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Изменение фото…}one{Изменение <xliff:g id="COUNT">^1</xliff:g> фото…}few{Изменение <xliff:g id="COUNT">^1</xliff:g> фото…}many{Изменение <xliff:g id="COUNT">^1</xliff:g> фото…}other{Изменение <xliff:g id="COUNT">^1</xliff:g> фото…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Разрешить приложению \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" изменить этот объект?}one{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> объект?}few{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> объекта?}many{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> объектов?}other{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" изменить <xliff:g id="COUNT">^2</xliff:g> объекта?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Изменение объекта…}one{Изменение <xliff:g id="COUNT">^1</xliff:g> объекта…}few{Изменение <xliff:g id="COUNT">^1</xliff:g> объектов…}many{Изменение <xliff:g id="COUNT">^1</xliff:g> объектов…}other{Изменение <xliff:g id="COUNT">^1</xliff:g> объекта…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Разрешить приложению \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" переместить этот аудиофайл в корзину?}one{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> аудиофайл в корзину?}few{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> аудиофайла в корзину?}many{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> аудиофайлов в корзину?}other{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> аудиофайла в корзину?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Перемещение аудиофайла в корзину…}one{Перемещение <xliff:g id="COUNT">^1</xliff:g> аудиофайла в корзину…}few{Перемещение <xliff:g id="COUNT">^1</xliff:g> аудиофайлов в корзину…}many{Перемещение <xliff:g id="COUNT">^1</xliff:g> аудиофайлов в корзину…}other{Перемещение <xliff:g id="COUNT">^1</xliff:g> аудиофайла в корзину…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Разрешить приложению \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" переместить это видео в корзину?}one{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> видео в корзину?}few{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> видео в корзину?}many{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> видео в корзину?}other{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> видео в корзину?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Перемещение видео в корзину…}one{Перемещение <xliff:g id="COUNT">^1</xliff:g> видео в корзину…}few{Перемещение <xliff:g id="COUNT">^1</xliff:g> видео в корзину…}many{Перемещение <xliff:g id="COUNT">^1</xliff:g> видео в корзину…}other{Перемещение <xliff:g id="COUNT">^1</xliff:g> видео в корзину…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Разрешить приложению \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" переместить это фото в корзину?}one{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> фото в корзину?}few{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> фото в корзину?}many{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> фото в корзину?}other{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> фото в корзину?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Перемещение фото в корзину…}one{Перемещение <xliff:g id="COUNT">^1</xliff:g> фото в корзину…}few{Перемещение <xliff:g id="COUNT">^1</xliff:g> фото в корзину…}many{Перемещение <xliff:g id="COUNT">^1</xliff:g> фото в корзину…}other{Перемещение <xliff:g id="COUNT">^1</xliff:g> фото в корзину…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Разрешить приложению \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" переместить этот объект в корзину?}one{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> объект в корзину?}few{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> объекта в корзину?}many{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> объектов в корзину?}other{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" переместить <xliff:g id="COUNT">^2</xliff:g> объекта в корзину?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Перемещение объекта в корзину…}one{Перемещение <xliff:g id="COUNT">^1</xliff:g> объекта в корзину…}few{Перемещение <xliff:g id="COUNT">^1</xliff:g> объектов в корзину…}many{Перемещение <xliff:g id="COUNT">^1</xliff:g> объектов в корзину…}other{Перемещение <xliff:g id="COUNT">^1</xliff:g> объекта в корзину…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Разрешить приложению \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" восстановить этот аудиофайл из корзины?}one{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> аудиофайл из корзины?}few{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> аудиофайла из корзины?}many{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> аудиофайлов из корзины?}other{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> аудиофайла из корзины?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Восстановление аудиофайла из корзины…}one{Восстановление <xliff:g id="COUNT">^1</xliff:g> аудиофайла из корзины…}few{Восстановление <xliff:g id="COUNT">^1</xliff:g> аудиофайлов из корзины…}many{Восстановление <xliff:g id="COUNT">^1</xliff:g> аудиофайлов из корзины…}other{Восстановление <xliff:g id="COUNT">^1</xliff:g> аудиофайла из корзины…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Разрешить приложению \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" восстановить это видео из корзины?}one{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> видео из корзины?}few{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> видео из корзины?}many{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> видео из корзины?}other{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> видео из корзины?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Восстановление видео из корзины…}one{Восстановление <xliff:g id="COUNT">^1</xliff:g> видео из корзины…}few{Восстановление <xliff:g id="COUNT">^1</xliff:g> видео из корзины…}many{Восстановление <xliff:g id="COUNT">^1</xliff:g> видео из корзины…}other{Восстановление <xliff:g id="COUNT">^1</xliff:g> видео из корзины…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Разрешить приложению \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" восстановить это фото из корзины?}one{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> фото из корзины?}few{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> фото из корзины?}many{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> фото из корзины?}other{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> фото из корзины?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Восстановление фото из корзины…}one{Восстановление <xliff:g id="COUNT">^1</xliff:g> фото из корзины…}few{Восстановление <xliff:g id="COUNT">^1</xliff:g> фото из корзины…}many{Восстановление <xliff:g id="COUNT">^1</xliff:g> фото из корзины…}other{Восстановление <xliff:g id="COUNT">^1</xliff:g> фото из корзины…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Разрешить приложению \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" восстановить этот объект из корзины?}one{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> объект из корзины?}few{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> объекта из корзины?}many{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> объектов из корзины?}other{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" восстановить <xliff:g id="COUNT">^2</xliff:g> объекта из корзины?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Восстановление объекта из корзины…}one{Восстановление <xliff:g id="COUNT">^1</xliff:g> объекта из корзины…}few{Восстановление <xliff:g id="COUNT">^1</xliff:g> объектов из корзины…}many{Восстановление <xliff:g id="COUNT">^1</xliff:g> объектов из корзины…}other{Восстановление <xliff:g id="COUNT">^1</xliff:g> объекта из корзины…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Разрешить приложению \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" удалить этот аудиофайл?}one{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> аудиофайл?}few{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> аудиофайла?}many{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> аудиофайлов?}other{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> аудиофайла?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Удаление аудиофайла…}one{Удаление <xliff:g id="COUNT">^1</xliff:g> аудиофайла…}few{Удаление <xliff:g id="COUNT">^1</xliff:g> аудиофайлов…}many{Удаление <xliff:g id="COUNT">^1</xliff:g> аудиофайлов…}other{Удаление <xliff:g id="COUNT">^1</xliff:g> аудиофайла…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Разрешить приложению \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" удалить это видео?}one{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> видео?}few{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> видео?}many{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> видео?}other{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> видео?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Удаление видео…}one{Удаление <xliff:g id="COUNT">^1</xliff:g> видео…}few{Удаление <xliff:g id="COUNT">^1</xliff:g> видео…}many{Удаление <xliff:g id="COUNT">^1</xliff:g> видео…}other{Удаление <xliff:g id="COUNT">^1</xliff:g> видео…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Разрешить приложению \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" удалить это фото?}one{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> фото?}few{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> фото?}many{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> фото?}other{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> фото?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Удаление фото…}one{Удаление <xliff:g id="COUNT">^1</xliff:g> фото…}few{Удаление <xliff:g id="COUNT">^1</xliff:g> фото…}many{Удаление <xliff:g id="COUNT">^1</xliff:g> фото…}other{Удаление <xliff:g id="COUNT">^1</xliff:g> фото…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Разрешить приложению \"<xliff:g id="APP_NAME_0">^1</xliff:g>\" удалить этот объект?}one{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> объект?}few{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> объекта?}many{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> объектов?}other{Разрешить приложению \"<xliff:g id="APP_NAME_1">^1</xliff:g>\" удалить <xliff:g id="COUNT">^2</xliff:g> объекта?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Удаление объекта…}one{Удаление <xliff:g id="COUNT">^1</xliff:g> объекта…}few{Удаление <xliff:g id="COUNT">^1</xliff:g> объектов…}many{Удаление <xliff:g id="COUNT">^1</xliff:g> объектов…}other{Удаление <xliff:g id="COUNT">^1</xliff:g> объекта…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"Приложение \"<xliff:g id="APP_NAME">%s</xliff:g>\" не может обрабатывать медиафайлы."</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Обработка медиафайла отменена."</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Произошла ошибка при обработке медиафайла."</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index 4b0b9bb..351e4b3 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"මාධ්ය"</string>
<string name="storage_description" msgid="4081716890357580107">"පෙදෙසි ආචයනය"</string>
<string name="app_label" msgid="9035307001052716210">"මාධ්ය ගබඩාව"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"ඡායාරූප තෝරකය"</string>
<string name="artist_label" msgid="8105600993099120273">"කලාකරු"</string>
<string name="unknown" msgid="2059049215682829375">"නොදනී"</string>
<string name="root_images" msgid="5861633549189045666">"රූප"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"ඉදිරියට යන්න"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"ඉඩ දෙන්න"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"ප්රතික්ෂේප කරන්න"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">සහ අමතර අයිතම <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">සහ අමතර අයිතම <xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}one{+<xliff:g id="COUNT_1">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{තවද අතිරේක අයිතම <xliff:g id="COUNT_0">^1</xliff:g>}one{තවද අතිරේක අයිතම <xliff:g id="COUNT_1">^1</xliff:g>}other{තවද අතිරේක අයිතම <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"තාවකාලික යෙදුම් ගොනු හිස් කරන්න"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> සමහර තාවකාලික ගොනු හිස් කිරීමට කැමතියි. මෙය බැටරි බලයේ හෝ සෙලියුලර් දත්තවල වැඩි භාවිතයකට හේතු විය හැකිය."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"තාවකාලික යෙදුම් ගොනු හිස් කරමින්…"</string>
<string name="clear" msgid="5524638938415865915">"හිස් කරන්න"</string>
<string name="allow" msgid="8885707816848569619">"ඉඩ දෙන්න"</string>
<string name="deny" msgid="6040983710442068936">"ප්රතික්ෂේප කරන්න"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ට ඕඩියෝ ගොනු <xliff:g id="COUNT">^2</xliff:g>ක් වෙනස් කිරීමට ඉඩ දෙන්නද?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ට ඕඩියෝ ගොනු <xliff:g id="COUNT">^2</xliff:g>ක් වෙනස් කිරීමට ඉඩ දෙන්නද?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one">ශ්රව්ය ගොනු <xliff:g id="COUNT">^1</xliff:g>ක් වෙනස් කරමින්…</item>
- <item quantity="other">ශ්රව්ය ගොනු <xliff:g id="COUNT">^1</xliff:g>ක් වෙනස් කරමින්…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ට වීඩියෝ <xliff:g id="COUNT">^2</xliff:g>ක් වෙනස් කිරීමට ඉඩ දෙන්නද?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ට වීඩියෝ <xliff:g id="COUNT">^2</xliff:g>ක් වෙනස් කිරීමට ඉඩ දෙන්නද?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one">වීඩියෝ <xliff:g id="COUNT">^1</xliff:g>ක් වෙනස් කරමින්…</item>
- <item quantity="other">වීඩියෝ <xliff:g id="COUNT">^1</xliff:g>ක් වෙනස් කරමින්…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ට ඡායාරූප <xliff:g id="COUNT">^2</xliff:g>ක් වෙනස් කිරීමට ඉඩ දෙන්නද?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ට ඡායාරූප <xliff:g id="COUNT">^2</xliff:g>ක් වෙනස් කිරීමට ඉඩ දෙන්නද?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one">ඡායාරූප <xliff:g id="COUNT">^1</xliff:g>ක් වෙනස් කරමින්…</item>
- <item quantity="other">ඡායාරූප <xliff:g id="COUNT">^1</xliff:g>ක් වෙනස් කරමින්…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ට අයිතම <xliff:g id="COUNT">^2</xliff:g>ක් වෙනස් කිරීමට ඉඩ දෙන්නද?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ට අයිතම <xliff:g id="COUNT">^2</xliff:g>ක් වෙනස් කිරීමට ඉඩ දෙන්නද?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one">අයිතම <xliff:g id="COUNT">^1</xliff:g>ක් වෙනස් කරමින්…</item>
- <item quantity="other">අයිතම <xliff:g id="COUNT">^1</xliff:g>ක් වෙනස් කරමින්…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ට ඕඩියෝ ගොනු <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩය වෙත ගෙන යාමට ඉඩ දෙන්නද?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ට ඕඩියෝ ගොනු <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩය වෙත ගෙන යාමට ඉඩ දෙන්නද?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one">ශ්රව්ය ගොනු <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කූඩය වෙත ගෙන යමින්…</item>
- <item quantity="other">ශ්රව්ය ගොනු <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කූඩය වෙත ගෙන යමින්…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ට වීඩියෝ <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩය වෙත ගෙන යාමට ඉඩ දෙන්නද?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ට වීඩියෝ <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩය වෙත ගෙන යාමට ඉඩ දෙන්නද?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one">වීඩියෝ <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කුඩය වෙත ගෙන යමින්…</item>
- <item quantity="other">වීඩියෝ <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කුඩය වෙත ගෙන යමින්…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ට ඡායාරූප <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩය වෙත ගෙන යාමට ඉඩ දෙන්නද?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ට ඡායාරූප <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩය වෙත ගෙන යාමට ඉඩ දෙන්නද?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one">ඡායාරූප <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කුඩය වෙත ගෙන යමින්…</item>
- <item quantity="other">ඡායාරූප <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කුඩය වෙත ගෙන යමින්…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ට අයිතම <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩය වෙත ගෙන යාමට ඉඩ දෙන්නද?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ට අයිතම <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩය වෙත ගෙන යාමට ඉඩ දෙන්නද?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one">අයිතම <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කුඩය වෙත ගෙන යමින්…</item>
- <item quantity="other">අයිතම <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කුඩය වෙත ගෙන යමින්…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ට ඕඩියෝ ගොනු <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩයෙන් පිටතට ගෙන යාමට ඉඩ දෙන්නද?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ට ඕඩියෝ ගොනු <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩයෙන් පිටතට ගෙන යාමට ඉඩ දෙන්නද?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one">ශ්රව්ය ගොනු <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කූඩයෙන් ඉවතට ගෙන යමින්…</item>
- <item quantity="other">ශ්රව්ය ගොනු <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කූඩයෙන් ඉවතට ගෙන යමින්…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ට වීඩියෝ <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩයෙන් පිටතට ගෙන යාමට ඉඩ දෙන්නද?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ට වීඩියෝ <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩයෙන් පිටතට ගෙන යාමට ඉඩ දෙන්නද?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one">වීඩියෝ <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කූඩයෙන් ඉවතට ගෙන යමින්…</item>
- <item quantity="other">වීඩියෝ <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කූඩයෙන් ඉවතට ගෙන යමින්…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ට ඡායාරූප <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩයෙන් පිටතට ගෙන යාමට ඉඩ දෙන්නද?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ට ඡායාරූප <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩයෙන් පිටතට ගෙන යාමට ඉඩ දෙන්නද?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one">ඡායාරූප <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කූඩයෙන් ඉවතට ගෙන යමින්…</item>
- <item quantity="other">ඡායාරූප <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කූඩයෙන් ඉවතට ගෙන යමින්…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ට අයිතම <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩයෙන් පිටතට ගෙන යාමට ඉඩ දෙන්නද?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ට අයිතම <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩයෙන් පිටතට ගෙන යාමට ඉඩ දෙන්නද?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one">අයිතම <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කූඩයෙන් ඉවතට ගෙන යමින්…</item>
- <item quantity="other">අයිතම <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කූඩයෙන් ඉවතට ගෙන යමින්…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ට ඕඩියෝ ගොනු <xliff:g id="COUNT">^2</xliff:g>ක් මැකීමට ඉඩ දෙන්නද?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ට ඕඩියෝ ගොනු <xliff:g id="COUNT">^2</xliff:g>ක් මැකීමට ඉඩ දෙන්නද?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one">ශ්රව්ය ගොනු <xliff:g id="COUNT">^1</xliff:g>ක් මකමින්…</item>
- <item quantity="other">ශ්රව්ය ගොනු <xliff:g id="COUNT">^1</xliff:g>ක් මකමින්…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ට වීඩියෝ <xliff:g id="COUNT">^2</xliff:g>ක් මැකීමට ඉඩ දෙන්නද?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ට වීඩියෝ <xliff:g id="COUNT">^2</xliff:g>ක් මැකීමට ඉඩ දෙන්නද?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one">වීඩියෝ <xliff:g id="COUNT">^1</xliff:g>ක් මකමින්…</item>
- <item quantity="other">වීඩියෝ <xliff:g id="COUNT">^1</xliff:g>ක් මකමින්…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ට ඡායාරූප <xliff:g id="COUNT">^2</xliff:g>ක් මැකීමට ඉඩ දෙන්නද?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ට ඡායාරූප <xliff:g id="COUNT">^2</xliff:g>ක් මැකීමට ඉඩ දෙන්නද?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one">ඡායාරූප <xliff:g id="COUNT">^1</xliff:g>ක් මකමින්…</item>
- <item quantity="other">ඡායාරූප <xliff:g id="COUNT">^1</xliff:g>ක් මකමින්…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ට අයිතම <xliff:g id="COUNT">^2</xliff:g>ක් මැකීමට ඉඩ දෙන්නද?</item>
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>ට අයිතම <xliff:g id="COUNT">^2</xliff:g>ක් මැකීමට ඉඩ දෙන්නද?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one">අයිතම <xliff:g id="COUNT">^1</xliff:g>ක් මකමින්…</item>
- <item quantity="other">අයිතම <xliff:g id="COUNT">^1</xliff:g>ක් මකමින්…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"එක් කරන්න"</string>
+ <string name="deselect" msgid="4297825044827769490">"නොතෝරන්න"</string>
+ <string name="deselected" msgid="8488133193326208475">"නොතෝරන ලද"</string>
+ <string name="select" msgid="2704765470563027689">"තෝරන්න"</string>
+ <string name="selected" msgid="9151797369975828124">"තෝරන ලද"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{අයිතම <xliff:g id="COUNT_0">^1</xliff:g>ක් දක්වා තෝරන්න}one{අයිතම <xliff:g id="COUNT_1">^1</xliff:g>ක් දක්වා තෝරන්න}other{අයිතම <xliff:g id="COUNT_1">^1</xliff:g>ක් දක්වා තෝරන්න}}"</string>
+ <string name="recent" msgid="6694613584743207874">"මෑත"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"ඡායාරූප හෝ වීඩියෝ නැත"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"ඇල්බම නැත"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"තෝරා ගත් දේවල් බලන්න"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"ඡායාරූප"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"ඇල්බම"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"පෙරදසුන"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"කාර්යාලය වෙත මාරු වන්න"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"පුද්ගලික වෙත මාරු වන්න"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"ඔබගේ පරිපාලක විසින් අවහිර කර ඇත"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"පුද්ගලික යෙදුමකින් කාර්යාල දත්තවලට ප්රවේශ වීමට අවසර නොදේ"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"කාර්යාල යෙදුමකින් පුද්ගලික දත්තවලට ප්රවේශ වීමට අවසර නොදේ"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"කාර්යාල යෙදුම් විරාම කර ඇත"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"කාර්යාල ඡායාරූප විවෘත කිරීමට, ඔබගේ කාර්යාල යෙදුම් ක්රියාත්මක කර නැවත උත්සාහ කරන්න"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"මෙම යෙදුමට ප්රවේශ විය හැක්කේ ඔබ තෝරන ඡායාරූපවලට පමණි"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{අයිතම <xliff:g id="COUNT_0">^1</xliff:g>}one{අයිතම <xliff:g id="COUNT_1">^1</xliff:g>}other{අයිතම <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"එක් කරන්න (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"කැමරාව"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"බාගැනීම්"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"ප්රියතමයන්"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"තිර රූ"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"චලන ඡායාරූපය"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="TIME">%2$s</xliff:g>ට ගන්නා ලද <xliff:g id="ITEM_NAME">%1$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"<xliff:g id="TIME">%1$s</xliff:g>ට <xliff:g id="DURATION">%2$s</xliff:g> කාල සීමාව සහිතව ලබා ගත් වීඩියෝව"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"ඡායාරූපය"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"චලන ඡායාරූපය"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"වීඩියෝව නිහඬ කරන්න"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"වීඩියෝව නිහඬ කිරීම ඉවත් කරන්න"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"වීඩියෝව ධාවනය කරන්න"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"විඩියෝව විරාම කරන්න"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"ක්ලවුඩ් මාධ්ය දැන් <xliff:g id="PKG_NAME">%1$s</xliff:g> වෙතින් ලබා ගත හැකිය"</string>
+ <string name="not_selected" msgid="2244008151669896758">"තෝරා නොමැත"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> හට මෙම ශ්රව්ය ගොනුව වෙනස් කිරීමට ඉඩ දෙන්නද?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> හට ශ්රව්ය ගොනු <xliff:g id="COUNT">^2</xliff:g>ක් වෙනස් කිරීමට ඉඩ දෙන්නද?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> හට ශ්රව්ය ගොනු <xliff:g id="COUNT">^2</xliff:g>ක් වෙනස් කිරීමට ඉඩ දෙන්නද?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{ශ්රව්ය ගොනුව වෙනස් කරමින්…}one{ශ්රව්ය ගොනු <xliff:g id="COUNT">^1</xliff:g>ක් වෙනස් කරමින්…}other{ශ්රව්ය ගොනු <xliff:g id="COUNT">^1</xliff:g>ක් වෙනස් කරමින්…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> හට මෙම වීඩියෝව වෙනස් කිරීමට ඉඩ දෙන්නද?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> හට වීඩියෝ <xliff:g id="COUNT">^2</xliff:g>ක් වෙනස් කිරීමට ඉඩ දෙන්නද?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> හට වීඩියෝ <xliff:g id="COUNT">^2</xliff:g>ක් වෙනස් කිරීමට ඉඩ දෙන්නද?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{වීඩියෝව වෙනස් කරමින්…}one{වීඩියෝ <xliff:g id="COUNT">^1</xliff:g>ක් වෙනස් කරමින්…}other{වීඩියෝ <xliff:g id="COUNT">^1</xliff:g>ක් වෙනස් කරමින්…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> හට මෙම ඡායාරූපය වෙනස් කිරීමට ඉඩ දෙන්නද?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> හට ඡායාරූප <xliff:g id="COUNT">^2</xliff:g>ක් වෙනස් කිරීමට ඉඩ දෙන්නද?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> හට ඡායාරූප <xliff:g id="COUNT">^2</xliff:g>ක් වෙනස් කිරීමට ඉඩ දෙන්නද?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{ඡායාරූපය වෙනස් කරමින්…}one{ඡායාරූප <xliff:g id="COUNT">^1</xliff:g>ක් වෙනස් කරමින්…}other{ඡායාරූප <xliff:g id="COUNT">^1</xliff:g>ක් වෙනස් කරමින්…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> හට මෙම අයිතමය වෙනස් කිරීමට ඉඩ දෙන්නද?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> හට අයිතම <xliff:g id="COUNT">^2</xliff:g>ක් වෙනස් කිරීමට ඉඩ දෙන්නද?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> හට අයිතම <xliff:g id="COUNT">^2</xliff:g>ක් වෙනස් කිරීමට ඉඩ දෙන්නද?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{අයිතමය වෙනස් කරමින්…}one{අයිතම <xliff:g id="COUNT">^1</xliff:g>ක් වෙනස් කරමින්…}other{අයිතම <xliff:g id="COUNT">^1</xliff:g>ක් වෙනස් කරමින්…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> හට මෙම ශ්රව්ය ගොනුව කුණු කුඩය වෙත ගෙන යාමට ඉඩ දෙන්නද?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> හට ශ්රව්ය ගොනු <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩය වෙත ගෙන යාමට ඉඩ දෙන්නද?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> හට ශ්රව්ය ගොනු <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩය වෙත ගෙන යාමට ඉඩ දෙන්නද?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{ශ්රව්ය ගොනුව කුණු කූඩය වෙත ගෙන යමින්…}one{ශ්රව්ය ගොනු <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කූඩය වෙත ගෙන යමින්…}other{ශ්රව්ය ගොනු <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කූඩය වෙත ගෙන යමින්…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> හට මෙම වීඩියෝව කුණු කුඩය වෙත ගෙන යාමට ඉඩ දෙන්නද?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> හට වීඩියෝ <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩය වෙත ගෙන යාමට ඉඩ දෙන්නද?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> හට වීඩියෝ <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩය වෙත ගෙන යාමට ඉඩ දෙන්නද?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{වීඩියෝව කුණු කුඩය වෙත ගෙන යමින්…}one{වීඩියෝ <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කුඩය වෙත ගෙන යමින්…}other{වීඩියෝ <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කුඩය වෙත ගෙන යමින්…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> හට මෙම ඡායාරූපය කුණු කුඩය වෙත ගෙන යාමට ඉඩ දෙන්නද?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> හට ඡායාරූප <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩය වෙත ගෙන යාමට ඉඩ දෙන්නද?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> හට ඡායාරූප <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩය වෙත ගෙන යාමට ඉඩ දෙන්නද?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{ඡායාරූපය කුණු කුඩය වෙත ගෙන යමින්…}one{ඡායාරූප <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කුඩය වෙත ගෙන යමින්…}other{ඡායාරූප <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කුඩය වෙත ගෙන යමින්…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> හට මෙම අයිතමය කුණු කුඩය වෙත ගෙන යාමට ඉඩ දෙන්නද?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> හට අයිතම <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩය වෙත ගෙන යාමට ඉඩ දෙන්නද?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> හට අයිතම <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩය වෙත ගෙන යාමට ඉඩ දෙන්නද?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{අයිතමය කුණු කුඩය වෙත ගෙන යමින්…}one{අයිතම <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කුඩය වෙත ගෙන යමින්…}other{අයිතම <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කුඩය වෙත ගෙන යමින්…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> හට මෙම ශ්රව්ය ගොනුව කුණු කුඩයෙන් පිටතට ගෙන යාමට ඉඩ දෙන්නද?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> හට ශ්රව්ය ගොනු <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩයෙන් පිටතට ගෙන යාමට ඉඩ දෙන්නද?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> හට ශ්රව්ය ගොනු <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩයෙන් පිටතට ගෙන යාමට ඉඩ දෙන්නද?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{ශ්රව්ය ගොනුව කුණු කූඩයෙන් ඉවතට ගෙන යමින්…}one{ශ්රව්ය ගොනු <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කූඩයෙන් ඉවතට ගෙන යමින්…}other{ශ්රව්ය ගොනු <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කූඩයෙන් ඉවතට ගෙන යමින්…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> හට මෙම වීඩියෝව කුණු කුඩයෙන් පිටතට ගෙන යාමට ඉඩ දෙන්නද?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> හට වීඩියෝ <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩයෙන් පිටතට ගෙන යාමට ඉඩ දෙන්නද?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> හට වීඩියෝ <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩයෙන් පිටතට ගෙන යාමට ඉඩ දෙන්නද?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{වීඩියෝව කුණු කූඩයෙන් ඉවතට ගෙන යමින්…}one{වීඩියෝ <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කූඩයෙන් ඉවතට ගෙන යමින්…}other{වීඩියෝ <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කූඩයෙන් ඉවතට ගෙන යමින්…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> හට මෙම ඡායාරූපය කුණු කුඩයෙන් පිටතට ගෙන යාමට ඉඩ දෙන්නද?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> හට ඡායාරූප <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩයෙන් පිටතට ගෙන යාමට ඉඩ දෙන්නද?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> හට ඡායාරූප <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩයෙන් පිටතට ගෙන යාමට ඉඩ දෙන්නද?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{ඡායාරූපය කුණු කූඩයෙන් ඉවතට ගෙන යමින්…}one{ඡායාරූප <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කූඩයෙන් ඉවතට ගෙන යමින්…}other{ඡායාරූප <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කූඩයෙන් ඉවතට ගෙන යමින්…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> හට මෙම අයිතමය කුණු කුඩයෙන් පිටතට ගෙන යාමට ඉඩ දෙන්නද?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> හට අයිතම <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩයෙන් පිටතට ගෙන යාමට ඉඩ දෙන්නද?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> හට අයිතම <xliff:g id="COUNT">^2</xliff:g>ක් කුණු කුඩයෙන් පිටතට ගෙන යාමට ඉඩ දෙන්නද?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{අයිතමය කුණු කූඩයෙන් ඉවතට ගෙන යමින්…}one{අයිතම <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කූඩයෙන් ඉවතට ගෙන යමින්…}other{අයිතම <xliff:g id="COUNT">^1</xliff:g>ක් කුණු කූඩයෙන් ඉවතට ගෙන යමින්…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> හට මෙම ශ්රව්ය ගොනුව මැකීමට ඉඩ දෙන්නද?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> හට ශ්රව්ය ගොනු <xliff:g id="COUNT">^2</xliff:g>ක් මැකීමට ඉඩ දෙන්නද?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> හට ශ්රව්ය ගොනු <xliff:g id="COUNT">^2</xliff:g>ක් මැකීමට ඉඩ දෙන්නද?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{ශ්රව්ය ගොනුව මකමින්…}one{ශ්රව්ය ගොනු <xliff:g id="COUNT">^1</xliff:g>ක් මකමින්…}other{ශ්රව්ය ගොනු <xliff:g id="COUNT">^1</xliff:g>ක් මකමින්…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> හට මෙම වීඩියෝව මැකීමට ඉඩ දෙන්නද?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> හට වීඩියෝ <xliff:g id="COUNT">^2</xliff:g>ක් මැකීමට ඉඩ දෙන්නද?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> හට වීඩියෝ <xliff:g id="COUNT">^2</xliff:g>ක් මැකීමට ඉඩ දෙන්නද?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{වීඩියෝව මකමින්…}one{වීඩියෝ <xliff:g id="COUNT">^1</xliff:g>ක් මකමින්…}other{වීඩියෝ <xliff:g id="COUNT">^1</xliff:g>ක් මකමින්…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> හට මෙම ඡායාරූපය මැකීමට ඉඩ දෙන්නද?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> හට ඡායාරූප <xliff:g id="COUNT">^2</xliff:g>ක් මැකීමට ඉඩ දෙන්නද?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> හට ඡායාරූප <xliff:g id="COUNT">^2</xliff:g>ක් මැකීමට ඉඩ දෙන්නද?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{ඡායාරූපය මකමින්…}one{ඡායාරූප <xliff:g id="COUNT">^1</xliff:g>ක් මකමින්…}other{ඡායාරූප <xliff:g id="COUNT">^1</xliff:g>ක් මකමින්…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> හට මෙම අයිතමය මැකීමට ඉඩ දෙන්නද?}one{<xliff:g id="APP_NAME_1">^1</xliff:g> හට අයිතම <xliff:g id="COUNT">^2</xliff:g>ක් මැකීමට ඉඩ දෙන්නද?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> හට අයිතම <xliff:g id="COUNT">^2</xliff:g>ක් මැකීමට ඉඩ දෙන්නද?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{අයිතමය මකමින්…}one{අයිතම <xliff:g id="COUNT">^1</xliff:g>ක් මකමින්…}other{අයිතම <xliff:g id="COUNT">^1</xliff:g>ක් මකමින්…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> හට මාධ්ය ගොනු සැකසිය නොහැකිය"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"මාධ්ය සැකසීම අවලංගු කරන ලදී"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"මාධ්ය සැකසීමේ දෝෂය"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 1950e7e..938c805 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Médiá"</string>
<string name="storage_description" msgid="4081716890357580107">"Miestne úložisko"</string>
<string name="app_label" msgid="9035307001052716210">"Úložisko médií"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"Výber fotiek"</string>
<string name="artist_label" msgid="8105600993099120273">"Interpret"</string>
<string name="unknown" msgid="2059049215682829375">"Neznáme"</string>
<string name="root_images" msgid="5861633549189045666">"Obrázky"</string>
@@ -29,216 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Pokračovať"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Povoliť"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Zamietnuť"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="few">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="many">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="few">A <xliff:g id="COUNT_1">^1</xliff:g> ďalšie položky</item>
- <item quantity="many">A <xliff:g id="COUNT_1">^1</xliff:g> ďalšej položky</item>
- <item quantity="other">A <xliff:g id="COUNT_1">^1</xliff:g> ďalších položiek</item>
- <item quantity="one">A <xliff:g id="COUNT_0">^1</xliff:g> ďalšia položka</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}few{+<xliff:g id="COUNT_1">^1</xliff:g>}many{+<xliff:g id="COUNT_1">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{A <xliff:g id="COUNT_0">^1</xliff:g> ďalšia položka}few{A <xliff:g id="COUNT_1">^1</xliff:g> ďalšie položky}many{Plus <xliff:g id="COUNT_1">^1</xliff:g> additional items}other{A <xliff:g id="COUNT_1">^1</xliff:g> ďalších položiek}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Vymazanie dočasných súborov aplikácií"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"Aplikácia <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> chce vymazať niekoľko dočasných súborov. To môže viesť k vyššiemu využívaniu batérie alebo mobilných dát."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Odstraňujú sa dočasné súbory aplikácie…"</string>
<string name="clear" msgid="5524638938415865915">"Vymazať"</string>
<string name="allow" msgid="8885707816848569619">"Povoliť"</string>
<string name="deny" msgid="6040983710442068936">"Zamietnuť"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="few">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> upraviť <xliff:g id="COUNT">^2</xliff:g> zvukové súbory?</item>
- <item quantity="many">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> audio files?</item>
- <item quantity="other">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> upraviť <xliff:g id="COUNT">^2</xliff:g> zvukových súborov?</item>
- <item quantity="one">Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> upraviť tento zvukový súbor?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="few">Upravujú sa <xliff:g id="COUNT">^1</xliff:g> zvukové súbory…</item>
- <item quantity="many">Modifying <xliff:g id="COUNT">^1</xliff:g> audio files…</item>
- <item quantity="other">Upravuje sa <xliff:g id="COUNT">^1</xliff:g> zvukových súborov…</item>
- <item quantity="one">Upravuje sa zvukový súbor…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="few">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> upraviť <xliff:g id="COUNT">^2</xliff:g> videá?</item>
- <item quantity="many">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> videos?</item>
- <item quantity="other">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> upraviť <xliff:g id="COUNT">^2</xliff:g> videí?</item>
- <item quantity="one">Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> upraviť toto video?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="few">Upravujú sa <xliff:g id="COUNT">^1</xliff:g> videá…</item>
- <item quantity="many">Modifying <xliff:g id="COUNT">^1</xliff:g> videos…</item>
- <item quantity="other">Upravuje sa <xliff:g id="COUNT">^1</xliff:g> videí…</item>
- <item quantity="one">Upravuje sa video…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="few">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> upraviť <xliff:g id="COUNT">^2</xliff:g> fotky?</item>
- <item quantity="many">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> photos?</item>
- <item quantity="other">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> upraviť <xliff:g id="COUNT">^2</xliff:g> fotiek?</item>
- <item quantity="one">Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> upraviť túto fotku?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="few">Upravujú sa <xliff:g id="COUNT">^1</xliff:g> fotky…</item>
- <item quantity="many">Modifying <xliff:g id="COUNT">^1</xliff:g> photos…</item>
- <item quantity="other">Upravuje sa <xliff:g id="COUNT">^1</xliff:g> fotiek…</item>
- <item quantity="one">Upravuje sa fotka…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="few">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> upraviť <xliff:g id="COUNT">^2</xliff:g> položky?</item>
- <item quantity="many">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> items?</item>
- <item quantity="other">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> upraviť <xliff:g id="COUNT">^2</xliff:g> položiek?</item>
- <item quantity="one">Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> upraviť túto položku?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="few">Upravujú sa <xliff:g id="COUNT">^1</xliff:g> položky…</item>
- <item quantity="many">Modifying <xliff:g id="COUNT">^1</xliff:g> items…</item>
- <item quantity="other">Upravuje sa <xliff:g id="COUNT">^1</xliff:g> položiek…</item>
- <item quantity="one">Upravuje sa položka…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="few">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> zvukové súbory do koša?</item>
- <item quantity="many">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files to trash?</item>
- <item quantity="other">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> zvukových súborov do koša?</item>
- <item quantity="one">Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> presunúť tento zvukový súbor do koša?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> zvukové súbory sa presúvajú do koša…</item>
- <item quantity="many">Moving <xliff:g id="COUNT">^1</xliff:g> audio files to trash…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> zvukových súborov sa presúva do koša…</item>
- <item quantity="one">Zvukový súbor sa presúva do koša…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="few">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> videá do koša?</item>
- <item quantity="many">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos to trash?</item>
- <item quantity="other">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> videí do koša?</item>
- <item quantity="one">Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> presunúť toto video do koša?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> videá sa presúvajú do koša…</item>
- <item quantity="many">Moving <xliff:g id="COUNT">^1</xliff:g> videos to trash…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> videí sa presúva do koša…</item>
- <item quantity="one">Video sa presúva do koša…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="few">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> fotky do koša?</item>
- <item quantity="many">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos to trash?</item>
- <item quantity="other">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> fotiek do koša?</item>
- <item quantity="one">Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> presunúť túto fotku do koša?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> fotky sa presúvajú do koša…</item>
- <item quantity="many">Moving <xliff:g id="COUNT">^1</xliff:g> photos to trash…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> fotiek sa presúva do koša…</item>
- <item quantity="one">Fotka sa presúva do koša…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="few">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> položky do koša?</item>
- <item quantity="many">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items to trash?</item>
- <item quantity="other">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> položiek do koša?</item>
- <item quantity="one">Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> presunúť túto položku do koša?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> položky sa presúvajú do koša…</item>
- <item quantity="many">Moving <xliff:g id="COUNT">^1</xliff:g> items to trash…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> položiek sa presúva do koša…</item>
- <item quantity="one">Položka sa presúva do koša…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="few">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> zvukové súbory z koša?</item>
- <item quantity="many">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files out of trash?</item>
- <item quantity="other">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> zvukových súborov z koša?</item>
- <item quantity="one">Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> presunúť tento zvukový súbor z koša?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> zvukové súbory sa presúvajú z koša…</item>
- <item quantity="many">Moving <xliff:g id="COUNT">^1</xliff:g> audio files out of trash…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> zvukových súborov sa presúva z koša…</item>
- <item quantity="one">Zvukový súbor sa presúva z koša…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="few">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> videá z koša?</item>
- <item quantity="many">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos out of trash?</item>
- <item quantity="other">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> videí z koša?</item>
- <item quantity="one">Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> presunúť toto video z koša?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> videá sa presúvajú z koša…</item>
- <item quantity="many">Moving <xliff:g id="COUNT">^1</xliff:g> videos out of trash…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> videí sa presúva z koša…</item>
- <item quantity="one">Video sa presúva z koša…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="few">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> fotky z koša?</item>
- <item quantity="many">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos out of trash?</item>
- <item quantity="other">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> fotiek z koša?</item>
- <item quantity="one">Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> presunúť túto fotku z koša?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> fotky sa presúvajú z koša…</item>
- <item quantity="many">Moving <xliff:g id="COUNT">^1</xliff:g> photos out of trash…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> fotiek sa presúva z koša…</item>
- <item quantity="one">Fotka sa presúva z koša…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="few">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> položky z koša?</item>
- <item quantity="many">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items out of trash?</item>
- <item quantity="other">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> položiek z koša?</item>
- <item quantity="one">Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> presunúť túto položku z koša?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> položky sa presúvajú z koša…</item>
- <item quantity="many">Moving <xliff:g id="COUNT">^1</xliff:g> items out of trash…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> položiek sa presúva z koša…</item>
- <item quantity="one">Položka sa presúva z koša…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="few">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> odstrániť <xliff:g id="COUNT">^2</xliff:g> zvukové súbory?</item>
- <item quantity="many">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> audio files?</item>
- <item quantity="other">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> odstrániť <xliff:g id="COUNT">^2</xliff:g> zvukových súborov?</item>
- <item quantity="one">Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> odstrániť tento zvukový súbor?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="few">Odstraňujú sa <xliff:g id="COUNT">^1</xliff:g> zvukové súbory…</item>
- <item quantity="many">Deleting <xliff:g id="COUNT">^1</xliff:g> audio files…</item>
- <item quantity="other">Odstraňuje sa <xliff:g id="COUNT">^1</xliff:g> zvukových súborov…</item>
- <item quantity="one">Odstraňuje sa zvukový súbor…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="few">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> odstrániť <xliff:g id="COUNT">^2</xliff:g> videá?</item>
- <item quantity="many">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> videos?</item>
- <item quantity="other">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> odstrániť <xliff:g id="COUNT">^2</xliff:g> videí?</item>
- <item quantity="one">Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> odstrániť toto video?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="few">Odstraňujú sa <xliff:g id="COUNT">^1</xliff:g> videá…</item>
- <item quantity="many">Deleting <xliff:g id="COUNT">^1</xliff:g> videos…</item>
- <item quantity="other">Odstraňuje sa <xliff:g id="COUNT">^1</xliff:g> videí…</item>
- <item quantity="one">Odstraňuje sa video…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="few">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> odstrániť <xliff:g id="COUNT">^2</xliff:g> fotky?</item>
- <item quantity="many">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> photos?</item>
- <item quantity="other">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> odstrániť <xliff:g id="COUNT">^2</xliff:g> fotiek?</item>
- <item quantity="one">Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> odstrániť túto fotku?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="few">Odstraňujú sa <xliff:g id="COUNT">^1</xliff:g> fotky…</item>
- <item quantity="many">Deleting <xliff:g id="COUNT">^1</xliff:g> photos…</item>
- <item quantity="other">Odstraňuje sa <xliff:g id="COUNT">^1</xliff:g> fotiek…</item>
- <item quantity="one">Odstraňuje sa fotka…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="few">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> odstrániť <xliff:g id="COUNT">^2</xliff:g> položky?</item>
- <item quantity="many">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> items?</item>
- <item quantity="other">Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> odstrániť <xliff:g id="COUNT">^2</xliff:g> položiek?</item>
- <item quantity="one">Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> odstrániť túto položku?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="few">Odstraňujú sa <xliff:g id="COUNT">^1</xliff:g> položky…</item>
- <item quantity="many">Deleting <xliff:g id="COUNT">^1</xliff:g> items…</item>
- <item quantity="other">Odstraňuje sa <xliff:g id="COUNT">^1</xliff:g> položiek…</item>
- <item quantity="one">Odstraňuje sa položka…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Pridať"</string>
+ <string name="deselect" msgid="4297825044827769490">"Zrušiť výber"</string>
+ <string name="deselected" msgid="8488133193326208475">"Výber bol zrušený"</string>
+ <string name="select" msgid="2704765470563027689">"Vybrať"</string>
+ <string name="selected" msgid="9151797369975828124">"Vybrané"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Vyberte maximálne <xliff:g id="COUNT_0">^1</xliff:g> položku}few{Vyberte maximálne <xliff:g id="COUNT_1">^1</xliff:g> položky}many{Select up to <xliff:g id="COUNT_1">^1</xliff:g> items}other{Vyberte maximálne <xliff:g id="COUNT_1">^1</xliff:g> položiek}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Nedávne"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Žiadne fotky ani videá"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Žiadne albumy"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Zobraziť vybrané"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Fotky"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albumy"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Ukážka"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Prepnúť na pracovný"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Prepnúť na osobný"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Blokované vaším správcom"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Prístup k pracovným údajom z osobnej aplikácie nie je povolený"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Prístup k osobným údajom z pracovnej aplikácie nie je povolený"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Pracovné aplikácie sú pozastavené"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Ak chcete otvoriť pracovné fotky, zapnite pracovné aplikácie a skúste to znova"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Táto aplikácia môže mať prístup iba k fotkám, ktoré vyberiete"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> položka}few{<xliff:g id="COUNT_1">^1</xliff:g> položky}many{<xliff:g id="COUNT_1">^1</xliff:g> items}other{<xliff:g id="COUNT_1">^1</xliff:g> položiek}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Pridať (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Stiahnuté"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Obľúbené"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Snímky obrazovky"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Pohyblivá fotka"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> – nasnímané <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Video bolo zaznamenané <xliff:g id="TIME">%1$s</xliff:g> a trvá <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Fotka"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Pohyblivá fotka"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Vypnúť zvuk videa"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Zapnúť zvuk videa"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Prehrať video"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Pozastaviť video"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Cloudové médiá sú teraz k dispozícii z aplikácie <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"nevybrané"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> upraviť tento zvukový súbor?}few{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> upraviť <xliff:g id="COUNT">^2</xliff:g> zvukové súbory?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> audio files?}other{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> upraviť <xliff:g id="COUNT">^2</xliff:g> zvukových súborov?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Upravuje sa zvukový súbor…}few{Upravujú sa <xliff:g id="COUNT">^1</xliff:g> zvukové súbory…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> audio files…}other{Upravuje sa <xliff:g id="COUNT">^1</xliff:g> zvukových súborov…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> upraviť toto video?}few{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> upraviť <xliff:g id="COUNT">^2</xliff:g> videá?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> videos?}other{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> upraviť <xliff:g id="COUNT">^2</xliff:g> videí?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Upravuje sa video…}few{Upravujú sa <xliff:g id="COUNT">^1</xliff:g> videá…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> videos…}other{Upravuje sa <xliff:g id="COUNT">^1</xliff:g> videí…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> upraviť túto fotku?}few{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> upraviť <xliff:g id="COUNT">^2</xliff:g> fotky?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> photos?}other{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> upraviť <xliff:g id="COUNT">^2</xliff:g> fotiek?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Upravuje sa fotka…}few{Upravujú sa <xliff:g id="COUNT">^1</xliff:g> fotky…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> photos…}other{Upravuje sa <xliff:g id="COUNT">^1</xliff:g> fotiek…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> upraviť túto položku?}few{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> upraviť <xliff:g id="COUNT">^2</xliff:g> položky?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> items?}other{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> upraviť <xliff:g id="COUNT">^2</xliff:g> položiek?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Upravuje sa položka…}few{Upravujú sa <xliff:g id="COUNT">^1</xliff:g> položky…}many{Modifying <xliff:g id="COUNT">^1</xliff:g> items…}other{Upravuje sa <xliff:g id="COUNT">^1</xliff:g> položiek…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> presunúť tento zvukový súbor do koša?}few{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> zvukové súbory do koša?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files to trash?}other{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> zvukových súborov do koša?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Zvukový súbor sa presúva do koša…}few{<xliff:g id="COUNT">^1</xliff:g> zvukové súbory sa presúvajú do koša…}many{Moving <xliff:g id="COUNT">^1</xliff:g> audio files to trash…}other{<xliff:g id="COUNT">^1</xliff:g> zvukových súborov sa presúva do koša…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> presunúť toto video do koša?}few{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> videá do koša?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos to trash?}other{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> videí do koša?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Video sa presúva do koša…}few{<xliff:g id="COUNT">^1</xliff:g> videá sa presúvajú do koša…}many{Moving <xliff:g id="COUNT">^1</xliff:g> videos to trash…}other{<xliff:g id="COUNT">^1</xliff:g> videí sa presúva do koša…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> presunúť túto fotku do koša?}few{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> fotky do koša?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos to trash?}other{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> fotiek do koša?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Fotka sa presúva do koša…}few{<xliff:g id="COUNT">^1</xliff:g> fotky sa presúvajú do koša…}many{Moving <xliff:g id="COUNT">^1</xliff:g> photos to trash…}other{<xliff:g id="COUNT">^1</xliff:g> fotiek sa presúva do koša…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> presunúť túto položku do koša?}few{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> položky do koša?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items to trash?}other{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> položiek do koša?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Položka sa presúva do koša…}few{<xliff:g id="COUNT">^1</xliff:g> položky sa presúvajú do koša…}many{Moving <xliff:g id="COUNT">^1</xliff:g> items to trash…}other{<xliff:g id="COUNT">^1</xliff:g> položiek sa presúva do koša…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> presunúť tento zvukový súbor z koša?}few{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> zvukové súbory z koša?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> audio files out of trash?}other{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> zvukových súborov z koša?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Zvukový súbor sa presúva z koša…}few{<xliff:g id="COUNT">^1</xliff:g> zvukové súbory sa presúvajú z koša…}many{Moving <xliff:g id="COUNT">^1</xliff:g> audio files out of trash…}other{<xliff:g id="COUNT">^1</xliff:g> zvukových súborov sa presúva z koša…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> presunúť toto video z koša?}few{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> videá z koša?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> videos out of trash?}other{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> videí z koša?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Video sa presúva z koša…}few{<xliff:g id="COUNT">^1</xliff:g> videá sa presúvajú z koša…}many{Moving <xliff:g id="COUNT">^1</xliff:g> videos out of trash…}other{<xliff:g id="COUNT">^1</xliff:g> videí sa presúva z koša…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> presunúť túto fotku z koša?}few{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> fotky z koša?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> photos out of trash?}other{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> fotiek z koša?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Fotka sa presúva z koša…}few{<xliff:g id="COUNT">^1</xliff:g> fotky sa presúvajú z koša…}many{Moving <xliff:g id="COUNT">^1</xliff:g> photos out of trash…}other{<xliff:g id="COUNT">^1</xliff:g> fotiek sa presúva z koša…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> presunúť túto položku z koša?}few{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> položky z koša?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to move <xliff:g id="COUNT">^2</xliff:g> items out of trash?}other{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> presunúť <xliff:g id="COUNT">^2</xliff:g> položiek z koša?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Položka sa presúva z koša…}few{<xliff:g id="COUNT">^1</xliff:g> položky sa presúvajú z koša…}many{Moving <xliff:g id="COUNT">^1</xliff:g> items out of trash…}other{<xliff:g id="COUNT">^1</xliff:g> položiek sa presúva z koša…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> odstrániť tento zvukový súbor?}few{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> odstrániť <xliff:g id="COUNT">^2</xliff:g> zvukové súbory?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> audio files?}other{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> odstrániť <xliff:g id="COUNT">^2</xliff:g> zvukových súborov?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Odstraňuje sa zvukový súbor…}few{Odstraňujú sa <xliff:g id="COUNT">^1</xliff:g> zvukové súbory…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> audio files…}other{Odstraňuje sa <xliff:g id="COUNT">^1</xliff:g> zvukových súborov…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> odstrániť toto video?}few{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> odstrániť <xliff:g id="COUNT">^2</xliff:g> videá?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> videos?}other{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> odstrániť <xliff:g id="COUNT">^2</xliff:g> videí?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Odstraňuje sa video…}few{Odstraňujú sa <xliff:g id="COUNT">^1</xliff:g> videá…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> videos…}other{Odstraňuje sa <xliff:g id="COUNT">^1</xliff:g> videí…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> odstrániť túto fotku?}few{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> odstrániť <xliff:g id="COUNT">^2</xliff:g> fotky?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> photos?}other{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> odstrániť <xliff:g id="COUNT">^2</xliff:g> fotiek?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Odstraňuje sa fotka…}few{Odstraňujú sa <xliff:g id="COUNT">^1</xliff:g> fotky…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> photos…}other{Odstraňuje sa <xliff:g id="COUNT">^1</xliff:g> fotiek…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Chcete povoliť aplikácii <xliff:g id="APP_NAME_0">^1</xliff:g> odstrániť túto položku?}few{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> odstrániť <xliff:g id="COUNT">^2</xliff:g> položky?}many{Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> items?}other{Chcete povoliť aplikácii <xliff:g id="APP_NAME_1">^1</xliff:g> odstrániť <xliff:g id="COUNT">^2</xliff:g> položiek?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Odstraňuje sa položka…}few{Odstraňujú sa <xliff:g id="COUNT">^1</xliff:g> položky…}many{Deleting <xliff:g id="COUNT">^1</xliff:g> items…}other{Odstraňuje sa <xliff:g id="COUNT">^1</xliff:g> položiek…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> nemôže spracovať súbory médií"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Spracúvanie médií bolo zrušené"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Pri spracúvaní médií sa vyskytla chyba"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index d4ac3a1..dc649a9 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Izbirnik fotografij"</string>
<string name="artist_label" msgid="8105600993099120273">"Izvajalec"</string>
<string name="unknown" msgid="2059049215682829375">"Neznano"</string>
<string name="root_images" msgid="5861633549189045666">"Slike"</string>
@@ -29,216 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Nadaljuj"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Dovoli"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Zavrni"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="two">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="few">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">In še <xliff:g id="COUNT_1">^1</xliff:g> dodaten element</item>
- <item quantity="two">In še <xliff:g id="COUNT_1">^1</xliff:g> dodatna elementa</item>
- <item quantity="few">In še <xliff:g id="COUNT_1">^1</xliff:g> dodatni elementi</item>
- <item quantity="other">In še <xliff:g id="COUNT_1">^1</xliff:g> dodatnih elementov</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}one{+<xliff:g id="COUNT_1">^1</xliff:g>}two{+<xliff:g id="COUNT_1">^1</xliff:g>}few{+<xliff:g id="COUNT_1">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{In še <xliff:g id="COUNT_0">^1</xliff:g> dodaten element}one{In še <xliff:g id="COUNT_1">^1</xliff:g> dodaten element}two{In še <xliff:g id="COUNT_1">^1</xliff:g> dodatna elementa}few{In še <xliff:g id="COUNT_1">^1</xliff:g> dodatni elementi}other{In še <xliff:g id="COUNT_1">^1</xliff:g> dodatnih elementov}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Brisanje začasnih datotek aplikacij"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"Aplikacija <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> želi izbrisati nekaj začasnih datotek. To lahko povzroči povečano porabo energije baterije ali povečan prenos podatkov v mobilnem omrežju."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Brisanje začasnih datotek aplikacij …"</string>
<string name="clear" msgid="5524638938415865915">"Počisti"</string>
<string name="allow" msgid="8885707816848569619">"Dovoli"</string>
<string name="deny" msgid="6040983710442068936">"Zavrni"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> zvočno datoteko?</item>
- <item quantity="two">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> zvočni datoteki?</item>
- <item quantity="few">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> zvočne datoteke?</item>
- <item quantity="other">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> zvočnih datotek?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one">Spreminjanje <xliff:g id="COUNT">^1</xliff:g> zvočne datoteke …</item>
- <item quantity="two">Spreminjanje <xliff:g id="COUNT">^1</xliff:g> zvočnih datotek …</item>
- <item quantity="few">Spreminjanje <xliff:g id="COUNT">^1</xliff:g> zvočnih datotek …</item>
- <item quantity="other">Spreminjanje <xliff:g id="COUNT">^1</xliff:g> zvočnih datotek …</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> videoposnetek?</item>
- <item quantity="two">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> videoposnetka?</item>
- <item quantity="few">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> videoposnetke?</item>
- <item quantity="other">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> videoposnetkov?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one">Spreminjanje <xliff:g id="COUNT">^1</xliff:g> videoposnetka …</item>
- <item quantity="two">Spreminjanje <xliff:g id="COUNT">^1</xliff:g> videoposnetkov …</item>
- <item quantity="few">Spreminjanje <xliff:g id="COUNT">^1</xliff:g> videoposnetkov …</item>
- <item quantity="other">Spreminjanje <xliff:g id="COUNT">^1</xliff:g> videoposnetkov …</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> fotografijo?</item>
- <item quantity="two">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> fotografiji?</item>
- <item quantity="few">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> fotografije?</item>
- <item quantity="other">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> fotografij?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one">Spreminjanje <xliff:g id="COUNT">^1</xliff:g> fotografije …</item>
- <item quantity="two">Spreminjanje <xliff:g id="COUNT">^1</xliff:g> fotografij …</item>
- <item quantity="few">Spreminjanje <xliff:g id="COUNT">^1</xliff:g> fotografij …</item>
- <item quantity="other">Spreminjanje <xliff:g id="COUNT">^1</xliff:g> fotografij …</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> element?</item>
- <item quantity="two">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> elementa?</item>
- <item quantity="few">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> elemente?</item>
- <item quantity="other">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> elementov?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one">Spreminjanje <xliff:g id="COUNT">^1</xliff:g> elementa …</item>
- <item quantity="two">Spreminjanje <xliff:g id="COUNT">^1</xliff:g> elementov …</item>
- <item quantity="few">Spreminjanje <xliff:g id="COUNT">^1</xliff:g> elementov …</item>
- <item quantity="other">Spreminjanje <xliff:g id="COUNT">^1</xliff:g> elementov …</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> zvočno datoteko v smetnjak?</item>
- <item quantity="two">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> zvočni datoteki v smetnjak?</item>
- <item quantity="few">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> zvočne datoteke v smetnjak?</item>
- <item quantity="other">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> zvočnih datotek v smetnjak?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one">Premikanje <xliff:g id="COUNT">^1</xliff:g> zvočne datoteke v smetnjak …</item>
- <item quantity="two">Premikanje <xliff:g id="COUNT">^1</xliff:g> zvočnih datotek v smetnjak …</item>
- <item quantity="few">Premikanje <xliff:g id="COUNT">^1</xliff:g> zvočnih datotek v smetnjak …</item>
- <item quantity="other">Premikanje <xliff:g id="COUNT">^1</xliff:g> zvočnih datotek v smetnjak …</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> videoposnetek v smetnjak?</item>
- <item quantity="two">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> videoposnetka v smetnjak?</item>
- <item quantity="few">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> videoposnetke v smetnjak?</item>
- <item quantity="other">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> videoposnetkov v smetnjak?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one">Premikanje <xliff:g id="COUNT">^1</xliff:g> videoposnetka v smetnjak …</item>
- <item quantity="two">Premikanje <xliff:g id="COUNT">^1</xliff:g> videoposnetkov v smetnjak …</item>
- <item quantity="few">Premikanje <xliff:g id="COUNT">^1</xliff:g> videoposnetkov v smetnjak …</item>
- <item quantity="other">Premikanje <xliff:g id="COUNT">^1</xliff:g> videoposnetkov v smetnjak …</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> fotografijo v smetnjak?</item>
- <item quantity="two">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> fotografiji v smetnjak?</item>
- <item quantity="few">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> fotografije v smetnjak?</item>
- <item quantity="other">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> fotografij v smetnjak?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one">Premikanje <xliff:g id="COUNT">^1</xliff:g> fotografije v smetnjak …</item>
- <item quantity="two">Premikanje <xliff:g id="COUNT">^1</xliff:g> fotografij v smetnjak …</item>
- <item quantity="few">Premikanje <xliff:g id="COUNT">^1</xliff:g> fotografij v smetnjak …</item>
- <item quantity="other">Premikanje <xliff:g id="COUNT">^1</xliff:g> fotografij v smetnjak …</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> element v smetnjak?</item>
- <item quantity="two">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> elementa v smetnjak?</item>
- <item quantity="few">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> elemente v smetnjak?</item>
- <item quantity="other">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> elementov v smetnjak?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one">Premikanje <xliff:g id="COUNT">^1</xliff:g> elementa v smetnjak …</item>
- <item quantity="two">Premikanje <xliff:g id="COUNT">^1</xliff:g> elementov v smetnjak …</item>
- <item quantity="few">Premikanje <xliff:g id="COUNT">^1</xliff:g> elementov v smetnjak …</item>
- <item quantity="other">Premikanje <xliff:g id="COUNT">^1</xliff:g> elementov v smetnjak …</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> zvočno datoteko iz smetnjaka?</item>
- <item quantity="two">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> zvočni datoteki iz smetnjaka?</item>
- <item quantity="few">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> zvočne datoteke iz smetnjaka?</item>
- <item quantity="other">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> zvočnih datotek iz smetnjaka?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one">Premikanje <xliff:g id="COUNT">^1</xliff:g> zvočne datoteke iz smetnjaka …</item>
- <item quantity="two">Premikanje <xliff:g id="COUNT">^1</xliff:g> zvočnih datotek iz smetnjaka …</item>
- <item quantity="few">Premikanje <xliff:g id="COUNT">^1</xliff:g> zvočnih datotek iz smetnjaka …</item>
- <item quantity="other">Premikanje <xliff:g id="COUNT">^1</xliff:g> zvočnih datotek iz smetnjaka …</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> videoposnetek iz smetnjaka?</item>
- <item quantity="two">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> videoposnetka iz smetnjaka?</item>
- <item quantity="few">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> videoposnetke iz smetnjaka?</item>
- <item quantity="other">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> videoposnetkov iz smetnjaka?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one">Premikanje <xliff:g id="COUNT">^1</xliff:g> videoposnetka iz smetnjaka …</item>
- <item quantity="two">Premikanje <xliff:g id="COUNT">^1</xliff:g> videoposnetkov iz smetnjaka …</item>
- <item quantity="few">Premikanje <xliff:g id="COUNT">^1</xliff:g> videoposnetkov iz smetnjaka …</item>
- <item quantity="other">Premikanje <xliff:g id="COUNT">^1</xliff:g> videoposnetkov iz smetnjaka …</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> fotografijo iz smetnjaka?</item>
- <item quantity="two">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> fotografiji iz smetnjaka?</item>
- <item quantity="few">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> fotografije iz smetnjaka?</item>
- <item quantity="other">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> fotografij iz smetnjaka?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one">Premikanje <xliff:g id="COUNT">^1</xliff:g> fotografije iz smetnjaka …</item>
- <item quantity="two">Premikanje <xliff:g id="COUNT">^1</xliff:g> fotografij iz smetnjaka …</item>
- <item quantity="few">Premikanje <xliff:g id="COUNT">^1</xliff:g> fotografij iz smetnjaka …</item>
- <item quantity="other">Premikanje <xliff:g id="COUNT">^1</xliff:g> fotografij iz smetnjaka …</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> element iz smetnjaka?</item>
- <item quantity="two">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> elementa iz smetnjaka?</item>
- <item quantity="few">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> elemente iz smetnjaka?</item>
- <item quantity="other">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> elementov iz smetnjaka?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one">Premikanje <xliff:g id="COUNT">^1</xliff:g> elementa iz smetnjaka …</item>
- <item quantity="two">Premikanje <xliff:g id="COUNT">^1</xliff:g> elementov iz smetnjaka …</item>
- <item quantity="few">Premikanje <xliff:g id="COUNT">^1</xliff:g> elementov iz smetnjaka …</item>
- <item quantity="other">Premikanje <xliff:g id="COUNT">^1</xliff:g> elementov iz smetnjaka …</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> zvočno datoteko?</item>
- <item quantity="two">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> zvočni datoteki?</item>
- <item quantity="few">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> zvočne datoteke?</item>
- <item quantity="other">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> zvočnih datotek?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one">Brisanje <xliff:g id="COUNT">^1</xliff:g> zvočne datoteke …</item>
- <item quantity="two">Brisanje <xliff:g id="COUNT">^1</xliff:g> zvočnih datotek …</item>
- <item quantity="few">Brisanje <xliff:g id="COUNT">^1</xliff:g> zvočnih datotek …</item>
- <item quantity="other">Brisanje <xliff:g id="COUNT">^1</xliff:g> zvočnih datotek …</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> videoposnetek?</item>
- <item quantity="two">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> videoposnetka?</item>
- <item quantity="few">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> videoposnetke?</item>
- <item quantity="other">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> videoposnetkov?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one">Brisanje <xliff:g id="COUNT">^1</xliff:g> videoposnetka …</item>
- <item quantity="two">Brisanje <xliff:g id="COUNT">^1</xliff:g> videoposnetkov …</item>
- <item quantity="few">Brisanje <xliff:g id="COUNT">^1</xliff:g> videoposnetkov …</item>
- <item quantity="other">Brisanje <xliff:g id="COUNT">^1</xliff:g> videoposnetkov …</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> fotografijo?</item>
- <item quantity="two">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> fotografiji?</item>
- <item quantity="few">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> fotografije?</item>
- <item quantity="other">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> fotografij?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one">Brisanje <xliff:g id="COUNT">^1</xliff:g> fotografije …</item>
- <item quantity="two">Brisanje <xliff:g id="COUNT">^1</xliff:g> fotografij …</item>
- <item quantity="few">Brisanje <xliff:g id="COUNT">^1</xliff:g> fotografij …</item>
- <item quantity="other">Brisanje <xliff:g id="COUNT">^1</xliff:g> fotografij …</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> element?</item>
- <item quantity="two">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> elementa?</item>
- <item quantity="few">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> elemente?</item>
- <item quantity="other">Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> elementov?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one">Brisanje <xliff:g id="COUNT">^1</xliff:g> elementa …</item>
- <item quantity="two">Brisanje <xliff:g id="COUNT">^1</xliff:g> elementov …</item>
- <item quantity="few">Brisanje <xliff:g id="COUNT">^1</xliff:g> elementov …</item>
- <item quantity="other">Brisanje <xliff:g id="COUNT">^1</xliff:g> elementov …</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Dodaj"</string>
+ <string name="deselect" msgid="4297825044827769490">"Počisti izbiro"</string>
+ <string name="deselected" msgid="8488133193326208475">"Izbor je preklican"</string>
+ <string name="select" msgid="2704765470563027689">"Izberi"</string>
+ <string name="selected" msgid="9151797369975828124">"Izbrano"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Izberite največ <xliff:g id="COUNT_0">^1</xliff:g> element}one{Izberite največ <xliff:g id="COUNT_1">^1</xliff:g> element}two{Izberite največ <xliff:g id="COUNT_1">^1</xliff:g> elementa}few{Izberite največ <xliff:g id="COUNT_1">^1</xliff:g> elemente}other{Izberite največ <xliff:g id="COUNT_1">^1</xliff:g> elementov}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Nedavno"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Ni fotografij ali videoposnetkov."</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Ni albumov."</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Prikaži izbrano"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Fotografije"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albumi"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Predogled"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Preklop na delovni profil"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Preklop na osebni profil"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Blokiral skrbnik."</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Iz osebne aplikacije ni dovoljeno dostopati do delovnih podatkov."</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Iz delovne aplikacije ni dovoljeno dostopati do osebnih podatkov."</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Delovne aplikacije so začasno zaustavljene"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Če želite odpreti delovne fotografije, vklopite delovne aplikacije in poskusite znova."</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Ta aplikacija ima dostop samo do fotografij, ki jih izberete."</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> element}one{<xliff:g id="COUNT_1">^1</xliff:g> element}two{<xliff:g id="COUNT_1">^1</xliff:g> elementa}few{<xliff:g id="COUNT_1">^1</xliff:g> elementi}other{<xliff:g id="COUNT_1">^1</xliff:g> elementov}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Dodaj (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Fotoaparat"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Prenosi"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Priljubljeno"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Posnetki zaslona"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Fotografija z videom"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g>: posneto <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Videoposnetek posnet dne <xliff:g id="TIME">%1$s</xliff:g>, trajanje <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Fotografija"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Fotografija z videom"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Izklopi zvok videa"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Vklopi zvok videa"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Predvajanje videoposnetka"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Začasna zaustavitev videoposnetka"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Predstavnost v oblaku je zdaj na voljo v aplikaciji <xliff:g id="PKG_NAME">%1$s</xliff:g>."</string>
+ <string name="not_selected" msgid="2244008151669896758">"ni izbrano"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g>, da spremeni to zvočno datoteko?}one{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> zvočno datoteko?}two{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> zvočni datoteki?}few{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> zvočne datoteke?}other{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> zvočnih datotek?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Spreminjanje zvočne datoteke …}one{Spreminjanje <xliff:g id="COUNT">^1</xliff:g> zvočne datoteke …}two{Spreminjanje <xliff:g id="COUNT">^1</xliff:g> zvočnih datotek …}few{Spreminjanje <xliff:g id="COUNT">^1</xliff:g> zvočnih datotek …}other{Spreminjanje <xliff:g id="COUNT">^1</xliff:g> zvočnih datotek …}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g>, da spremeni ta videoposnetek?}one{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> videoposnetek?}two{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> videoposnetka?}few{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> videoposnetke?}other{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> videoposnetkov?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Spreminjanje videoposnetka …}one{Spreminjanje <xliff:g id="COUNT">^1</xliff:g> videoposnetka …}two{Spreminjanje <xliff:g id="COUNT">^1</xliff:g> videoposnetkov …}few{Spreminjanje <xliff:g id="COUNT">^1</xliff:g> videoposnetkov …}other{Spreminjanje <xliff:g id="COUNT">^1</xliff:g> videoposnetkov …}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g>, da spremeni to fotografijo?}one{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> fotografijo?}two{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> fotografiji?}few{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> fotografije?}other{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> fotografij?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Spreminjanje fotografije …}one{Spreminjanje <xliff:g id="COUNT">^1</xliff:g> fotografije …}two{Spreminjanje <xliff:g id="COUNT">^1</xliff:g> fotografij …}few{Spreminjanje <xliff:g id="COUNT">^1</xliff:g> fotografij …}other{Spreminjanje <xliff:g id="COUNT">^1</xliff:g> fotografij …}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g>, da spremeni ta element?}one{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> element?}two{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> elementa?}few{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> elemente?}other{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da spremeni <xliff:g id="COUNT">^2</xliff:g> elementov?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Spreminjanje elementa …}one{Spreminjanje <xliff:g id="COUNT">^1</xliff:g> elementa …}two{Spreminjanje <xliff:g id="COUNT">^1</xliff:g> elementov …}few{Spreminjanje <xliff:g id="COUNT">^1</xliff:g> elementov …}other{Spreminjanje <xliff:g id="COUNT">^1</xliff:g> elementov …}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g>, da premakne to zvočno datoteko v smetnjak?}one{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> zvočno datoteko v smetnjak?}two{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> zvočni datoteki v smetnjak?}few{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> zvočne datoteke v smetnjak?}other{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> zvočnih datotek v smetnjak?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Premikanje zvočne datoteke v smetnjak …}one{Premikanje <xliff:g id="COUNT">^1</xliff:g> zvočne datoteke v smetnjak …}two{Premikanje <xliff:g id="COUNT">^1</xliff:g> zvočnih datotek v smetnjak …}few{Premikanje <xliff:g id="COUNT">^1</xliff:g> zvočnih datotek v smetnjak …}other{Premikanje <xliff:g id="COUNT">^1</xliff:g> zvočnih datotek v smetnjak …}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g>, da premakne ta videoposnetek v smetnjak?}one{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> videoposnetek v smetnjak?}two{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> videoposnetka v smetnjak?}few{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> videoposnetke v smetnjak?}other{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> videoposnetkov v smetnjak?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Premikanje videoposnetka v smetnjak …}one{Premikanje <xliff:g id="COUNT">^1</xliff:g> videoposnetka v smetnjak …}two{Premikanje <xliff:g id="COUNT">^1</xliff:g> videoposnetkov v smetnjak …}few{Premikanje <xliff:g id="COUNT">^1</xliff:g> videoposnetkov v smetnjak …}other{Premikanje <xliff:g id="COUNT">^1</xliff:g> videoposnetkov v smetnjak …}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g>, da premakne to fotografijo v smetnjak?}one{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> fotografijo v smetnjak?}two{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> fotografiji v smetnjak?}few{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> fotografije v smetnjak?}other{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> fotografij v smetnjak?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Premikanje fotografije v smetnjak …}one{Premikanje <xliff:g id="COUNT">^1</xliff:g> fotografije v smetnjak …}two{Premikanje <xliff:g id="COUNT">^1</xliff:g> fotografij v smetnjak …}few{Premikanje <xliff:g id="COUNT">^1</xliff:g> fotografij v smetnjak …}other{Premikanje <xliff:g id="COUNT">^1</xliff:g> fotografij v smetnjak …}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g>, da premakne ta element v smetnjak?}one{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> element v smetnjak?}two{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> elementa v smetnjak?}few{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> elemente v smetnjak?}other{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> elementov v smetnjak?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Premikanje elementa v smetnjak …}one{Premikanje <xliff:g id="COUNT">^1</xliff:g> elementa v smetnjak …}two{Premikanje <xliff:g id="COUNT">^1</xliff:g> elementov v smetnjak …}few{Premikanje <xliff:g id="COUNT">^1</xliff:g> elementov v smetnjak …}other{Premikanje <xliff:g id="COUNT">^1</xliff:g> elementov v smetnjak …}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g>, da premakne to zvočno datoteko iz smetnjaka?}one{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> zvočno datoteko iz smetnjaka?}two{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> zvočni datoteki iz smetnjaka?}few{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> zvočne datoteke iz smetnjaka?}other{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> zvočnih datotek iz smetnjaka?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Premikanje zvočne datoteke iz smetnjaka …}one{Premikanje <xliff:g id="COUNT">^1</xliff:g> zvočne datoteke iz smetnjaka …}two{Premikanje <xliff:g id="COUNT">^1</xliff:g> zvočnih datotek iz smetnjaka …}few{Premikanje <xliff:g id="COUNT">^1</xliff:g> zvočnih datotek iz smetnjaka …}other{Premikanje <xliff:g id="COUNT">^1</xliff:g> zvočnih datotek iz smetnjaka …}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g>, da premakne ta videoposnetek iz smetnjaka?}one{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> videoposnetek iz smetnjaka?}two{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> videoposnetka iz smetnjaka?}few{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> videoposnetke iz smetnjaka?}other{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> videoposnetkov iz smetnjaka?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Premikanje videoposnetka iz smetnjaka …}one{Premikanje <xliff:g id="COUNT">^1</xliff:g> videoposnetka iz smetnjaka …}two{Premikanje <xliff:g id="COUNT">^1</xliff:g> videoposnetkov iz smetnjaka …}few{Premikanje <xliff:g id="COUNT">^1</xliff:g> videoposnetkov iz smetnjaka …}other{Premikanje <xliff:g id="COUNT">^1</xliff:g> videoposnetkov iz smetnjaka …}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g>, da premakne to fotografijo iz smetnjaka?}one{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> fotografijo iz smetnjaka?}two{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> fotografiji iz smetnjaka?}few{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> fotografije iz smetnjaka?}other{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> fotografij iz smetnjaka?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Premikanje fotografije iz smetnjaka …}one{Premikanje <xliff:g id="COUNT">^1</xliff:g> fotografije iz smetnjaka …}two{Premikanje <xliff:g id="COUNT">^1</xliff:g> fotografij iz smetnjaka …}few{Premikanje <xliff:g id="COUNT">^1</xliff:g> fotografij iz smetnjaka …}other{Premikanje <xliff:g id="COUNT">^1</xliff:g> fotografij iz smetnjaka …}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g>, da premakne ta element iz smetnjaka?}one{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> element iz smetnjaka?}two{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> elementa iz smetnjaka?}few{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> elemente iz smetnjaka?}other{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da premakne <xliff:g id="COUNT">^2</xliff:g> elementov iz smetnjaka?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Premikanje elementa iz smetnjaka …}one{Premikanje <xliff:g id="COUNT">^1</xliff:g> elementa iz smetnjaka …}two{Premikanje <xliff:g id="COUNT">^1</xliff:g> elementov iz smetnjaka …}few{Premikanje <xliff:g id="COUNT">^1</xliff:g> elementov iz smetnjaka …}other{Premikanje <xliff:g id="COUNT">^1</xliff:g> elementov iz smetnjaka …}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g>, da izbriše to zvočno datoteko?}one{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> zvočno datoteko?}two{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> zvočni datoteki?}few{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> zvočne datoteke?}other{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> zvočnih datotek?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Brisanje zvočne datoteke …}one{Brisanje <xliff:g id="COUNT">^1</xliff:g> zvočne datoteke …}two{Brisanje <xliff:g id="COUNT">^1</xliff:g> zvočnih datotek …}few{Brisanje <xliff:g id="COUNT">^1</xliff:g> zvočnih datotek …}other{Brisanje <xliff:g id="COUNT">^1</xliff:g> zvočnih datotek …}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g>, da izbriše ta videoposnetek?}one{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> videoposnetek?}two{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> videoposnetka?}few{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> videoposnetke?}other{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> videoposnetkov?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Brisanje videoposnetka …}one{Brisanje <xliff:g id="COUNT">^1</xliff:g> videoposnetka …}two{Brisanje <xliff:g id="COUNT">^1</xliff:g> videoposnetkov …}few{Brisanje <xliff:g id="COUNT">^1</xliff:g> videoposnetkov …}other{Brisanje <xliff:g id="COUNT">^1</xliff:g> videoposnetkov …}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g>, da izbriše to fotografijo?}one{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> fotografijo?}two{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> fotografiji?}few{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> fotografije?}other{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> fotografij?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Brisanje fotografije …}one{Brisanje <xliff:g id="COUNT">^1</xliff:g> fotografije …}two{Brisanje <xliff:g id="COUNT">^1</xliff:g> fotografij …}few{Brisanje <xliff:g id="COUNT">^1</xliff:g> fotografij …}other{Brisanje <xliff:g id="COUNT">^1</xliff:g> fotografij …}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_0">^1</xliff:g>, da izbriše ta element?}one{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> element?}two{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> elementa?}few{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> elemente?}other{Želite dovoliti aplikaciji <xliff:g id="APP_NAME_1">^1</xliff:g>, da izbriše <xliff:g id="COUNT">^2</xliff:g> elementov?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Brisanje elementa …}one{Brisanje <xliff:g id="COUNT">^1</xliff:g> elementa …}two{Brisanje <xliff:g id="COUNT">^1</xliff:g> elementov …}few{Brisanje <xliff:g id="COUNT">^1</xliff:g> elementov …}other{Brisanje <xliff:g id="COUNT">^1</xliff:g> elementov …}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> ne more obdelati predstavnostnih datotek."</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Obdelava predstavnosti je preklicana."</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Napaka pri obdelavi predstavnosti"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index d69a10c..2c7928e 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Zgjedhësi i fotografive"</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>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Vazhdo"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Lejo"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Refuzo"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Dhe <xliff:g id="COUNT_1">^1</xliff:g> artikuj të tjerë</item>
- <item quantity="one">Dhe <xliff:g id="COUNT_0">^1</xliff:g> artikull tjetër</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Plus <xliff:g id="COUNT_0">^1</xliff:g> artikull tjetër}other{Plus <xliff:g id="COUNT_1">^1</xliff:g> artikuj të tjerë}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Pastro skedarët e përkohshëm të aplikacioneve"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> dëshiron të pastrojë disa skedarë të përkohshëm. Kjo mund të rezultojë në një rritje të përdorimit të baterisë ose të të dhënave celulare."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Skedarët e përkohshëm të aplikacioneve po pastrohen…"</string>
<string name="clear" msgid="5524638938415865915">"Pastro"</string>
<string name="allow" msgid="8885707816848569619">"Lejo"</string>
<string name="deny" msgid="6040983710442068936">"Refuzo"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të modifikojë <xliff:g id="COUNT">^2</xliff:g> skedarë audio?</item>
- <item quantity="one">Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta modifikojë këtë skedar audio?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> skedarë audio po modifikohen…</item>
- <item quantity="one">Një skedar audio po modifikohet…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të modifikojë <xliff:g id="COUNT">^2</xliff:g> video?</item>
- <item quantity="one">Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta modifikojë këtë video?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> video po modifikohen…</item>
- <item quantity="one">Një video po modifikohet…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të modifikojë <xliff:g id="COUNT">^2</xliff:g> fotografi?</item>
- <item quantity="one">Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta modifikojë këtë fotografi?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> fotografi po modifikohen…</item>
- <item quantity="one">Një fotografi po modifikohet…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të modifikojë <xliff:g id="COUNT">^2</xliff:g> artikuj?</item>
- <item quantity="one">Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta modifikojë këtë artikull?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> artikuj po modifikohen…</item>
- <item quantity="one">Një artikull po modifikohet…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të zhvendosë <xliff:g id="COUNT">^2</xliff:g> skedarë audio te koshi?</item>
- <item quantity="one">Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta zhvendosë këtë skedar audio te koshi?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> skedarë audio po zhvendosen te koshi…</item>
- <item quantity="one">Një skedar audio po zhvendoset te koshi…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të zhvendosë <xliff:g id="COUNT">^2</xliff:g> video te koshi?</item>
- <item quantity="one">Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta zhvendosë këtë video te koshi?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> video po zhvendosen te koshi…</item>
- <item quantity="one">Një video po zhvendoset te koshi…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të zhvendosë <xliff:g id="COUNT">^2</xliff:g> fotografi te koshi?</item>
- <item quantity="one">Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta zhvendosë këtë fotografi te koshi?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> fotografi po zhvendosen te koshi…</item>
- <item quantity="one">Një fotografi po zhvendoset te koshi…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të zhvendosë <xliff:g id="COUNT">^2</xliff:g> artikuj te koshi?</item>
- <item quantity="one">Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta zhvendosë këtë artikull te koshi?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> artikuj po zhvendosen te koshi…</item>
- <item quantity="one">Një artikull po zhvendoset te koshi…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të zhvendosë <xliff:g id="COUNT">^2</xliff:g> skedarë audio nga koshi?</item>
- <item quantity="one">Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta zhvendosë këtë skedar audio nga koshi?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> skedarë audio po zhvendosen nga koshi…</item>
- <item quantity="one">Një skedar audio po zhvendoset nga koshi…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të zhvendosë <xliff:g id="COUNT">^2</xliff:g> video nga koshi?</item>
- <item quantity="one">Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta zhvendosë këtë video nga koshi?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> video po zhvendosen nga koshi…</item>
- <item quantity="one">Një video po zhvendoset nga koshi…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të zhvendosë <xliff:g id="COUNT">^2</xliff:g> fotografi nga koshi?</item>
- <item quantity="one">Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta zhvendosë këtë fotografi nga koshi?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> fotografi po zhvendosen nga koshi…</item>
- <item quantity="one">Një fotografi po zhvendoset nga koshi…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të zhvendosë <xliff:g id="COUNT">^2</xliff:g> artikuj nga koshi?</item>
- <item quantity="one">Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta zhvendosë këtë artikull nga koshi?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> artikuj po zhvendosen nga koshi…</item>
- <item quantity="one">Një artikull po zhvendoset nga koshi…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të fshijë <xliff:g id="COUNT">^2</xliff:g> skedarë audio?</item>
- <item quantity="one">Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që të fshijë këtë skedar audio?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> skedarë audio po fshihen…</item>
- <item quantity="one">Një skedar audio po fshihet…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të fshijë <xliff:g id="COUNT">^2</xliff:g> video?</item>
- <item quantity="one">Të lejohet që <xliff:g id="APP_NAME_0">^1</xliff:g> ta fshijë këtë video?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> video po fshihen…</item>
- <item quantity="one">Një video po fshihet…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të fshijë <xliff:g id="COUNT">^2</xliff:g> fotografi?</item>
- <item quantity="one">Të lejohet që <xliff:g id="APP_NAME_0">^1</xliff:g> ta fshijë këtë fotografi?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> fotografi po fshihen…</item>
- <item quantity="one">Një fotografi po fshihet…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të fshijë <xliff:g id="COUNT">^2</xliff:g> artikuj?</item>
- <item quantity="one">Të lejohet që <xliff:g id="APP_NAME_0">^1</xliff:g> ta fshijë këtë artikull?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> artikuj po fshihen…</item>
- <item quantity="one">Një artikull po fshihet…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Shto"</string>
+ <string name="deselect" msgid="4297825044827769490">"Hiq përzgjedhjen"</string>
+ <string name="deselected" msgid="8488133193326208475">"Zgjedhja është hequr"</string>
+ <string name="select" msgid="2704765470563027689">"Zgjidh"</string>
+ <string name="selected" msgid="9151797369975828124">"Zgjedhur"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Zgjidh deri në <xliff:g id="COUNT_0">^1</xliff:g> artikull}other{Zgjidh deri në <xliff:g id="COUNT_1">^1</xliff:g> artikuj}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Të fundit"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Nuk ka fotografi apo video"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Nuk ka albume"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Shiko të zgjedhurat"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Fotografitë"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albumet"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Pamja paraprake"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Ndryshoje te puna"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Ndryshoje te personale"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Bllokuar nga administratori yt"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Qasja e të dhënave të punës nga një aplikacion personal nuk lejohet"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Qasja e të dhënave personale nga një aplikacion pune nuk lejohet"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Aplikacionet e punës janë vendosur në pauzë"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Për të hapur fotografitë e punës, aktivizo aplikacionet e punës dhe provo sërish"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Ky aplikacion mund të ketë qasje vetëm në fotografitë që zgjedh ti"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> artikull}other{<xliff:g id="COUNT_1">^1</xliff:g> artikuj}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Shto (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Shkarkimet"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Të preferuarat"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Pamjet e ekranit"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Fotografi me lëvizje"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> shkrepur në <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Video e regjistruar në <xliff:g id="TIME">%1$s</xliff:g> me kohëzgjatje <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Fotografi"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Fotografi me lëvizje"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Çaktivizo zërin e videos"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Aktivizo zërin e videos"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Luaj videon"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Vendos videon në pauzë"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Media në renë kompjuterike tani ofrohet nga <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"nuk është zgjedhur"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta modifikojë këtë skedar audio?}other{Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të modifikojë <xliff:g id="COUNT">^2</xliff:g> skedarë audio?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Skedari audio po modifikohet…}other{<xliff:g id="COUNT">^1</xliff:g> skedarë audio po modifikohen…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta modifikojë këtë video?}other{Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të modifikojë <xliff:g id="COUNT">^2</xliff:g> video?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Videoja po modifikohet…}other{<xliff:g id="COUNT">^1</xliff:g> video po modifikohen…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta modifikojë këtë fotografi?}other{Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të modifikojë <xliff:g id="COUNT">^2</xliff:g> fotografi?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Fotografia po modifikohet…}other{<xliff:g id="COUNT">^1</xliff:g> fotografi po modifikohen…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta modifikojë këtë artikull?}other{Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të modifikojë <xliff:g id="COUNT">^2</xliff:g> artikuj?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Artikulli po modifikohet…}other{<xliff:g id="COUNT">^1</xliff:g> artikuj po modifikohen…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta zhvendosë këtë skedar audio te koshi?}other{Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të zhvendosë <xliff:g id="COUNT">^2</xliff:g> skedarë audio te koshi?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Skedari audio po zhvendoset te koshi…}other{<xliff:g id="COUNT">^1</xliff:g> skedarë audio po zhvendosen te koshi…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta zhvendosë këtë video te koshi?}other{Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të zhvendosë <xliff:g id="COUNT">^2</xliff:g> video te koshi?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Videoja po zhvendoset te koshi…}other{<xliff:g id="COUNT">^1</xliff:g> video po zhvendosen te koshi…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta zhvendosë këtë fotografi te koshi?}other{Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të zhvendosë <xliff:g id="COUNT">^2</xliff:g> fotografi te koshi?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Fotografia po zhvendoset te koshi…}other{<xliff:g id="COUNT">^1</xliff:g> fotografi po zhvendosen te koshi…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta zhvendosë këtë artikull te koshi?}other{Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të zhvendosë <xliff:g id="COUNT">^2</xliff:g> artikuj te koshi?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Artikulli po zhvendoset te koshi…}other{<xliff:g id="COUNT">^1</xliff:g> artikuj po zhvendosen te koshi…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta zhvendosë këtë skedar audio nga koshi?}other{Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të zhvendosë <xliff:g id="COUNT">^2</xliff:g> skedarë audio nga koshi?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Skedari audio po zhvendoset nga koshi…}other{<xliff:g id="COUNT">^1</xliff:g> skedarë audio po zhvendosen nga koshi…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta zhvendosë këtë video nga koshi?}other{Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të zhvendosë <xliff:g id="COUNT">^2</xliff:g> video nga koshi?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Videoja po zhvendoset nga koshi…}other{<xliff:g id="COUNT">^1</xliff:g> video po zhvendosen nga koshi…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta zhvendosë këtë fotografi nga koshi?}other{Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të zhvendosë <xliff:g id="COUNT">^2</xliff:g> fotografi nga koshi?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Fotografia po zhvendoset nga koshi…}other{<xliff:g id="COUNT">^1</xliff:g> fotografi po zhvendosen nga koshi…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta zhvendosë këtë artikull nga koshi?}other{Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të zhvendosë <xliff:g id="COUNT">^2</xliff:g> artikuj nga koshi?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Artikulli po zhvendoset nga koshi…}other{<xliff:g id="COUNT">^1</xliff:g> artikuj po zhvendosen nga koshi…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta fshijë këtë skedar audio?}other{Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të fshijë <xliff:g id="COUNT">^2</xliff:g> skedarë audio?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Skedari audio po fshihet…}other{<xliff:g id="COUNT">^1</xliff:g> skedarë audio po fshihen…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta fshijë këtë video?}other{Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të fshijë <xliff:g id="COUNT">^2</xliff:g> video?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Videoja po fshihet…}other{<xliff:g id="COUNT">^1</xliff:g> video po fshihen…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta fshijë këtë fotografi?}other{Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të fshijë <xliff:g id="COUNT">^2</xliff:g> fotografi?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Fotografia po fshihet…}other{<xliff:g id="COUNT">^1</xliff:g> fotografi po fshihen…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Të lejohet <xliff:g id="APP_NAME_0">^1</xliff:g> që ta fshijë këtë artikull?}other{Të lejohet <xliff:g id="APP_NAME_1">^1</xliff:g> që të fshijë <xliff:g id="COUNT">^2</xliff:g> artikuj?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Artikulli po fshihet…}other{<xliff:g id="COUNT">^1</xliff:g> artikuj po fshihen…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> nuk mund t\'i përpunojë skedarët e medias"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Përpunimi i medias u anulua"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Gabim i përpunimit të medias"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index cfc2aa8..95d9152 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Медији"</string>
<string name="storage_description" msgid="4081716890357580107">"Локални меморијски простор"</string>
<string name="app_label" msgid="9035307001052716210">"Меморијски простор за медије"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"Бирач слика"</string>
<string name="artist_label" msgid="8105600993099120273">"Извођач"</string>
<string name="unknown" msgid="2059049215682829375">"Непознато"</string>
<string name="root_images" msgid="5861633549189045666">"Слике"</string>
@@ -29,182 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Настави"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Дозволи"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Одбиј"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">и још <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="few">и још <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">и још <xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">И још <xliff:g id="COUNT_1">^1</xliff:g> ставка</item>
- <item quantity="few">И још <xliff:g id="COUNT_1">^1</xliff:g> ставке</item>
- <item quantity="other">И још <xliff:g id="COUNT_1">^1</xliff:g> ставки</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{и још <xliff:g id="COUNT_0">^1</xliff:g>}one{и још <xliff:g id="COUNT_1">^1</xliff:g>}few{и још <xliff:g id="COUNT_1">^1</xliff:g>}other{и још <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{И још <xliff:g id="COUNT_0">^1</xliff:g> ставка}one{И још <xliff:g id="COUNT_1">^1</xliff:g> ставка}few{И још <xliff:g id="COUNT_1">^1</xliff:g> ставке}other{И још <xliff:g id="COUNT_1">^1</xliff:g> ставки}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Обришите привремене датотеке апликација"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> жели да обрише неке привремене датотеке. Ово може да доведе до повећане потрошње батерије или мобилних података."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Бришу се привремене датотеке апликација…"</string>
<string name="clear" msgid="5524638938415865915">"Обриши"</string>
<string name="allow" msgid="8885707816848569619">"Дозволи"</string>
<string name="deny" msgid="6040983710442068936">"Одбиј"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> измени <xliff:g id="COUNT">^2</xliff:g> аудио датотеку?</item>
- <item quantity="few">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> измени <xliff:g id="COUNT">^2</xliff:g> аудио датотеке?</item>
- <item quantity="other">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> измени <xliff:g id="COUNT">^2</xliff:g> аудио датотека?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one">Мења се <xliff:g id="COUNT">^1</xliff:g> аудио фајл…</item>
- <item quantity="few">Мењају се <xliff:g id="COUNT">^1</xliff:g> аудио фајла…</item>
- <item quantity="other">Мења се <xliff:g id="COUNT">^1</xliff:g> аудио фајлова…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> измени <xliff:g id="COUNT">^2</xliff:g> видео?</item>
- <item quantity="few">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> измени <xliff:g id="COUNT">^2</xliff:g> видео снимка?</item>
- <item quantity="other">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> измени <xliff:g id="COUNT">^2</xliff:g> видео снимака?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one">Мења се <xliff:g id="COUNT">^1</xliff:g> видео…</item>
- <item quantity="few">Мењају се <xliff:g id="COUNT">^1</xliff:g> видео снимка…</item>
- <item quantity="other">Мења се <xliff:g id="COUNT">^1</xliff:g> видео снимака…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> измени <xliff:g id="COUNT">^2</xliff:g> слику?</item>
- <item quantity="few">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> измени <xliff:g id="COUNT">^2</xliff:g> слике?</item>
- <item quantity="other">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> измени <xliff:g id="COUNT">^2</xliff:g> слика?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one">Мења се <xliff:g id="COUNT">^1</xliff:g> слика…</item>
- <item quantity="few">Мењају се <xliff:g id="COUNT">^1</xliff:g> слике…</item>
- <item quantity="other">Мења се <xliff:g id="COUNT">^1</xliff:g> слика…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> измени <xliff:g id="COUNT">^2</xliff:g> ставку?</item>
- <item quantity="few">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> измени <xliff:g id="COUNT">^2</xliff:g> ставке?</item>
- <item quantity="other">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> измени <xliff:g id="COUNT">^2</xliff:g> ставки?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one">Мења се <xliff:g id="COUNT">^1</xliff:g> ставка…</item>
- <item quantity="few">Мењају се <xliff:g id="COUNT">^1</xliff:g> ставке…</item>
- <item quantity="other">Мења се <xliff:g id="COUNT">^1</xliff:g> ставки…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> аудио датотеку у отпад?</item>
- <item quantity="few">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> аудио датотеке у отпад?</item>
- <item quantity="other">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> аудио датотека у отпад?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> аудио фајл се премешта у отпад…</item>
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> аудио фајла се премештају у отпад…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> аудио фајлова се премешта у отпад…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> видео у отпад?</item>
- <item quantity="few">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> видео снимка у отпад?</item>
- <item quantity="other">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> видео снимака у отпад?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> видео се премешта у отпад…</item>
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> видео снимка се премештају у отпад…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> видео снимака се премешта у отпад…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> слику у отпад?</item>
- <item quantity="few">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> слике у отпад?</item>
- <item quantity="other">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> слика у отпад?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> слика се премешта у отпад…</item>
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> слике се премештају у отпад…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> слика се премешта у отпад…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> ставку у отпад?</item>
- <item quantity="few">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> ставке у отпад?</item>
- <item quantity="other">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> ставки у отпад?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ставка се премешта у отпад…</item>
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> ставке се премештају у отпад…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ставки се премешта у отпад…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> аудио датотеку из отпада?</item>
- <item quantity="few">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> аудио датотеке из отпада?</item>
- <item quantity="other">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> аудио датотека из отпада?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> аудио фајл се премешта из отпада…</item>
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> аудио фајла се премештају из отпада…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> аудио фајлова се премешта из отпада…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> видео из отпада?</item>
- <item quantity="few">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> видео снимка из отпада?</item>
- <item quantity="other">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> видео снимака из отпада?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> видео се премешта из отпада…</item>
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> видео снимка се премештају из отпада…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> видео снимака се премешта из отпада…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> слику из отпада?</item>
- <item quantity="few">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> слике из отпада?</item>
- <item quantity="other">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> слика из отпада?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> слика се премешта из отпада…</item>
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> слике се премештају из отпада…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> слика се премешта из отпада…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> ставку из отпада?</item>
- <item quantity="few">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> ставке из отпада?</item>
- <item quantity="other">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> ставки из отпада?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one"><xliff:g id="COUNT">^1</xliff:g> ставка се премешта из отпада…</item>
- <item quantity="few"><xliff:g id="COUNT">^1</xliff:g> ставке се премештају из отпада…</item>
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ставки се премешта из отпада…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> избрише <xliff:g id="COUNT">^2</xliff:g> аудио датотеку?</item>
- <item quantity="few">Желите ли да дозволите <xliff:g id="APP_NAME_1">^1</xliff:g> избрише <xliff:g id="COUNT">^2</xliff:g> аудио датотеке?</item>
- <item quantity="other">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> избрише <xliff:g id="COUNT">^2</xliff:g> аудио датотека?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one">Брише се <xliff:g id="COUNT">^1</xliff:g> аудио фајл…</item>
- <item quantity="few">Бришу се <xliff:g id="COUNT">^1</xliff:g> аудио фајла…</item>
- <item quantity="other">Брише се <xliff:g id="COUNT">^1</xliff:g> аудио фајлова…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> избрише <xliff:g id="COUNT">^2</xliff:g> видео?</item>
- <item quantity="few">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> избрише <xliff:g id="COUNT">^2</xliff:g> видео снимка?</item>
- <item quantity="other">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> избрише <xliff:g id="COUNT">^2</xliff:g> видео снимака?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one">Брише се <xliff:g id="COUNT">^1</xliff:g> видео…</item>
- <item quantity="few">Бришу се <xliff:g id="COUNT">^1</xliff:g> видео снимка…</item>
- <item quantity="other">Брише се <xliff:g id="COUNT">^1</xliff:g> видео снимака…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> избрише <xliff:g id="COUNT">^2</xliff:g> слику?</item>
- <item quantity="few">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> избрише <xliff:g id="COUNT">^2</xliff:g> слике?</item>
- <item quantity="other">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> избрише <xliff:g id="COUNT">^2</xliff:g> слика?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one">Брише се <xliff:g id="COUNT">^1</xliff:g> слика…</item>
- <item quantity="few">Бришу се <xliff:g id="COUNT">^1</xliff:g> слике…</item>
- <item quantity="other">Брише се <xliff:g id="COUNT">^1</xliff:g> слика…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> избрише <xliff:g id="COUNT">^2</xliff:g> ставку?</item>
- <item quantity="few">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> избрише <xliff:g id="COUNT">^2</xliff:g> ставке?</item>
- <item quantity="other">Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> избрише <xliff:g id="COUNT">^2</xliff:g> ставки?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one">Брише се <xliff:g id="COUNT">^1</xliff:g> ставка…</item>
- <item quantity="few">Бришу се <xliff:g id="COUNT">^1</xliff:g> ставке…</item>
- <item quantity="other">Брише се <xliff:g id="COUNT">^1</xliff:g> ставки…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Додај"</string>
+ <string name="deselect" msgid="4297825044827769490">"Опозови избор"</string>
+ <string name="deselected" msgid="8488133193326208475">"Опозван је избор"</string>
+ <string name="select" msgid="2704765470563027689">"Изабери"</string>
+ <string name="selected" msgid="9151797369975828124">"Изабрано"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Изаберите највише <xliff:g id="COUNT_0">^1</xliff:g> ставку}one{Изаберите највише <xliff:g id="COUNT_1">^1</xliff:g> ставку}few{Изаберите највише <xliff:g id="COUNT_1">^1</xliff:g> ставке}other{Изаберите највише <xliff:g id="COUNT_1">^1</xliff:g> ставки}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Недавно"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Нема слика нити видео снимака"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Нема албума"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Прикажи изабранo"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Слике"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Албуми"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Преглед"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Пређи на пословни профил"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Пређи на лични профил"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Блокира администратор"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Приступ подацима о послу из личне апликације није дозвољен"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Приступ личним подацима из пословне апликације није дозвољен"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Пословне апликације су паузиране"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Да бисте отворили пословне слике, укључите пословне апликације, па пробајте поново"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Ова апликација може да приступа само сликама које изаберете"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> ставка}one{<xliff:g id="COUNT_1">^1</xliff:g> ставка}few{<xliff:g id="COUNT_1">^1</xliff:g> ставке}other{<xliff:g id="COUNT_1">^1</xliff:g> ставки}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Додај (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Камера"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Преузето"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Омиљено"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Снимци екрана"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Слике у покрету"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g>: снимљено <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Видео је снимљен <xliff:g id="TIME">%1$s</xliff:g> и траје <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Слика"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Слика у покрету"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Искључи звук видеа"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Укључи звук видеа"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Пусти видео"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Паузирај видео"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"<xliff:g id="PKG_NAME">%1$s</xliff:g> сада нуди медијски садржај у клауду"</string>
+ <string name="not_selected" msgid="2244008151669896758">"није изабрано"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Желите ли да дозволите да <xliff:g id="APP_NAME_0">^1</xliff:g> измени овај аудио фајл?}one{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> измени <xliff:g id="COUNT">^2</xliff:g> аудио фајл?}few{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> измени <xliff:g id="COUNT">^2</xliff:g> аудио фајла?}other{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> измени <xliff:g id="COUNT">^2</xliff:g> аудио фајлова?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Мења се аудио фајл…}one{Мења се <xliff:g id="COUNT">^1</xliff:g> аудио фајл…}few{Мењају се <xliff:g id="COUNT">^1</xliff:g> аудио фајла…}other{Мења се <xliff:g id="COUNT">^1</xliff:g> аудио фајлова…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Желите ли да дозволите да <xliff:g id="APP_NAME_0">^1</xliff:g> измени овај видео?}one{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> измени <xliff:g id="COUNT">^2</xliff:g> видео?}few{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> измени <xliff:g id="COUNT">^2</xliff:g> видео снимка?}other{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> измени <xliff:g id="COUNT">^2</xliff:g> видео снимака?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Мења се видео…}one{Мења се <xliff:g id="COUNT">^1</xliff:g> видео…}few{Мењају се <xliff:g id="COUNT">^1</xliff:g> видео снимка…}other{Мења се <xliff:g id="COUNT">^1</xliff:g> видео снимака…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Желите ли да дозволите да <xliff:g id="APP_NAME_0">^1</xliff:g> измени ову слику?}one{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> измени <xliff:g id="COUNT">^2</xliff:g> слику?}few{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> измени <xliff:g id="COUNT">^2</xliff:g> слике?}other{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> измени <xliff:g id="COUNT">^2</xliff:g> слика?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Мења се слика…}one{Мења се <xliff:g id="COUNT">^1</xliff:g> слика…}few{Мењају се <xliff:g id="COUNT">^1</xliff:g> слике…}other{Мења се <xliff:g id="COUNT">^1</xliff:g> слика…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Желите ли да дозволите да <xliff:g id="APP_NAME_0">^1</xliff:g> измени ову ставку?}one{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> измени <xliff:g id="COUNT">^2</xliff:g> ставку?}few{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> измени <xliff:g id="COUNT">^2</xliff:g> ставке?}other{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> измени <xliff:g id="COUNT">^2</xliff:g> ставки?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Мења се ставка…}one{Мења се <xliff:g id="COUNT">^1</xliff:g> ставка…}few{Мењају се <xliff:g id="COUNT">^1</xliff:g> ставке…}other{Мења се <xliff:g id="COUNT">^1</xliff:g> ставки…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Желите ли да дозволите да <xliff:g id="APP_NAME_0">^1</xliff:g> премести овај аудио фајл у отпад?}one{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> аудио фајл у отпад?}few{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> аудио фајла у отпад?}other{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> аудио фајлова у отпад?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Аудио фајл се премешта у отпад…}one{<xliff:g id="COUNT">^1</xliff:g> аудио фајл се премешта у отпад…}few{<xliff:g id="COUNT">^1</xliff:g> аудио фајла се премештају у отпад…}other{<xliff:g id="COUNT">^1</xliff:g> аудио фајлова се премешта у отпад…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Желите ли да дозволите да <xliff:g id="APP_NAME_0">^1</xliff:g> премести овај видео у отпад?}one{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> видео у отпад?}few{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> видео снимка у отпад?}other{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> видео снимака у отпад?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Видео се премешта у отпад…}one{<xliff:g id="COUNT">^1</xliff:g> видео се премешта у отпад…}few{<xliff:g id="COUNT">^1</xliff:g> видео снимка се премештају у отпад…}other{<xliff:g id="COUNT">^1</xliff:g> видео снимака се премешта у отпад…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Желите ли да дозволите да <xliff:g id="APP_NAME_0">^1</xliff:g> премести ову слику у отпад?}one{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> слику у отпад?}few{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> слике у отпад?}other{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> слика у отпад?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Слика се премешта у отпад…}one{<xliff:g id="COUNT">^1</xliff:g> слика се премешта у отпад…}few{<xliff:g id="COUNT">^1</xliff:g> слике се премештају у отпад…}other{<xliff:g id="COUNT">^1</xliff:g> слика се премешта у отпад…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Желите ли да дозволите да <xliff:g id="APP_NAME_0">^1</xliff:g> премести ову ставку у отпад?}one{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> ставку у отпад?}few{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> ставке у отпад?}other{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> ставки у отпад?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Ставка се премешта у отпад…}one{<xliff:g id="COUNT">^1</xliff:g> ставка се премешта у отпад…}few{<xliff:g id="COUNT">^1</xliff:g> ставке се премештају у отпад…}other{<xliff:g id="COUNT">^1</xliff:g> ставки се премешта у отпад…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Желите ли да дозволите да <xliff:g id="APP_NAME_0">^1</xliff:g> премести овај аудио фајл из отпада?}one{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> аудио фајл из отпада?}few{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> аудио фајла из отпада?}other{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> аудио фајлова из отпада?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Аудио фајл се премешта из отпада…}one{<xliff:g id="COUNT">^1</xliff:g> аудио фајл се премешта из отпада…}few{<xliff:g id="COUNT">^1</xliff:g> аудио фајла се премештају из отпада…}other{<xliff:g id="COUNT">^1</xliff:g> аудио фајлова се премешта из отпада…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Желите ли да дозволите да <xliff:g id="APP_NAME_0">^1</xliff:g> премести овај видео из отпада?}one{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> видео из отпада?}few{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> видео снимка из отпада?}other{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> видео снимака из отпада?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Видео се премешта из отпада…}one{<xliff:g id="COUNT">^1</xliff:g> видео се премешта из отпада…}few{<xliff:g id="COUNT">^1</xliff:g> видео снимка се премештају из отпада…}other{<xliff:g id="COUNT">^1</xliff:g> видео снимака се премешта из отпада…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Желите ли да дозволите да <xliff:g id="APP_NAME_0">^1</xliff:g> премести ову слику из отпада?}one{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> слику из отпада?}few{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> слике из отпада?}other{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> слика из отпада?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Слика се премешта из отпада…}one{<xliff:g id="COUNT">^1</xliff:g> слика се премешта из отпада…}few{<xliff:g id="COUNT">^1</xliff:g> слике се премештају из отпада…}other{<xliff:g id="COUNT">^1</xliff:g> слика се премешта из отпада…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Желите ли да дозволите да <xliff:g id="APP_NAME_0">^1</xliff:g> премести ову ставку из отпада?}one{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> ставку из отпада?}few{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> ставке из отпада?}other{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> премести <xliff:g id="COUNT">^2</xliff:g> ставки из отпада?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Ставка се премешта из отпада…}one{<xliff:g id="COUNT">^1</xliff:g> ставка се премешта из отпада…}few{<xliff:g id="COUNT">^1</xliff:g> ставке се премештају из отпада…}other{<xliff:g id="COUNT">^1</xliff:g> ставки се премешта из отпада…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Желите ли да дозволите да <xliff:g id="APP_NAME_0">^1</xliff:g> избрише овај аудио фајл?}one{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> избрише <xliff:g id="COUNT">^2</xliff:g> аудио фајл?}few{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> избрише <xliff:g id="COUNT">^2</xliff:g> аудио фајла?}other{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> избрише <xliff:g id="COUNT">^2</xliff:g> аудио фајлова?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Брише се аудио фајл…}one{Брише се <xliff:g id="COUNT">^1</xliff:g> аудио фајл…}few{Бришу се <xliff:g id="COUNT">^1</xliff:g> аудио фајла…}other{Брише се <xliff:g id="COUNT">^1</xliff:g> аудио фајлова…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Желите ли да дозволите да <xliff:g id="APP_NAME_0">^1</xliff:g> избрише овај видео?}one{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> избрише <xliff:g id="COUNT">^2</xliff:g> видео?}few{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> избрише <xliff:g id="COUNT">^2</xliff:g> видео снимка?}other{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> избрише <xliff:g id="COUNT">^2</xliff:g> видео снимака?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Брише се видео…}one{Брише се <xliff:g id="COUNT">^1</xliff:g> видео…}few{Бришу се <xliff:g id="COUNT">^1</xliff:g> видео снимка…}other{Брише се <xliff:g id="COUNT">^1</xliff:g> видео снимака…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Желите ли да дозволите да <xliff:g id="APP_NAME_0">^1</xliff:g> избрише ову слику?}one{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> избрише <xliff:g id="COUNT">^2</xliff:g> слику?}few{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> избрише <xliff:g id="COUNT">^2</xliff:g> слике?}other{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> избрише <xliff:g id="COUNT">^2</xliff:g> слика?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Брише се слика…}one{Брише се <xliff:g id="COUNT">^1</xliff:g> слика…}few{Бришу се <xliff:g id="COUNT">^1</xliff:g> слике…}other{Брише се <xliff:g id="COUNT">^1</xliff:g> слика…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Желите ли да дозволите да <xliff:g id="APP_NAME_0">^1</xliff:g> избрише ову ставку?}one{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> избрише <xliff:g id="COUNT">^2</xliff:g> ставку?}few{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> избрише <xliff:g id="COUNT">^2</xliff:g> ставке?}other{Желите ли да дозволите да <xliff:g id="APP_NAME_1">^1</xliff:g> избрише <xliff:g id="COUNT">^2</xliff:g> ставки?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Брише се ставка…}one{Брише се <xliff:g id="COUNT">^1</xliff:g> ставка…}few{Бришу се <xliff:g id="COUNT">^1</xliff:g> ставке…}other{Брише се <xliff:g id="COUNT">^1</xliff:g> ставки…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> не може да обради медијске фајлове"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Обрада медија је отказана"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Грешка при обради медија"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index a576b61..8d7feff 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Fotoväljare"</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>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Fortsätt"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Tillåt"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Neka"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+ <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">och <xliff:g id="COUNT_1">^1</xliff:g> objekt till</item>
- <item quantity="one">och <xliff:g id="COUNT_0">^1</xliff:g> objekt till</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+ <xliff:g id="COUNT_0">^1</xliff:g>}other{+ <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{och <xliff:g id="COUNT_0">^1</xliff:g> objekt till}other{och <xliff:g id="COUNT_1">^1</xliff:g> objekt till}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Rensa tillfälliga appfiler"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vill ta bort några tillfälliga filer. Det kan leda till ökad batteriförbrukning eller användning av mobildata."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Tillfälliga appfiler rensas …"</string>
<string name="clear" msgid="5524638938415865915">"Rensa"</string>
<string name="allow" msgid="8885707816848569619">"Tillåt"</string>
<string name="deny" msgid="6040983710442068936">"Neka"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> ändrar <xliff:g id="COUNT">^2</xliff:g> ljudfiler?</item>
- <item quantity="one">Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> ändrar den här ljudfilen?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ljudfiler modifieras …</item>
- <item quantity="one">Ljudfilen modifieras …</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> ändrar <xliff:g id="COUNT">^2</xliff:g> videor?</item>
- <item quantity="one">Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> ändrar den här videon?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> videor modifieras …</item>
- <item quantity="one">Videon modifieras …</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> ändrar <xliff:g id="COUNT">^2</xliff:g> foton?</item>
- <item quantity="one">Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> ändrar det här fotot?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> foton modifieras …</item>
- <item quantity="one">Fotot modifieras …</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> ändrar <xliff:g id="COUNT">^2</xliff:g> objekt?</item>
- <item quantity="one">Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> ändrar det här objektet?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> objekt modifieras …</item>
- <item quantity="one">Objektet modifieras …</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> flyttar <xliff:g id="COUNT">^2</xliff:g> ljudfiler till papperskorgen?</item>
- <item quantity="one">Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> flyttar den här ljudfilen till papperskorgen?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ljudfiler flyttas till papperskorgen …</item>
- <item quantity="one">Ljudfilen flyttas till papperskorgen …</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> flyttar <xliff:g id="COUNT">^2</xliff:g> videor till papperskorgen?</item>
- <item quantity="one">Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> flyttar den här videon till papperskorgen?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> videor flyttas till papperskorgen …</item>
- <item quantity="one">Videon flyttas till papperskorgen …</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> flyttar <xliff:g id="COUNT">^2</xliff:g> foton till papperskorgen?</item>
- <item quantity="one">Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> flyttar det här fotot till papperskorgen?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> foton flyttas till papperskorgen …</item>
- <item quantity="one">Fotot flyttas till papperskorgen …</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> flyttar <xliff:g id="COUNT">^2</xliff:g> objekt till papperskorgen?</item>
- <item quantity="one">Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> flyttar det här objektet till papperskorgen?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> objekt flyttas till papperskorgen …</item>
- <item quantity="one">Objektet flyttas till papperskorgen …</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> flyttar <xliff:g id="COUNT">^2</xliff:g> ljudfiler från papperskorgen?</item>
- <item quantity="one">Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> flyttar den här ljudfilen från papperskorgen?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ljudfiler flyttas från papperskorgen …</item>
- <item quantity="one">Ljudfilen flyttas från papperskorgen …</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> flyttar <xliff:g id="COUNT">^2</xliff:g> videor från papperskorgen?</item>
- <item quantity="one">Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> flyttar den här videon från papperskorgen?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> videor flyttas från papperskorgen …</item>
- <item quantity="one">Videon flyttas från papperskorgen …</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> flyttar <xliff:g id="COUNT">^2</xliff:g> foton från papperskorgen?</item>
- <item quantity="one">Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> flyttar det här fotot från papperskorgen?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> foton flyttas från papperskorgen …</item>
- <item quantity="one">Fotot flyttas från papperskorgen …</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> flyttar <xliff:g id="COUNT">^2</xliff:g> objekt från papperskorgen?</item>
- <item quantity="one">Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> flyttar det här objektet från papperskorgen?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> objekt flyttas från papperskorgen …</item>
- <item quantity="one">Objektet flyttas från papperskorgen …</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> raderar <xliff:g id="COUNT">^2</xliff:g> ljudfiler?</item>
- <item quantity="one">Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> raderar den här ljudfilen?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ljudfiler raderas …</item>
- <item quantity="one">Ljudfilen raderas …</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> raderar <xliff:g id="COUNT">^2</xliff:g> videor?</item>
- <item quantity="one">Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> raderar den här videon?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> videor raderas …</item>
- <item quantity="one">Videon raderas …</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> raderar <xliff:g id="COUNT">^2</xliff:g> foton?</item>
- <item quantity="one">Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> raderar det här fotot?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> foton raderas …</item>
- <item quantity="one">Fotot raderas …</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> raderar <xliff:g id="COUNT">^2</xliff:g> objekt?</item>
- <item quantity="one">Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> raderar det här objektet?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> objekt raderas …</item>
- <item quantity="one">Objektet raderas …</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Lägg till"</string>
+ <string name="deselect" msgid="4297825044827769490">"Avmarkera"</string>
+ <string name="deselected" msgid="8488133193326208475">"Avmarkerad"</string>
+ <string name="select" msgid="2704765470563027689">"Välj"</string>
+ <string name="selected" msgid="9151797369975828124">"Markerad"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Välj upp till <xliff:g id="COUNT_0">^1</xliff:g> objekt}other{Välj upp till <xliff:g id="COUNT_1">^1</xliff:g> objekt}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Senaste"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Inga foton eller videor"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Inga album"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Visa valda"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Foton"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Album"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Förhandsgranska"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Byt till jobbprofilen"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Byt till den privata profilen"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Blockeras av administratören"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Åtkomst till jobbdata från en privat app tillåts inte"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Åtkomst till privat data från en jobbapp tillåts inte"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Jobbappar har pausats"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Om du vill öppna jobbfoton aktiverar du jobbappar och försöker igen"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Denna app får bara tillgång till foton du väljer"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> objekt}other{<xliff:g id="COUNT_1">^1</xliff:g> objekt}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Lägg till (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Nedladdningar"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Favoriter"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Skärmbilder"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Rörelsefoto"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> togs <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Videon spelades in den <xliff:g id="TIME">%1$s</xliff:g>. Längd: <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Foto"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Rörelsefoto"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Stäng av ljudet för videon"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Slå på ljudet för videon"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Spela upp video"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Pausa video"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Molnmedia är nu tillgänglig från <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"inte valt"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> ändrar den här ljudfilen?}other{Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> ändrar <xliff:g id="COUNT">^2</xliff:g> ljudfiler?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Ljudfilen ändras …}other{<xliff:g id="COUNT">^1</xliff:g> ljudfiler ändras …}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> ändrar den här videon?}other{Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> ändrar <xliff:g id="COUNT">^2</xliff:g> videor?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Videon ändras …}other{<xliff:g id="COUNT">^1</xliff:g> videor ändras …}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> ändrar det här fotot?}other{Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> ändrar <xliff:g id="COUNT">^2</xliff:g> foton?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Fotot ändras …}other{<xliff:g id="COUNT">^1</xliff:g> foton ändras …}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> ändrar det här objektet?}other{Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> ändrar <xliff:g id="COUNT">^2</xliff:g> objekt?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Objektet ändras …}other{<xliff:g id="COUNT">^1</xliff:g> objekt ändras …}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> flyttar den här ljudfilen till papperskorgen?}other{Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> flyttar <xliff:g id="COUNT">^2</xliff:g> ljudfiler till papperskorgen?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Ljudfilen flyttas till papperskorgen …}other{<xliff:g id="COUNT">^1</xliff:g> ljudfiler flyttas till papperskorgen …}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> flyttar den här videon till papperskorgen?}other{Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> flyttar <xliff:g id="COUNT">^2</xliff:g> videor till papperskorgen?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Videon flyttas till papperskorgen …}other{<xliff:g id="COUNT">^1</xliff:g> videor flyttas till papperskorgen …}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> flyttar det här fotot till papperskorgen?}other{Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> flyttar <xliff:g id="COUNT">^2</xliff:g> foton till papperskorgen?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Fotot flyttas till papperskorgen …}other{<xliff:g id="COUNT">^1</xliff:g> foton flyttas till papperskorgen …}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> flyttar det här objektet till papperskorgen?}other{Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> flyttar <xliff:g id="COUNT">^2</xliff:g> objekt till papperskorgen?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Objektet flyttas till papperskorgen …}other{<xliff:g id="COUNT">^1</xliff:g> objekt flyttas till papperskorgen …}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> flyttar den här ljudfilen från papperskorgen?}other{Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> flyttar <xliff:g id="COUNT">^2</xliff:g> ljudfiler från papperskorgen?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Ljudfilen flyttas från papperskorgen …}other{<xliff:g id="COUNT">^1</xliff:g> ljudfiler flyttas från papperskorgen …}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> flyttar den här videon från papperskorgen?}other{Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> flyttar <xliff:g id="COUNT">^2</xliff:g> videor från papperskorgen?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Videon flyttas från papperskorgen …}other{<xliff:g id="COUNT">^1</xliff:g> videor flyttas från papperskorgen …}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> flyttar det här fotot från papperskorgen?}other{Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> flyttar <xliff:g id="COUNT">^2</xliff:g> foton från papperskorgen?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Fotot flyttas från papperskorgen …}other{<xliff:g id="COUNT">^1</xliff:g> foton flyttas från papperskorgen …}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> flyttar det här objektet från papperskorgen?}other{Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> flyttar <xliff:g id="COUNT">^2</xliff:g> objekt från papperskorgen?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Objektet flyttas från papperskorgen …}other{<xliff:g id="COUNT">^1</xliff:g> objekt flyttas från papperskorgen …}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> raderar den här ljudfilen?}other{Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> raderar <xliff:g id="COUNT">^2</xliff:g> ljudfiler?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Ljudfilen raderas …}other{<xliff:g id="COUNT">^1</xliff:g> ljudfiler raderas …}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> raderar den här videon?}other{Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> raderar <xliff:g id="COUNT">^2</xliff:g> videor?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Videon raderas …}other{<xliff:g id="COUNT">^1</xliff:g> videor raderas …}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> raderar det här fotot?}other{Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> raderar <xliff:g id="COUNT">^2</xliff:g> foton?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Fotot raderas …}other{<xliff:g id="COUNT">^1</xliff:g> foton raderas …}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Vill du tillåta att <xliff:g id="APP_NAME_0">^1</xliff:g> raderar det här objektet?}other{Vill du tillåta att <xliff:g id="APP_NAME_1">^1</xliff:g> raderar <xliff:g id="COUNT">^2</xliff:g> objekt?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Objektet raderas …}other{<xliff:g id="COUNT">^1</xliff:g> objekt raderas …}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> kan inte behandla mediefiler"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Mediebearbetningen har avbrutits"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Fel vid mediebearbetning"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 1357d44..bea0fd2 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Kiteua picha"</string>
<string name="artist_label" msgid="8105600993099120273">"Msanii"</string>
<string name="unknown" msgid="2059049215682829375">"Isiyojulikana"</string>
<string name="root_images" msgid="5861633549189045666">"Picha"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Endelea"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Ruhusu"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Kataa"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Pamoja na vipengee <xliff:g id="COUNT_1">^1</xliff:g> zaidi</item>
- <item quantity="one">Pamoja na kipengee <xliff:g id="COUNT_0">^1</xliff:g> zaidi</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Pamoja na kipengee <xliff:g id="COUNT_0">^1</xliff:g> zaidi}other{Pamoja na vipengee <xliff:g id="COUNT_1">^1</xliff:g> zaidi}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Futa faili za muda za programu"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ingependa kufuta baadhi ya faili za muda. Huenda hii ikasababisha kuongezeka kwa matumizi ya betri au data ya mtandao wa simu."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Inafuta faili za muda za programu…"</string>
<string name="clear" msgid="5524638938415865915">"Futa"</string>
<string name="allow" msgid="8885707816848569619">"Ruhusu"</string>
<string name="deny" msgid="6040983710442068936">"Kataa"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> ibadilishe faili <xliff:g id="COUNT">^2</xliff:g> za sauti?</item>
- <item quantity="one">Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> ibadilishe faili hii ya sauti?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">Inarekebisha faili <xliff:g id="COUNT">^1</xliff:g> za sauti…</item>
- <item quantity="one">Inarekebisha faili ya sauti…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> ibadilishe video <xliff:g id="COUNT">^2</xliff:g>?</item>
- <item quantity="one">Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> ibadilishe video hii?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">Inarekebisha video <xliff:g id="COUNT">^1</xliff:g>…</item>
- <item quantity="one">Inarekebisha video…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> ibadilishe picha <xliff:g id="COUNT">^2</xliff:g>?</item>
- <item quantity="one">Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> ibadilishe picha hii?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">Inarekebisha picha <xliff:g id="COUNT">^1</xliff:g>…</item>
- <item quantity="one">Inarekebisha picha…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> ibadilishe vipengee <xliff:g id="COUNT">^2</xliff:g>?</item>
- <item quantity="one">Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> ibadilishe kipengee hiki?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">Inarekebisha vipengee <xliff:g id="COUNT">^1</xliff:g>…</item>
- <item quantity="one">Inarekebisha kipengee…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> ihamishie faili <xliff:g id="COUNT">^2</xliff:g> za sauti kwenye tupio?</item>
- <item quantity="one">Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> ihamishie faili hii ya sauti kwenye tupio?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">Inahamishia faili <xliff:g id="COUNT">^1</xliff:g> za sauti kwenye tupio…</item>
- <item quantity="one">Inahamishia faili ya sauti kwenye tupio…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> ihamishie video <xliff:g id="COUNT">^2</xliff:g> kwenye tupio?</item>
- <item quantity="one">Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> ihamishie video hii kwenye tupio?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">Inahamishia video <xliff:g id="COUNT">^1</xliff:g> kwenye tupio…</item>
- <item quantity="one">Inahamishia video kwenye tupio…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> ihamishie picha <xliff:g id="COUNT">^2</xliff:g> kwenye tupio?</item>
- <item quantity="one">Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> ihamishie picha hii kwenye tupio?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">Inahamishia picha <xliff:g id="COUNT">^1</xliff:g> kwenye tupio…</item>
- <item quantity="one">Inahamishia picha kwenye tupio…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> ihamishie vipengee <xliff:g id="COUNT">^2</xliff:g> kwenye tupio?</item>
- <item quantity="one">Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> ihamishie kipengee hiki kwenye tupio?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">Inahamishia vipengee <xliff:g id="COUNT">^1</xliff:g> kwenye tupio…</item>
- <item quantity="one">Inahamishia kipengee kwenye tupio…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> iondoe faili <xliff:g id="COUNT">^2</xliff:g> za sauti kwenye tupio?</item>
- <item quantity="one">Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> iondoe faili hii ya sauti kwenye tupio?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">Inahamishia faili <xliff:g id="COUNT">^1</xliff:g> za sauti kwenye tupio…</item>
- <item quantity="one">Inahamishia faili ya sauti kwenye tupio…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> iondoe video <xliff:g id="COUNT">^2</xliff:g> kwenye tupio?</item>
- <item quantity="one">Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> iondoe video hii kwenye tupio?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">Inaondoa video <xliff:g id="COUNT">^1</xliff:g> kwenye tupio…</item>
- <item quantity="one">Inaondoa video kwenye tupio…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> iondoe picha <xliff:g id="COUNT">^2</xliff:g> kwenye tupio?</item>
- <item quantity="one">Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> iondoe picha hii kwenye tupio?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">Inaondoa picha <xliff:g id="COUNT">^1</xliff:g> kwenye tupio…</item>
- <item quantity="one">Inaondoa picha kwenye tupio…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> iondoe vipengee <xliff:g id="COUNT">^2</xliff:g> kwenye tupio?</item>
- <item quantity="one">Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> iondoe kipengee hiki kwenye tupio?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">Inaondoa vipengee <xliff:g id="COUNT">^1</xliff:g> kwenye tupio…</item>
- <item quantity="one">Inaondoa kipengee kwenye tupio…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> ifute faili <xliff:g id="COUNT">^2</xliff:g> za sauti?</item>
- <item quantity="one">Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> ifute faili hii ya sauti?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">Inafuta faili <xliff:g id="COUNT">^1</xliff:g> za sauti…</item>
- <item quantity="one">Inafuta faili ya sauti…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> ifute video <xliff:g id="COUNT">^2</xliff:g>?</item>
- <item quantity="one">Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> ifute video hii?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">Inafuta video <xliff:g id="COUNT">^1</xliff:g>…</item>
- <item quantity="one">Inafuta video…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> ifute picha <xliff:g id="COUNT">^2</xliff:g>?</item>
- <item quantity="one">Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> ifute picha hii?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">Inafuta picha <xliff:g id="COUNT">^1</xliff:g>…</item>
- <item quantity="one">Inafuta picha…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> ifute vipengee <xliff:g id="COUNT">^2</xliff:g>?</item>
- <item quantity="one">Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> ifute kipengee hiki?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">Inafuta vipengee <xliff:g id="COUNT">^1</xliff:g>…</item>
- <item quantity="one">Inafuta kipengee…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Weka"</string>
+ <string name="deselect" msgid="4297825044827769490">"Acha kuchagua"</string>
+ <string name="deselected" msgid="8488133193326208475">"Umeacha kuchagua"</string>
+ <string name="select" msgid="2704765470563027689">"Chagua"</string>
+ <string name="selected" msgid="9151797369975828124">"Umechagua"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Chagua hadi kipengee <xliff:g id="COUNT_0">^1</xliff:g>}other{Chagua hadi vipengee <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Za hivi majuzi"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Hakuna picha au video"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Hakuna albamu"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Angalia ulizochagua"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Picha"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albamu"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Onyesho la kukagua"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Badili uweke wasifu wa kazini"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Badili uweke wasifu wa binafsi"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Umezuiwa na msimamizi wako"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Huruhusiwi kufikia data ya kazini kwenye programu ya binafsi"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Huruhusiwi kufikia data binafsi kwenye programu ya kazini"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Programu za kazini zimesimamishwa"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Ili ufungue picha za kazini, washa programu zako za kazini kisha ujaribu tena"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Programu hii inaweza tu kufikia picha unazochagua"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{Kipengee <xliff:g id="COUNT_0">^1</xliff:g>}other{Vipengee <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Weka (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Vipakuliwa"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Vipendwa"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Picha za skrini"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Picha Zenye Video"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> iliyopigwa <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Video imerekodiwa <xliff:g id="TIME">%1$s</xliff:g> ikiwa na muda wa <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Picha"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Picha zenye video"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Zima sauti ya video"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Rejesha sauti ya video"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Cheza video"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Sitisha video"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Maudhui ya kwenye wingu sasa yanapatikana katika <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"haijachaguliwa"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> ibadilishe faili hii ya sauti?}other{Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> ibadilishe faili <xliff:g id="COUNT">^2</xliff:g> za sauti?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Inarekebisha faili ya sauti…}other{Inarekebisha faili <xliff:g id="COUNT">^1</xliff:g> za sauti…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> ibadilishe video hii?}other{Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> ibadilishe video <xliff:g id="COUNT">^2</xliff:g>?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Inarekebisha video…}other{Inarekebisha video <xliff:g id="COUNT">^1</xliff:g>…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> ibadilishe picha hii?}other{Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> ibadilishe picha <xliff:g id="COUNT">^2</xliff:g>?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Inarekebisha picha…}other{Inarekebisha picha <xliff:g id="COUNT">^1</xliff:g>…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> ibadilishe kipengee hiki?}other{Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> ibadilishe vipengee <xliff:g id="COUNT">^2</xliff:g>?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Inarekebisha kipengee…}other{Inarekebisha vipengee <xliff:g id="COUNT">^1</xliff:g>…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> ihamishie faili hii ya sauti kwenye tupio?}other{Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> ihamishie faili <xliff:g id="COUNT">^2</xliff:g> za sauti kwenye tupio?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Inahamishia faili ya sauti kwenye tupio…}other{Inahamishia faili <xliff:g id="COUNT">^1</xliff:g> za sauti kwenye tupio…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> ihamishie video hii kwenye tupio?}other{Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> ihamishie video <xliff:g id="COUNT">^2</xliff:g> kwenye tupio?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Inahamishia video kwenye tupio…}other{Inahamishia video <xliff:g id="COUNT">^1</xliff:g> kwenye tupio…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> ihamishie picha hii kwenye tupio?}other{Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> ihamishie picha <xliff:g id="COUNT">^2</xliff:g> kwenye tupio?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Inahamishia picha kwenye tupio…}other{Inahamishia picha <xliff:g id="COUNT">^1</xliff:g> kwenye tupio…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> ihamishie kipengee hiki kwenye tupio?}other{Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> ihamishie vipengee <xliff:g id="COUNT">^2</xliff:g> kwenye tupio?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Inahamishia kipengee kwenye tupio…}other{Inahamishia vipengee <xliff:g id="COUNT">^1</xliff:g> kwenye tupio…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> iondoe faili hii ya sauti kwenye tupio?}other{Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> iondoe faili <xliff:g id="COUNT">^2</xliff:g> za sauti kwenye tupio?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Inaondoa faili ya sauti kwenye tupio…}other{Inaondoa faili <xliff:g id="COUNT">^1</xliff:g> za sauti kwenye tupio…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> iondoe video hii kwenye tupio?}other{Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> iondoe video <xliff:g id="COUNT">^2</xliff:g> kwenye tupio?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Inaondoa video kwenye tupio…}other{Inaondoa video <xliff:g id="COUNT">^1</xliff:g> kwenye tupio…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> iondoe picha hii kwenye tupio?}other{Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> iondoe picha <xliff:g id="COUNT">^2</xliff:g> kwenye tupio?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Inaondoa picha kwenye tupio…}other{Inaondoa picha <xliff:g id="COUNT">^1</xliff:g> kwenye tupio…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> iondoe kipengee hiki kwenye tupio?}other{Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> iondoe vipengee <xliff:g id="COUNT">^2</xliff:g> kwenye tupio?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Inaondoa kipengee kwenye tupio…}other{Inaondoa vipengee <xliff:g id="COUNT">^1</xliff:g> kwenye tupio…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> ifute faili hii ya sauti?}other{Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> ifute faili <xliff:g id="COUNT">^2</xliff:g> za sauti?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Inafuta faili ya sauti…}other{Inafuta faili <xliff:g id="COUNT">^1</xliff:g> za sauti…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> ifute video hii?}other{Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> ifute video <xliff:g id="COUNT">^2</xliff:g>?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Inafuta video…}other{Inafuta video <xliff:g id="COUNT">^1</xliff:g>…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> ifute picha hii?}other{Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> ifute picha <xliff:g id="COUNT">^2</xliff:g>?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Inafuta picha…}other{Inafuta picha <xliff:g id="COUNT">^1</xliff:g>…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Ungependa kuruhusu <xliff:g id="APP_NAME_0">^1</xliff:g> ifute kipengee hiki?}other{Ungependa kuruhusu <xliff:g id="APP_NAME_1">^1</xliff:g> ifute vipengee <xliff:g id="COUNT">^2</xliff:g>?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Inafuta kipengee…}other{Inafuta vipengee <xliff:g id="COUNT">^1</xliff:g>…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> imeshindwa kuchakata faili za maudhui"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Mchakato wa maudhui umeghairiwa"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Hitilafu ya kuchakata maudhui"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index a5cee8d..bd19a87 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"மீடியா"</string>
<string name="storage_description" msgid="4081716890357580107">"சாதனச் சேமிப்பகம்"</string>
<string name="app_label" msgid="9035307001052716210">"மீடியா சேமிப்பிடம்"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"படத் தேர்வுக் கருவி"</string>
<string name="artist_label" msgid="8105600993099120273">"கலைஞர்"</string>
<string name="unknown" msgid="2059049215682829375">"அறியாதது"</string>
<string name="root_images" msgid="5861633549189045666">"Images"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"தொடர்க"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"அனுமதி"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"நிராகரி"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">அத்துடன் கூடுதலாக <xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">அத்துடன் கூடுதலாக <xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{அத்துடன் கூடுதலாக <xliff:g id="COUNT_0">^1</xliff:g> ஆவணம்}other{அத்துடன் கூடுதலாக <xliff:g id="COUNT_1">^1</xliff:g> ஆவணங்கள்}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"ஆப்ஸின் தற்காலிக ஃபைல்களை அழித்தல்"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"சில தற்காலிக ஃபைல்களை நீக்குவதற்கு <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> அனுமதி கேட்கின்றது. இதனால் பேட்டரி அல்லது மொபைல் டேட்டா பயன்பாடு அதிகரிக்கக்கூடும்."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"ஆப்ஸிலுள்ள தற்காலிக ஃபைல்களை அழிக்கிறது…"</string>
<string name="clear" msgid="5524638938415865915">"அழி"</string>
<string name="allow" msgid="8885707816848569619">"அனுமதி"</string>
<string name="deny" msgid="6040983710442068936">"நிராகரி"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ஆடியோ ஃபைல்களில் மாற்றங்களைச் செய்ய <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- <item quantity="one">இந்த ஆடியோ ஃபைலில் மாற்றங்களைச் செய்ய <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ஆடியோ ஃபைல்களை மாற்றியமைக்கிறது…</item>
- <item quantity="one">ஆடியோ ஃபைலை மாற்றியமைக்கிறது…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> வீடியோக்களில் மாற்றங்களைச் செய்ய <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- <item quantity="one">இந்த வீடியோவில் மாற்றங்களைச் செய்ய <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> வீடியோக்களை மாற்றியமைக்கிறது…</item>
- <item quantity="one">வீடியோவை மாற்றியமைக்கிறது…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> படங்களில் மாற்றங்களைச் செய்ய <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- <item quantity="one">இந்தப் படத்தில் மாற்றங்களைச் செய்ய <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> படங்களை மாற்றியமைக்கிறது…</item>
- <item quantity="one">படத்தை மாற்றியமைக்கிறது…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ஃபைல்களில் மாற்றங்களைச் செய்ய <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- <item quantity="one">இந்த ஃபைலில் மாற்றங்களைச் செய்ய <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ஆவணங்களை மாற்றியமைக்கிறது…</item>
- <item quantity="one">ஆவணத்தை மாற்றியமைக்கிறது…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ஆடியோ ஃபைல்களை நீக்கியவற்றிற்கு நகர்த்த <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- <item quantity="one">இந்த ஆடியோ ஃபைலை நீக்கியவற்றிற்கு நகர்த்த <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ஆடியோ ஃபைல்களை ‘நீக்கியவை’ ஃபோல்டருக்கு நகர்த்துகிறது…</item>
- <item quantity="one">ஆடியோ ஃபைலை ‘நீக்கியவை’ ஃபோல்டருக்கு நகர்த்துகிறது…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> வீடியோக்களை நீக்கியவற்றிற்கு நகர்த்த <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- <item quantity="one">இந்த வீடியோவை நீக்கியவற்றிற்கு நகர்த்த <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> வீடியோக்களை ‘நீக்கியவை’ ஃபோல்டருக்கு நகர்த்துகிறது…</item>
- <item quantity="one">வீடியோவை ‘நீக்கியவை’ ஃபோல்டருக்கு நகர்த்துகிறது…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> படங்களை நீக்கியவற்றிற்கு நகர்த்த <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- <item quantity="one">இந்தப் படத்தை நீக்கியவற்றிற்கு நகர்த்த <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> படங்களை ‘நீக்கியவை’ ஃபோல்டருக்கு நகர்த்துகிறது…</item>
- <item quantity="one">படத்தை ‘நீக்கியவை’ ஃபோல்டருக்கு நகர்த்துகிறது…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ஃபைல்களை நீக்கியவற்றிற்கு நகர்த்த <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- <item quantity="one">இந்த ஃபைலை நீக்கியவற்றிற்கு நகர்த்த <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ஆவணங்களை ‘நீக்கியவை’ ஃபோல்டருக்கு நகர்த்துகிறது…</item>
- <item quantity="one">ஆவணத்தை ‘நீக்கியவை’ ஃபோல்டருக்கு நகர்த்துகிறது…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ஆடியோ ஃபைல்களை நீக்கியவற்றில் இருந்து நகர்த்த <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- <item quantity="one">இந்த ஆடியோ ஃபைலை நீக்கியவற்றில் இருந்து நகர்த்த <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ஆடியோ ஃபைல்களை ‘நீக்கியவை’ ஃபோல்டரிலிருந்து நகர்த்துகிறது…</item>
- <item quantity="one">ஆடியோ ஃபைலை ‘நீக்கியவை’ ஃபோல்டரிலிருந்து நகர்த்துகிறது…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> வீடியோக்களை நீக்கியவற்றில் இருந்து நகர்த்த <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- <item quantity="one">இந்த வீடியோவை நீக்கியவற்றில் இருந்து நகர்த்த <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> வீடியோக்களை ‘நீக்கியவை’ ஃபோல்டரிலிருந்து நகர்த்துகிறது…</item>
- <item quantity="one">வீடியோவை ‘நீக்கியவை’ ஃபோல்டரிலிருந்து நகர்த்துகிறது…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> படங்களை நீக்கியவற்றில் இருந்து நகர்த்த <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- <item quantity="one">இந்தப் படத்தை நீக்கியவற்றில் இருந்து நகர்த்த <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> படங்களை ‘நீக்கியவை’ ஃபோல்டரிலிருந்து நகர்த்துகிறது…</item>
- <item quantity="one">படத்தை ‘நீக்கியவை’ ஃபோல்டரிலிருந்து நகர்த்துகிறது…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ஃபைல்களை நீக்கியவற்றில் இருந்து நகர்த்த <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- <item quantity="one">இந்த ஃபைலை நீக்கியவற்றில் இருந்து நகர்த்த <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ஆவணங்களை ‘நீக்கியவை’ ஃபோல்டரிலிருந்து நகர்த்துகிறது…</item>
- <item quantity="one">ஆவணத்தை ‘நீக்கியவை’ ஃபோல்டரிலிருந்து நகர்த்துகிறது…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ஆடியோ ஃபைல்களை நீக்க <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- <item quantity="one">இந்த ஆடியோ ஃபைலை நீக்க <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ஆடியோ ஃபைல்களை நீக்குகிறது…</item>
- <item quantity="one">ஆடியோ ஃபைலை நீக்குகிறது…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> வீடியோக்களை நீக்க <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- <item quantity="one">இந்த வீடியோவை நீக்க <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> வீடியோக்களை நீக்குகிறது…</item>
- <item quantity="one">வீடியோவை நீக்குகிறது…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> படங்களை நீக்க <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- <item quantity="one">இந்தப் படத்தை நீக்க <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> படங்களை நீக்குகிறது…</item>
- <item quantity="one">படத்தை நீக்குகிறது…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ஃபைல்களை நீக்க <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- <item quantity="one">இந்த ஃபைலை நீக்க <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ஆவணங்களை நீக்குகிறது…</item>
- <item quantity="one">ஆவணத்தை நீக்குகிறது…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"சேர்"</string>
+ <string name="deselect" msgid="4297825044827769490">"தேர்வுநீக்கு"</string>
+ <string name="deselected" msgid="8488133193326208475">"தேர்வுநீக்கப்பட்டது"</string>
+ <string name="select" msgid="2704765470563027689">"தேர்ந்தெடு"</string>
+ <string name="selected" msgid="9151797369975828124">"தேர்ந்தெடுக்கப்பட்டது"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> படத்தைத் தேர்ந்தெடுங்கள்}other{<xliff:g id="COUNT_1">^1</xliff:g> படங்களைத் தேர்ந்தெடுங்கள்}}"</string>
+ <string name="recent" msgid="6694613584743207874">"சமீபத்தியவை"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"படங்களோ வீடியோக்களோ இல்லை"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"ஆல்பங்கள் இல்லை"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"தேர்ந்தெடுத்ததைக் காட்டு"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"படங்கள்"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"ஆல்பங்கள்"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"மாதிரிக்காட்சி"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"பணிச் சுயவிவரத்திற்கு மாறு"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"தனிப்பட்ட சுயவிவரத்திற்கு மாறு"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"உங்கள் நிர்வாகி தடைசெய்துள்ளார்"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"தனிப்பட்ட ஆப்ஸிலிருந்து பணித் தரவை அணுக அனுமதியில்லை"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"பணி ஆப்ஸிலிருந்து தனிப்பட்ட தரவை அணுக அனுமதியில்லை"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"பணி ஆப்ஸ் இடைநிறுத்தப்பட்டுள்ளன"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"பணிப் படங்களைத் திறக்க, பணி ஆப்ஸை ஆன் செய்துவிட்டு முயலவும்"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"நீங்கள் தேர்ந்தெடுக்கும் படங்களை மட்டுமே இந்த ஆப்ஸ் அணுக முடியும்"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> ஆவணம்}other{<xliff:g id="COUNT_1">^1</xliff:g> ஆவணங்கள்}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"(<xliff:g id="COUNT">^1</xliff:g>) படங்களைச் சேர்"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"கேமரா"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"பதிவிறக்கங்கள்"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"பிடித்தவை"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"ஸ்கிரீன்ஷாட்டுகள்"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"அசையும் படம்"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> எடுக்கப்பட்ட நேரம்: <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"வீடியோ எடுக்கப்பட்ட நேரம்: <xliff:g id="TIME">%1$s</xliff:g>, வீடியோவின் கால அளவு: <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"படம்"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"அசையும் படம்"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"வீடியோவின் ஒலியை முடக்கும்"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"வீடியோவின் ஒலியை இயக்கும்"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"வீடியோவைப் பிளே செய்யும்"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"வீடியோவை இடைநிறுத்தும்"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"கிளவுட் மீடியா <xliff:g id="PKG_NAME">%1$s</xliff:g> ஆப்ஸில் தற்போது கிடைக்கிறது"</string>
+ <string name="not_selected" msgid="2244008151669896758">"தேர்ந்தெடுக்கப்படவில்லை"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{இந்த ஆடியோ ஃபைலில் மாற்றங்களைச் செய்ய <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}other{<xliff:g id="COUNT">^2</xliff:g> ஆடியோ ஃபைல்களில் மாற்றங்களைச் செய்ய <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{ஆடியோ ஃபைலை மாற்றியமைக்கிறது…}other{<xliff:g id="COUNT">^1</xliff:g> ஆடியோ ஃபைல்களை மாற்றியமைக்கிறது…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{இந்த வீடியோவில் மாற்றங்களைச் செய்ய <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}other{<xliff:g id="COUNT">^2</xliff:g> வீடியோக்களில் மாற்றங்களைச் செய்ய <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{வீடியோவை மாற்றியமைக்கிறது…}other{<xliff:g id="COUNT">^1</xliff:g> வீடியோக்களை மாற்றியமைக்கிறது…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{இந்தப் படத்தில் மாற்றங்களைச் செய்ய <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}other{<xliff:g id="COUNT">^2</xliff:g> படங்களில் மாற்றங்களைச் செய்ய <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{படத்தை மாற்றியமைக்கிறது…}other{<xliff:g id="COUNT">^1</xliff:g> படங்களை மாற்றியமைக்கிறது…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{இந்த ஆவணத்தில் மாற்றங்களைச் செய்ய <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}other{<xliff:g id="COUNT">^2</xliff:g> ஆவணங்களில் மாற்றங்களைச் செய்ய <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{ஆவணத்தை மாற்றியமைக்கிறது…}other{<xliff:g id="COUNT">^1</xliff:g> ஆவணங்களை மாற்றியமைக்கிறது…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{இந்த ஆடியோ ஃபைலை \'நீக்கியவை\' என்பதற்கு நகர்த்த <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}other{<xliff:g id="COUNT">^2</xliff:g> ஆடியோ ஃபைல்களை \'நீக்கியவை\' என்பதற்கு நகர்த்த <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{ஆடியோ ஃபைலை ‘நீக்கியவை’ என்பதற்கு நகர்த்துகிறது…}other{<xliff:g id="COUNT">^1</xliff:g> ஆடியோ ஃபைல்களை ‘நீக்கியவை’ என்பதற்கு நகர்த்துகிறது…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{இந்த வீடியோவை \'நீக்கியவை\' என்பதற்கு நகர்த்த <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}other{<xliff:g id="COUNT">^2</xliff:g> வீடியோக்களை \'நீக்கியவை\' என்பதற்கு நகர்த்த <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{வீடியோவை ‘நீக்கியவை’ என்பதற்கு நகர்த்துகிறது…}other{<xliff:g id="COUNT">^1</xliff:g> வீடியோக்களை ‘நீக்கியவை’ என்பதற்கு நகர்த்துகிறது…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{இந்தப் படத்தை \'நீக்கியவை\' என்பதற்கு நகர்த்த <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}other{<xliff:g id="COUNT">^2</xliff:g> படங்களை \'நீக்கியவை\' என்பதற்கு நகர்த்த <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{படத்தை ‘நீக்கியவை’ என்பதற்கு நகர்த்துகிறது…}other{<xliff:g id="COUNT">^1</xliff:g> படங்களை ‘நீக்கியவை’ என்பதற்கு நகர்த்துகிறது…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{இந்த ஆவணத்தை \'நீக்கியவை\' என்பதற்கு நகர்த்த <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}other{<xliff:g id="COUNT">^2</xliff:g> ஆவணங்களை \'நீக்கியவை\' என்பதற்கு நகர்த்த <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{ஆவணத்தை ‘நீக்கியவை’ என்பதற்கு நகர்த்துகிறது…}other{<xliff:g id="COUNT">^1</xliff:g> ஆவணங்களை ‘நீக்கியவை’ என்பதற்கு நகர்த்துகிறது…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{இந்த ஆடியோ ஃபைலை \'நீக்கியவை\' என்பதிலிருந்து நகர்த்த <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}other{<xliff:g id="COUNT">^2</xliff:g> ஆடியோ ஃபைல்களை \'நீக்கியவை\' என்பதிலிருந்து நகர்த்த <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{ஆடியோ ஃபைலை ‘நீக்கியவை’ என்பதிலிருந்து நகர்த்துகிறது…}other{<xliff:g id="COUNT">^1</xliff:g> ஆடியோ ஃபைல்களை ‘நீக்கியவை’ என்பதிலிருந்து நகர்த்துகிறது…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{இந்த வீடியோவை \'நீக்கியவை\' என்பதிலிருந்து நகர்த்த <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}other{<xliff:g id="COUNT">^2</xliff:g> வீடியோக்களை \'நீக்கியவை\' என்பதிலிருந்து நகர்த்த <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{வீடியோவை ‘நீக்கியவை’ என்பதிலிருந்து நகர்த்துகிறது…}other{<xliff:g id="COUNT">^1</xliff:g> வீடியோக்களை ‘நீக்கியவை’ என்பதிலிருந்து நகர்த்துகிறது…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{இந்தப் படத்தை \'நீக்கியவை\' என்பதிலிருந்து நகர்த்த <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}other{<xliff:g id="COUNT">^2</xliff:g> படங்களை \'நீக்கியவை\' என்பதிலிருந்து நகர்த்த <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{படத்தை ‘நீக்கியவை’ என்பதிலிருந்து நகர்த்துகிறது…}other{<xliff:g id="COUNT">^1</xliff:g> படங்களை ‘நீக்கியவை’ என்பதிலிருந்து நகர்த்துகிறது…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{இந்த ஆவணத்தை \'நீக்கியவை\' என்பதிலிருந்து நகர்த்த <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}other{<xliff:g id="COUNT">^2</xliff:g> ஆவணங்களை \'நீக்கியவை\' என்பதிலிருந்து நகர்த்த <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{ஆவணத்தை ‘நீக்கியவை’ என்பதிலிருந்து நகர்த்துகிறது…}other{<xliff:g id="COUNT">^1</xliff:g> ஆவணங்களை ‘நீக்கியவை’ என்பதிலிருந்து நகர்த்துகிறது…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{இந்த ஆடியோ ஃபைலை நீக்க <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}other{<xliff:g id="COUNT">^2</xliff:g> ஆடியோ ஃபைல்களை நீக்க <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{ஆடியோ ஃபைலை நீக்குகிறது…}other{<xliff:g id="COUNT">^1</xliff:g> ஆடியோ ஃபைல்களை நீக்குகிறது…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{இந்த வீடியோவை நீக்க <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}other{<xliff:g id="COUNT">^2</xliff:g> வீடியோக்களை நீக்க <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{வீடியோவை நீக்குகிறது…}other{<xliff:g id="COUNT">^1</xliff:g> வீடியோக்களை நீக்குகிறது…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{இந்தப் படத்தை நீக்க <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}other{<xliff:g id="COUNT">^2</xliff:g> படங்களை நீக்க <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{படத்தை நீக்குகிறது…}other{<xliff:g id="COUNT">^1</xliff:g> படங்களை நீக்குகிறது…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{இந்த ஆவணத்தை நீக்க <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}other{<xliff:g id="COUNT">^2</xliff:g> ஆவணங்களை நீக்க <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{ஆவணத்தை நீக்குகிறது…}other{<xliff:g id="COUNT">^1</xliff:g> ஆவணங்களை நீக்குகிறது…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"மீடியா ஃபைல்களை <xliff:g id="APP_NAME">%s</xliff:g> ஆப்ஸால் செயலாக்க முடியவில்லை"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"மீடியா செயலாக்கம் ரத்துசெய்யப்பட்டது"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"மீடியா செயலாக்கத்தில் பிழை"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 3d65365..337b0f9 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"మీడియా"</string>
<string name="storage_description" msgid="4081716890357580107">"స్థానిక నిల్వ"</string>
<string name="app_label" msgid="9035307001052716210">"మీడియా నిల్వ"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"ఫోటో పికర్"</string>
<string name="artist_label" msgid="8105600993099120273">"కళాకారుడు"</string>
<string name="unknown" msgid="2059049215682829375">"తెలియదు"</string>
<string name="root_images" msgid="5861633549189045666">"ఇమేజ్లు"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"కొనసాగించండి"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"అనుమతించండి"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"తిరస్కరించండి"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">దానితో పాటు, <xliff:g id="COUNT_1">^1</xliff:g> అదనపు అంశాలు</item>
- <item quantity="one">దానితో పాటు, <xliff:g id="COUNT_0">^1</xliff:g> అదనపు అంశం</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{దానితో పాటు, <xliff:g id="COUNT_0">^1</xliff:g> అదనపు ఐటెమ్}other{దానితో పాటు, <xliff:g id="COUNT_1">^1</xliff:g> అదనపు ఐటెమ్లు}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"తాత్కాలిక యాప్ ఫైళ్లను క్లియర్ చేయండి"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"కొన్ని తాత్కాలిక ఫైల్స్ను క్లియర్ చేయడానికి <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> అనుమతి కోరుతోంది. దీని వలన బ్యాటరీ లేదా సెల్యూలార్ డేటా వినియోగం పెరగవచ్చు."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"తాత్కాలిక యాప్ ఫైళ్లను క్లియర్ చేస్తోంది…"</string>
<string name="clear" msgid="5524638938415865915">"క్లియర్ చేయండి"</string>
<string name="allow" msgid="8885707816848569619">"అనుమతించండి"</string>
<string name="deny" msgid="6040983710442068936">"నిరాకరించండి"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ఆడియో ఫైళ్లను ఎడిట్ చేయడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?</item>
- <item quantity="one">ఈ ఆడియో ఫైల్ను ఎడిట్ చేయడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ఆడియో ఫైళ్లను సవరిస్తోంది…</item>
- <item quantity="one">ఆడియో ఫైల్ను సవరిస్తోంది…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> వీడియోలను ఎడిట్ చేయడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?</item>
- <item quantity="one">ఈ వీడియోను ఎడిట్ చేయడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> వీడియోలను సవరిస్తోంది…</item>
- <item quantity="one">వీడియోను సవరిస్తోంది…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ఫోటోలను ఎడిట్ చేయడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?</item>
- <item quantity="one">ఈ ఫోటోను ఎడిట్ చేయడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ఫోటోలను సవరిస్తోంది…</item>
- <item quantity="one">ఫోటోను సవరిస్తోంది…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ఐటెమ్లను ఎడిట్ చేయడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?</item>
- <item quantity="one">ఈ ఐటెమ్ను ఎడిట్ చేయడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ఐటెమ్లను సవరిస్తోంది…</item>
- <item quantity="one">ఐటెమ్ను సవరిస్తోంది…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ఆడియో ఫైళ్లను ట్రాష్కు తరలించడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?</item>
- <item quantity="one">ఈ ఆడియో ఫైల్ను ట్రాష్కు తరలించడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ఆడియో ఫైళ్లను ట్రాష్కు తరలిస్తోంది…</item>
- <item quantity="one">ఆడియో ఫైల్ను ట్రాష్కు తరలిస్తోంది…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> వీడియోలను ట్రాష్కు తరలించడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?</item>
- <item quantity="one">ఈ వీడియోను ట్రాష్కు తరలించడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> వీడియోలను ట్రాష్కు తరలిస్తోంది…</item>
- <item quantity="one">వీడియోను ట్రాష్కు తరలిస్తోంది…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ఫోటోలను ట్రాష్కు తరలించడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?</item>
- <item quantity="one">ఈ ఫోటోను ట్రాష్కు తరలించడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ఫోటోలను ట్రాష్కు తరలిస్తోంది…</item>
- <item quantity="one">ఫోటోను ట్రాష్కు తరలిస్తోంది…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ఐటెమ్లను ట్రాష్కు తరలించడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?</item>
- <item quantity="one">ఈ ఐటెమ్ను ట్రాష్కు తరలించడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ఐటెమ్లను ట్రాష్కు తరలిస్తోంది…</item>
- <item quantity="one">ఐటెమ్ను ట్రాష్కు తరలిస్తోంది…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ఆడియో ఫైళ్లను ట్రాష్ నుండి బయటకు తీయడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?</item>
- <item quantity="one">ఈ ఆడియో ఫైల్ను ట్రాష్ నుండి బయటకు తీయడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ఆడియో ఫైళ్లను ట్రాష్ నుండి బయటకు తరలిస్తోంది…</item>
- <item quantity="one">ఆడియో ఫైల్ను ట్రాష్ నుండి బయటకు తరలిస్తోంది…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> వీడియోలను ట్రాష్ నుండి బయటకు తీయడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?</item>
- <item quantity="one">ఈ వీడియోను ట్రాష్ నుండి బయటకు తీయడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> వీడియోలను ట్రాష్ నుండి బయటకు తరలిస్తోంది…</item>
- <item quantity="one">వీడియోను ట్రాష్ నుండి బయటకు తరలిస్తోంది…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ఫోటోలను ట్రాష్ నుండి బయటకు తీయడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?</item>
- <item quantity="one">ఈ ఫోటోను ట్రాష్ నుండి బయటకు తీయడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ఫోటోలను ట్రాష్ నుండి బయటకు తరలిస్తోంది…</item>
- <item quantity="one">ఫోటోను ట్రాష్ నుండి బయటకు తరలిస్తోంది…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ఐటెమ్లను ట్రాష్ నుండి బయటకు తీయడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?</item>
- <item quantity="one">ఈ ఐటెమ్ను ట్రాష్ నుండి బయటకు తీయడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ఐటెమ్లను ట్రాష్ నుండి బయటకు తరలిస్తోంది…</item>
- <item quantity="one">ఐటెమ్ను ట్రాష్ నుండి బయటకు తరలిస్తోంది…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ఆడియో ఫైళ్లను తొలగించడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?</item>
- <item quantity="one">ఈ ఆడియో ఫైల్ను తొలగించడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g>ఆడియో ఫైళ్లను తొలగిస్తోంది…</item>
- <item quantity="one">ఆడియో ఫైల్ను తొలగిస్తోంది…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> వీడియోలను తొలగించడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?</item>
- <item quantity="one">ఈ వీడియోను తొలగించడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> వీడియోలను తొలగిస్తోంది…</item>
- <item quantity="one">వీడియోను తొలగిస్తోంది…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ఫోటోలను తొలగించడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?</item>
- <item quantity="one">ఈ ఫోటోను తొలగించడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ఫోటోలను తొలగిస్తోంది…</item>
- <item quantity="one">ఫోటోను తొలగిస్తోంది…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ఐటెమ్లను తొలగించడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?</item>
- <item quantity="one">ఈ ఐటెమ్ను తొలగించడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ఐటెమ్లను తొలగిస్తోంది…</item>
- <item quantity="one">ఐటెమ్ను తొలగిస్తోంది…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"జోడించు"</string>
+ <string name="deselect" msgid="4297825044827769490">"ఎంపికను తొలగించండి"</string>
+ <string name="deselected" msgid="8488133193326208475">"ఎంపికను తొలగించండి"</string>
+ <string name="select" msgid="2704765470563027689">"ఎంచుకోండి"</string>
+ <string name="selected" msgid="9151797369975828124">"ఎంచుకోబడింది"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{గరిష్ఠంగా <xliff:g id="COUNT_0">^1</xliff:g> ఐటెమ్ను ఎంచుకోండి}other{గరిష్ఠంగా <xliff:g id="COUNT_1">^1</xliff:g> ఐటెమ్లను ఎంచుకోండి}}"</string>
+ <string name="recent" msgid="6694613584743207874">"ఇటీవలివి"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"ఫోటోలు లేదా వీడియోలు లేవు"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"ఆల్బమ్లు ఏవీ లేవు"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"ఎంచుకున్న వాటిని చూడండి"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"ఫోటోలు"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"ఆల్బమ్లు"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"ప్రివ్యూ"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"ఆఫీస్ ప్రొఫైల్కు మార్చండి"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"వ్యక్తిగత ప్రొఫైల్కు మార్చండి"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"మీ అడ్మిన్ బ్లాక్ చేశారు"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"వ్యక్తిగత యాప్ నుండి వర్క్ డేటాను యాక్సెస్ చేయడం అనుమతించబడదు"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"వర్క్ యాప్ నుండి వ్యక్తిగత డేటాను యాక్సెస్ చేయడం అనుమతించబడదు"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"వర్క్ యాప్లు పాజ్ చేయబడ్డాయి"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"వర్క్ ఫోటోలను తెరవడానికి, మీ వర్క్ యాప్లను ఆన్ చేసి, ఆపై మళ్లీ ట్రై చేయండి"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"ఈ యాప్ మీరు ఎంచుకున్న ఫోటోలను మాత్రమే యాక్సెస్ చేయగలదు"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> ఐటెమ్}other{<xliff:g id="COUNT_1">^1</xliff:g> ఐటెమ్లు}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"జోడించండి (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"కెమెరా"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"డౌన్లోడ్లు"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"ఫేవరెట్స్"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"స్క్రీన్షాట్లు"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"మోషన్ ఫోటో"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="TIME">%2$s</xliff:g> సమయంలో తీసిన <xliff:g id="ITEM_NAME">%1$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"<xliff:g id="DURATION">%2$s</xliff:g> వ్యవధితో <xliff:g id="TIME">%1$s</xliff:g>న తీసిన వీడియో"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"ఫోటో"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"మోషన్ ఫోటో"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"వీడియోను మ్యూట్ చేయండి"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"వీడియోను అన్మ్యూట్ చేయండి"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"వీడియోను ప్లే చేయండి"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"వీడియోను పాజ్ చేయండి"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"క్లౌడ్ మీడియా ఇప్పుడు <xliff:g id="PKG_NAME">%1$s</xliff:g> నుండి అందుబాటులో ఉంది"</string>
+ <string name="not_selected" msgid="2244008151669896758">"ఎంచుకోబడలేదు"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{ఈ ఆడియో ఫైల్ను ఎడిట్ చేయడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?}other{<xliff:g id="COUNT">^2</xliff:g> ఆడియో ఫైళ్లను ఎడిట్ చేయడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{ఆడియో ఫైల్ను సవరిస్తోంది…}other{<xliff:g id="COUNT">^1</xliff:g> ఆడియో ఫైళ్లను సవరిస్తోంది…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{ఈ వీడియోను ఎడిట్ చేయడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?}other{<xliff:g id="COUNT">^2</xliff:g> వీడియోలను ఎడిట్ చేయడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{వీడియోను సవరిస్తోంది…}other{<xliff:g id="COUNT">^1</xliff:g> వీడియోలను సవరిస్తోంది…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{ఈ ఫోటోను ఎడిట్ చేయడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?}other{<xliff:g id="COUNT">^2</xliff:g> ఫోటోలను ఎడిట్ చేయడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{ఫోటోను సవరిస్తోంది…}other{<xliff:g id="COUNT">^1</xliff:g> ఫోటోలను సవరిస్తోంది…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{ఈ ఐటెమ్ను ఎడిట్ చేయడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?}other{<xliff:g id="COUNT">^2</xliff:g> ఐటెమ్లను ఎడిట్ చేయడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{ఐటెమ్ను సవరిస్తోంది…}other{<xliff:g id="COUNT">^1</xliff:g> ఐటెమ్లను సవరిస్తోంది…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{ఈ ఆడియో ఫైల్ను ట్రాష్కు తరలించడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?}other{<xliff:g id="COUNT">^2</xliff:g> ఆడియో ఫైళ్లను ట్రాష్కు తరలించడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{ఆడియో ఫైల్ను ట్రాష్కు తరలిస్తోంది…}other{<xliff:g id="COUNT">^1</xliff:g> ఆడియో ఫైళ్లను ట్రాష్కు తరలిస్తోంది…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{ఈ వీడియోను ట్రాష్కు తరలించడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?}other{<xliff:g id="COUNT">^2</xliff:g> వీడియోలను ట్రాష్కు తరలించడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{వీడియోను ట్రాష్కు తరలిస్తోంది…}other{<xliff:g id="COUNT">^1</xliff:g> వీడియోలను ట్రాష్కు తరలిస్తోంది…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{ఈ ఫోటోను ట్రాష్కు తరలించడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?}other{<xliff:g id="COUNT">^2</xliff:g> ఫోటోలను ట్రాష్కు తరలించడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{ఫోటోను ట్రాష్కు తరలిస్తోంది…}other{<xliff:g id="COUNT">^1</xliff:g> ఫోటోలను ట్రాష్కు తరలిస్తోంది…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{ఈ ఐటెమ్ను ట్రాష్కు తరలించడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?}other{<xliff:g id="COUNT">^2</xliff:g> ఐటెమ్లను ట్రాష్కు తరలించడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{ఐటెమ్ను ట్రాష్కు తరలిస్తోంది…}other{<xliff:g id="COUNT">^1</xliff:g> ఐటెమ్లను ట్రాష్కు తరలిస్తోంది…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{ఈ ఆడియో ఫైల్ను ట్రాష్ నుండి బయటకు తీయడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?}other{<xliff:g id="COUNT">^2</xliff:g> ఆడియో ఫైళ్లను ట్రాష్ నుండి బయటకు తీయడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{ఆడియో ఫైల్ను ట్రాష్ నుండి బయటకు తరలిస్తోంది…}other{<xliff:g id="COUNT">^1</xliff:g> ఆడియో ఫైళ్లను ట్రాష్ నుండి బయటకు తరలిస్తోంది…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{ఈ వీడియోను ట్రాష్ నుండి బయటకు తీయడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?}other{<xliff:g id="COUNT">^2</xliff:g> వీడియోలను ట్రాష్ నుండి బయటకు తీయడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{వీడియోను ట్రాష్ నుండి బయటకు తరలిస్తోంది…}other{<xliff:g id="COUNT">^1</xliff:g> వీడియోలను ట్రాష్ నుండి బయటకు తరలిస్తోంది…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{ఈ ఫోటోను ట్రాష్ నుండి బయటకు తీయడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?}other{<xliff:g id="COUNT">^2</xliff:g> ఫోటోలను ట్రాష్ నుండి బయటకు తీయడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{ఫోటోను ట్రాష్ నుండి బయటకు తరలిస్తోంది…}other{<xliff:g id="COUNT">^1</xliff:g> ఫోటోలను ట్రాష్ నుండి బయటకు తరలిస్తోంది…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{ఈ ఐటెమ్ను ట్రాష్ నుండి బయటకు తీయడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?}other{<xliff:g id="COUNT">^2</xliff:g> ఐటెమ్లను ట్రాష్ నుండి బయటకు తీయడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{ఐటెమ్ను ట్రాష్ నుండి బయటకు తరలిస్తోంది…}other{<xliff:g id="COUNT">^1</xliff:g> ఐటెమ్లను ట్రాష్ నుండి బయటకు తరలిస్తోంది…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{ఈ ఆడియో ఫైల్ను తొలగించడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?}other{<xliff:g id="COUNT">^2</xliff:g> ఆడియో ఫైళ్లను తొలగించడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{ఆడియో ఫైల్ను తొలగిస్తోంది…}other{<xliff:g id="COUNT">^1</xliff:g> ఆడియో ఫైళ్లను తొలగిస్తోంది…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{ఈ వీడియోను తొలగించడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?}other{<xliff:g id="COUNT">^2</xliff:g> వీడియోలను తొలగించడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{వీడియోను తొలగిస్తోంది…}other{<xliff:g id="COUNT">^1</xliff:g> వీడియోలను తొలగిస్తోంది…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{ఈ ఫోటోను తొలగించడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?}other{<xliff:g id="COUNT">^2</xliff:g> ఫోటోలను తొలగించడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{ఫోటోను తొలగిస్తోంది…}other{<xliff:g id="COUNT">^1</xliff:g> ఫోటోలను తొలగిస్తోంది…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{ఈ ఐటెమ్ను తొలగించడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ఐటెమ్లను తొలగించడానికి <xliff:g id="COUNT">^2</xliff:g>ను అనుమతించాలా?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{ఐటెమ్ను తొలగిస్తోంది…}other{<xliff:g id="COUNT">^1</xliff:g> ఐటెమ్లను తొలగిస్తోంది…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> మీడియా ఫైళ్లను ప్రాసెస్ చేయదు"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"మీడియా ప్రాసెసింగ్ రద్దు చేయబడింది"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"మీడియా ప్రాసెసింగ్లో ఎర్రర్ ఏర్పడింది"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index da8604c..9906f64 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"สื่อ"</string>
<string name="storage_description" msgid="4081716890357580107">"พื้นที่เก็บข้อมูลในเครื่อง"</string>
<string name="app_label" msgid="9035307001052716210">"พื้นที่เก็บข้อมูลสื่อ"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"เครื่องมือเลือกรูปภาพ"</string>
<string name="artist_label" msgid="8105600993099120273">"ศิลปิน"</string>
<string name="unknown" msgid="2059049215682829375">"ไม่ทราบ"</string>
<string name="root_images" msgid="5861633549189045666">"รูปภาพ"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"ต่อไป"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"อนุญาต"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"ปฏิเสธ"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">และอีก <xliff:g id="COUNT_1">^1</xliff:g> รายการ</item>
- <item quantity="one">และอีก <xliff:g id="COUNT_0">^1</xliff:g> รายการ</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{และอีก <xliff:g id="COUNT_0">^1</xliff:g> รายการ}other{และอีก <xliff:g id="COUNT_1">^1</xliff:g> รายการ}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"ล้างไฟล์แอปชั่วคราว"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ต้องการล้างไฟล์ชั่วคราวบางไฟล์ ซึ่งอาจส่งผลให้ใช้แบตเตอรี่หรืออินเทอร์เน็ตมือถือมากขึ้น"</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"กำลังล้างไฟล์แอปชั่วคราว…"</string>
<string name="clear" msgid="5524638938415865915">"ล้าง"</string>
<string name="allow" msgid="8885707816848569619">"อนุญาต"</string>
<string name="deny" msgid="6040983710442068936">"ปฏิเสธ"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> แก้ไขไฟล์เสียง <xliff:g id="COUNT">^2</xliff:g> ไฟล์ไหม</item>
- <item quantity="one">อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> แก้ไขไฟล์เสียงนี้ไหม</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">กำลังแก้ไขไฟล์เสียง <xliff:g id="COUNT">^1</xliff:g> ไฟล์…</item>
- <item quantity="one">กำลังแก้ไขไฟล์เสียง…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> แก้ไขวิดีโอ <xliff:g id="COUNT">^2</xliff:g> รายการไหม</item>
- <item quantity="one">อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> แก้ไขวิดีโอนี้ไหม</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">กำลังแก้ไขวิดีโอ <xliff:g id="COUNT">^1</xliff:g> รายการ…</item>
- <item quantity="one">กำลังแก้ไขวิดีโอ…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> แก้ไขรูปภาพ <xliff:g id="COUNT">^2</xliff:g> รูปไหม</item>
- <item quantity="one">อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> แก้ไขรูปภาพนี้ไหม</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">กำลังแก้ไขรูปภาพ <xliff:g id="COUNT">^1</xliff:g> รูป…</item>
- <item quantity="one">กำลังแก้ไขรูปภาพ…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> แก้ไข <xliff:g id="COUNT">^2</xliff:g> รายการไหม</item>
- <item quantity="one">อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> แก้ไขรายการนี้ไหม</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">กำลังแก้ไข <xliff:g id="COUNT">^1</xliff:g> รายการ…</item>
- <item quantity="one">กำลังแก้ไขรายการ…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> ย้ายไฟล์เสียง <xliff:g id="COUNT">^2</xliff:g> ไฟล์ไปที่ถังขยะไหม</item>
- <item quantity="one">อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> ย้ายไฟล์เสียงนี้ไปที่ถังขยะไหม</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">กำลังย้ายไฟล์เสียง <xliff:g id="COUNT">^1</xliff:g> ไฟล์ไปที่ถังขยะ…</item>
- <item quantity="one">กำลังย้ายไฟล์เสียงไปที่ถังขยะ…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> ย้ายวิดีโอ <xliff:g id="COUNT">^2</xliff:g> รายการไปที่ถังขยะไหม</item>
- <item quantity="one">อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> ย้ายวิดีโอนี้ไปที่ถังขยะไหม</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">กำลังย้ายวิดีโอ <xliff:g id="COUNT">^1</xliff:g> รายการไปที่ถังขยะ…</item>
- <item quantity="one">กำลังย้ายวิดีโอไปที่ถังขยะ…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> ย้ายรูปภาพ <xliff:g id="COUNT">^2</xliff:g> รูปไปที่ถังขยะไหม</item>
- <item quantity="one">อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> ย้ายรูปภาพนี้ไปที่ถังขยะไหม</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">กำลังย้ายรูปภาพ <xliff:g id="COUNT">^1</xliff:g> รูปไปที่ถังขยะ…</item>
- <item quantity="one">กำลังย้ายรูปภาพไปที่ถังขยะ…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> ย้าย <xliff:g id="COUNT">^2</xliff:g> รายการไปที่ถังขยะไหม</item>
- <item quantity="one">อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> ย้ายรายการนี้ไปที่ถังขยะไหม</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">กำลังย้าย <xliff:g id="COUNT">^1</xliff:g> รายการไปที่ถังขยะ…</item>
- <item quantity="one">กำลังย้ายรายการไปที่ถังขยะ…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> ย้ายไฟล์เสียง <xliff:g id="COUNT">^2</xliff:g> ไฟล์ออกจากถังขยะไหม</item>
- <item quantity="one">อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> ย้ายไฟล์เสียงนี้ออกจากถังขยะไหม</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">กำลังย้ายไฟล์เสียง <xliff:g id="COUNT">^1</xliff:g> ไฟล์ออกจากถังขยะ…</item>
- <item quantity="one">กำลังย้ายไฟล์เสียงออกจากถังขยะ…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> ย้ายวิดีโอ <xliff:g id="COUNT">^2</xliff:g> รายการออกจากถังขยะไหม</item>
- <item quantity="one">อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> ย้ายวิดีโอนี้ออกจากถังขยะไหม</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">กำลังย้ายวิดีโอ <xliff:g id="COUNT">^1</xliff:g> รายการออกจากถังขยะ…</item>
- <item quantity="one">กำลังย้ายวิดีโอออกจากถังขยะ…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> ย้ายรูปภาพ <xliff:g id="COUNT">^2</xliff:g> รูปออกจากถังขยะไหม</item>
- <item quantity="one">อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> ย้ายรูปภาพนี้ออกจากถังขยะไหม</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">กำลังย้ายรูปภาพ <xliff:g id="COUNT">^1</xliff:g> รูปออกจากถังขยะ…</item>
- <item quantity="one">กำลังย้ายรูปภาพออกจากถังขยะ…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> ย้าย <xliff:g id="COUNT">^2</xliff:g> รายการออกจากถังขยะไหม</item>
- <item quantity="one">อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> ย้ายรายการนี้ออกจากถังขยะไหม</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">กำลังย้าย <xliff:g id="COUNT">^1</xliff:g> รายการออกจากถังขยะ…</item>
- <item quantity="one">กำลังย้ายรายการออกจากถังขยะ…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> ลบไฟล์เสียง <xliff:g id="COUNT">^2</xliff:g> ไฟล์ไหม</item>
- <item quantity="one">อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> ลบไฟล์เสียงนี้ไหม</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">กำลังลบไฟล์เสียง <xliff:g id="COUNT">^1</xliff:g> ไฟล์…</item>
- <item quantity="one">กำลังลบไฟล์เสียง…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> ลบวิดีโอ <xliff:g id="COUNT">^2</xliff:g> รายการไหม</item>
- <item quantity="one">อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> ลบวิดีโอนี้ไหม</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">กำลังลบวิดีโอ <xliff:g id="COUNT">^1</xliff:g> รายการ…</item>
- <item quantity="one">กำลังลบวิดีโอ…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> ลบรูปภาพ <xliff:g id="COUNT">^2</xliff:g> รูปไหม</item>
- <item quantity="one">อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> ลบรูปภาพนี้ไหม</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">กำลังลบรูปภาพ <xliff:g id="COUNT">^1</xliff:g> รูป…</item>
- <item quantity="one">กำลังลบรูปภาพ…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> ลบ <xliff:g id="COUNT">^2</xliff:g> รายการไหม</item>
- <item quantity="one">อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> ลบรายการนี้ไหม</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">กำลังลบ <xliff:g id="COUNT">^1</xliff:g> รายการ…</item>
- <item quantity="one">กำลังลบรายการ…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"เพิ่ม"</string>
+ <string name="deselect" msgid="4297825044827769490">"ยกเลิกการเลือก"</string>
+ <string name="deselected" msgid="8488133193326208475">"ยกเลิกการเลือก"</string>
+ <string name="select" msgid="2704765470563027689">"เลือก"</string>
+ <string name="selected" msgid="9151797369975828124">"เลือก"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{เลือกได้สูงสุด <xliff:g id="COUNT_0">^1</xliff:g> รายการ}other{เลือกได้สูงสุด <xliff:g id="COUNT_1">^1</xliff:g> รายการ}}"</string>
+ <string name="recent" msgid="6694613584743207874">"ล่าสุด"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"ไม่มีรูปภาพหรือวิดีโอ"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"ไม่มีอัลบั้ม"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"ดูรายการที่เลือก"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"รูปภาพ"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"อัลบั้ม"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"ตัวอย่าง"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"เปลี่ยนไปใช้โปรไฟล์งาน"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"เปลี่ยนไปใช้โปรไฟล์ส่วนตัว"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"ผู้ดูแลระบบบล็อกไว้"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"ไม่อนุญาตให้เข้าถึงข้อมูลงานจากแอปส่วนตัว"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"ไม่อนุญาตให้เข้าถึงข้อมูลส่วนตัวจากแอปงาน"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"แอปงานหยุดชั่วคราว"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"หากต้องการเปิดรูปภาพงาน ให้เปิดแอปงานแล้วลองอีกครั้ง"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"แอปนี้จะเข้าถึงได้เฉพาะรูปภาพที่คุณเลือกเท่านั้น"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> รายการ}other{<xliff:g id="COUNT_1">^1</xliff:g> รายการ}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"เพิ่ม (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"กล้อง"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"การดาวน์โหลด"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"รายการโปรด"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"ภาพหน้าจอ"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"รูปภาพเคลื่อนไหว"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> ถ่ายเมื่อ <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"ถ่ายวิดีโอไว้เมื่อ <xliff:g id="TIME">%1$s</xliff:g> โดยมีระยะเวลา <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"รูปภาพ"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"รูปภาพเคลื่อนไหว"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"ปิดเสียงวิดีโอ"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"เปิดเสียงวิดีโอ"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"เล่นวิดีโอ"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"หยุดวิดีโอชั่วคราว"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"ไฟล์สื่อจาก <xliff:g id="PKG_NAME">%1$s</xliff:g> ในระบบคลาวด์พร้อมให้ใช้งานแล้ว"</string>
+ <string name="not_selected" msgid="2244008151669896758">"ไม่ได้เลือกไว้"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> แก้ไขไฟล์เสียงนี้ไหม}other{อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> แก้ไขไฟล์เสียง <xliff:g id="COUNT">^2</xliff:g> ไฟล์ไหม}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{กำลังแก้ไขไฟล์เสียง…}other{กำลังแก้ไขไฟล์เสียง <xliff:g id="COUNT">^1</xliff:g> ไฟล์…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> แก้ไขวิดีโอนี้ไหม}other{อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> แก้ไขวิดีโอ <xliff:g id="COUNT">^2</xliff:g> รายการไหม}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{กำลังแก้ไขวิดีโอ…}other{กำลังแก้ไขวิดีโอ <xliff:g id="COUNT">^1</xliff:g> รายการ…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> แก้ไขรูปภาพนี้ไหม}other{อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> แก้ไขรูปภาพ <xliff:g id="COUNT">^2</xliff:g> รูปไหม}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{กำลังแก้ไขรูปภาพ…}other{กำลังแก้ไขรูปภาพ <xliff:g id="COUNT">^1</xliff:g> รูป…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> แก้ไขรายการนี้ไหม}other{อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> แก้ไข <xliff:g id="COUNT">^2</xliff:g> รายการไหม}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{กำลังแก้ไขรายการ…}other{กำลังแก้ไข <xliff:g id="COUNT">^1</xliff:g> รายการ…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> ย้ายไฟล์เสียงนี้ไปที่ถังขยะไหม}other{อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> ย้ายไฟล์เสียง <xliff:g id="COUNT">^2</xliff:g> ไฟล์ไปที่ถังขยะไหม}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{กำลังย้ายไฟล์เสียงไปที่ถังขยะ…}other{กำลังย้ายไฟล์เสียง <xliff:g id="COUNT">^1</xliff:g> ไฟล์ไปที่ถังขยะ…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> ย้ายวิดีโอนี้ไปที่ถังขยะไหม}other{อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> ย้ายวิดีโอ <xliff:g id="COUNT">^2</xliff:g> รายการไปที่ถังขยะไหม}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{กำลังย้ายวิดีโอไปที่ถังขยะ…}other{กำลังย้ายวิดีโอ <xliff:g id="COUNT">^1</xliff:g> รายการไปที่ถังขยะ…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> ย้ายรูปภาพนี้ไปที่ถังขยะไหม}other{อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> ย้ายรูปภาพ <xliff:g id="COUNT">^2</xliff:g> รูปไปที่ถังขยะไหม}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{กำลังย้ายรูปภาพไปที่ถังขยะ…}other{กำลังย้ายรูปภาพ <xliff:g id="COUNT">^1</xliff:g> รูปไปที่ถังขยะ…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> ย้ายรายการนี้ไปที่ถังขยะไหม}other{อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> ย้าย <xliff:g id="COUNT">^2</xliff:g> รายการไปที่ถังขยะไหม}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{กำลังย้ายรายการไปที่ถังขยะ…}other{กำลังย้าย <xliff:g id="COUNT">^1</xliff:g> รายการไปที่ถังขยะ…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> ย้ายไฟล์เสียงนี้ออกจากถังขยะไหม}other{อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> ย้ายไฟล์เสียง <xliff:g id="COUNT">^2</xliff:g> ไฟล์ออกจากถังขยะไหม}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{กำลังย้ายไฟล์เสียงออกจากถังขยะ…}other{กำลังย้ายไฟล์เสียง <xliff:g id="COUNT">^1</xliff:g> ไฟล์ออกจากถังขยะ…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> ย้ายวิดีโอนี้ออกจากถังขยะไหม}other{อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> ย้ายวิดีโอ <xliff:g id="COUNT">^2</xliff:g> รายการออกจากถังขยะไหม}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{กำลังย้ายวิดีโอออกจากถังขยะ…}other{กำลังย้ายวิดีโอ <xliff:g id="COUNT">^1</xliff:g> รายการออกจากถังขยะ…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> ย้ายรูปภาพนี้ออกจากถังขยะไหม}other{อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> ย้ายรูปภาพ <xliff:g id="COUNT">^2</xliff:g> รูปออกจากถังขยะไหม}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{กำลังย้ายรูปภาพออกจากถังขยะ…}other{กำลังย้ายรูปภาพ <xliff:g id="COUNT">^1</xliff:g> รูปออกจากถังขยะ…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> ย้ายรายการนี้ออกจากถังขยะไหม}other{อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> ย้าย <xliff:g id="COUNT">^2</xliff:g> รายการออกจากถังขยะไหม}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{กำลังย้ายรายการออกจากถังขยะ…}other{กำลังย้าย <xliff:g id="COUNT">^1</xliff:g> รายการออกจากถังขยะ…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> ลบไฟล์เสียงนี้ไหม}other{อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> ลบไฟล์เสียง <xliff:g id="COUNT">^2</xliff:g> ไฟล์ไหม}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{กำลังลบไฟล์เสียง…}other{กำลังลบไฟล์เสียง <xliff:g id="COUNT">^1</xliff:g> ไฟล์…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> ลบวิดีโอนี้ไหม}other{อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> ลบวิดีโอ <xliff:g id="COUNT">^2</xliff:g> รายการไหม}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{กำลังลบวิดีโอ…}other{กำลังลบวิดีโอ <xliff:g id="COUNT">^1</xliff:g> รายการ…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> ลบรูปภาพนี้ไหม}other{อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> ลบรูปภาพ <xliff:g id="COUNT">^2</xliff:g> รูปไหม}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{กำลังลบรูปภาพ…}other{กำลังลบรูปภาพ <xliff:g id="COUNT">^1</xliff:g> รูป…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{อนุญาตให้ <xliff:g id="APP_NAME_0">^1</xliff:g> ลบรายการนี้ไหม}other{อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> ลบ <xliff:g id="COUNT">^2</xliff:g> รายการไหม}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{กำลังลบรายการ…}other{กำลังลบ <xliff:g id="COUNT">^1</xliff:g> รายการ…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g>ประมวลผลไฟล์สื่อไม่ได้"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"ยกเลิกการประมวลผลสื่อแล้ว"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"ข้อผิดพลาดในการประมวลผลสื่อ"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 804e99e..25a169c 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Picker ng larawan"</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>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Magpatuloy"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Payagan"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Tanggihan"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">At <xliff:g id="COUNT_1">^1</xliff:g> pang item</item>
- <item quantity="other">At <xliff:g id="COUNT_1">^1</xliff:g> pang item</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}one{+<xliff:g id="COUNT_1">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{At <xliff:g id="COUNT_0">^1</xliff:g> pang item}one{At <xliff:g id="COUNT_1">^1</xliff:g> pang item}other{At <xliff:g id="COUNT_1">^1</xliff:g> pang item}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"I-clear ang mga pansamantalang file ng app"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"Gustong mag-clear ng <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ng ilang pansamantalang file. Baka lumakas ang paggamit ng baterya o cellular data dahil dito."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Kini-clear ang mga pansamantalang file ng app…"</string>
<string name="clear" msgid="5524638938415865915">"I-clear"</string>
<string name="allow" msgid="8885707816848569619">"Payagan"</string>
<string name="deny" msgid="6040983710442068936">"Tanggihan"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na i-modify ang <xliff:g id="COUNT">^2</xliff:g> audio file?</item>
- <item quantity="other">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na i-modify ang <xliff:g id="COUNT">^2</xliff:g> na audio file?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one">Nagbabago ng <xliff:g id="COUNT">^1</xliff:g> audio file…</item>
- <item quantity="other">Nagbabago ng <xliff:g id="COUNT">^1</xliff:g> na audio file…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na baguhin ang <xliff:g id="COUNT">^2</xliff:g> video?</item>
- <item quantity="other">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na baguhin ang <xliff:g id="COUNT">^2</xliff:g> na video?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one">Nagbabago ng <xliff:g id="COUNT">^1</xliff:g> video…</item>
- <item quantity="other">Nagbabago ng <xliff:g id="COUNT">^1</xliff:g> na video…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na baguhin ang <xliff:g id="COUNT">^2</xliff:g> larawan?</item>
- <item quantity="other">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na baguhin ang <xliff:g id="COUNT">^2</xliff:g> na larawan?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one">Nagbabago ng <xliff:g id="COUNT">^1</xliff:g> larawan…</item>
- <item quantity="other">Nagbabago ng <xliff:g id="COUNT">^1</xliff:g> na larawan…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na baguhin ang <xliff:g id="COUNT">^2</xliff:g> item?</item>
- <item quantity="other">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na baguhin ang <xliff:g id="COUNT">^2</xliff:g> na item?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one">Nagbabago ng <xliff:g id="COUNT">^1</xliff:g> item…</item>
- <item quantity="other">Nagbabago ng <xliff:g id="COUNT">^1</xliff:g> na item…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na ilipat sa trash ang <xliff:g id="COUNT">^2</xliff:g> audio file?</item>
- <item quantity="other">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na ilipat sa trash ang <xliff:g id="COUNT">^2</xliff:g> na audio file?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one">Naglilipat ng <xliff:g id="COUNT">^1</xliff:g> audio file sa trash…</item>
- <item quantity="other">Naglilipat ng <xliff:g id="COUNT">^1</xliff:g> na audio file sa trash…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na ilipat sa trash ang <xliff:g id="COUNT">^2</xliff:g> video?</item>
- <item quantity="other">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na ilipat sa trash ang <xliff:g id="COUNT">^2</xliff:g> na video?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one">Naglilipat ng <xliff:g id="COUNT">^1</xliff:g> video sa trash…</item>
- <item quantity="other">Naglilipat ng <xliff:g id="COUNT">^1</xliff:g> na video sa trash…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na ilipat sa trash ang <xliff:g id="COUNT">^2</xliff:g> larawan?</item>
- <item quantity="other">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na ilipat sa trash ang <xliff:g id="COUNT">^2</xliff:g> na larawan?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one">Naglilipat ng <xliff:g id="COUNT">^1</xliff:g> larawan sa trash…</item>
- <item quantity="other">Naglilipat ng <xliff:g id="COUNT">^1</xliff:g> na larawan sa trash…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na ilipat sa trash ang <xliff:g id="COUNT">^2</xliff:g> item?</item>
- <item quantity="other">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na ilipat sa trash ang <xliff:g id="COUNT">^2</xliff:g> na item?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one">Naglilipat ng <xliff:g id="COUNT">^1</xliff:g> item sa trash…</item>
- <item quantity="other">Naglilipat ng <xliff:g id="COUNT">^1</xliff:g> na item sa trash…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na alisin sa trash ang <xliff:g id="COUNT">^2</xliff:g> audio file?</item>
- <item quantity="other">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na alisin sa trash ang <xliff:g id="COUNT">^2</xliff:g> na audio file?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one">Nag-aalis ng <xliff:g id="COUNT">^1</xliff:g> audio file sa trash…</item>
- <item quantity="other">Nag-aalis ng <xliff:g id="COUNT">^1</xliff:g> na audio file sa trash…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na alisin sa trash ang <xliff:g id="COUNT">^2</xliff:g> video?</item>
- <item quantity="other">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na alisin sa trash ang <xliff:g id="COUNT">^2</xliff:g> na video?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one">Nag-aalis ng <xliff:g id="COUNT">^1</xliff:g> video sa trash…</item>
- <item quantity="other">Nag-aalis ng <xliff:g id="COUNT">^1</xliff:g> na video sa trash…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na alisin sa trash ang <xliff:g id="COUNT">^2</xliff:g> larawan?</item>
- <item quantity="other">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na alisin sa trash ang <xliff:g id="COUNT">^2</xliff:g> na larawan?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one">Nag-aalis ng <xliff:g id="COUNT">^1</xliff:g> larawan sa trash…</item>
- <item quantity="other">Nag-aalis ng <xliff:g id="COUNT">^1</xliff:g> na larawan sa trash…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na alisin sa trash ang <xliff:g id="COUNT">^2</xliff:g> item?</item>
- <item quantity="other">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na alisin sa trash ang <xliff:g id="COUNT">^2</xliff:g> na item?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one">Nag-aalis ng <xliff:g id="COUNT">^1</xliff:g> item sa trash…</item>
- <item quantity="other">Nag-aalis ng <xliff:g id="COUNT">^1</xliff:g> na item sa trash…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na i-delete ang <xliff:g id="COUNT">^2</xliff:g> audio file?</item>
- <item quantity="other">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na i-delete ang <xliff:g id="COUNT">^2</xliff:g> na audio file?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one">Nagde-delete ng <xliff:g id="COUNT">^1</xliff:g> audio file…</item>
- <item quantity="other">Nagde-delete ng <xliff:g id="COUNT">^1</xliff:g> na audio file…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na i-delete ang <xliff:g id="COUNT">^2</xliff:g> video?</item>
- <item quantity="other">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na i-delete ang <xliff:g id="COUNT">^2</xliff:g> na video?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one">Nagde-delete ng <xliff:g id="COUNT">^1</xliff:g> video…</item>
- <item quantity="other">Nagde-delete ng <xliff:g id="COUNT">^1</xliff:g> na video…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na i-delete ang <xliff:g id="COUNT">^2</xliff:g> larawan?</item>
- <item quantity="other">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na i-delete ang <xliff:g id="COUNT">^2</xliff:g> na larawan?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one">Nagde-delete ng <xliff:g id="COUNT">^1</xliff:g> larawan…</item>
- <item quantity="other">Nagde-delete ng <xliff:g id="COUNT">^1</xliff:g> na larawan…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na i-delete ang <xliff:g id="COUNT">^2</xliff:g> item?</item>
- <item quantity="other">Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na i-delete ang <xliff:g id="COUNT">^2</xliff:g> na item?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one">Nagde-delete ng <xliff:g id="COUNT">^1</xliff:g> item…</item>
- <item quantity="other">Nagde-delete ng <xliff:g id="COUNT">^1</xliff:g> na item…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Magdagdag"</string>
+ <string name="deselect" msgid="4297825044827769490">"I-deselect"</string>
+ <string name="deselected" msgid="8488133193326208475">"Na-deselect"</string>
+ <string name="select" msgid="2704765470563027689">"Piliin"</string>
+ <string name="selected" msgid="9151797369975828124">"Napili"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Pumili ng hanggang <xliff:g id="COUNT_0">^1</xliff:g> item}one{Pumili ng hanggang <xliff:g id="COUNT_1">^1</xliff:g> item}other{Pumili ng hanggang <xliff:g id="COUNT_1">^1</xliff:g> na item}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Kamakailan"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Walang larawan o video"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Walang album"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Tingnan ang napili"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Photos"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Mga Album"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Preview"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Lumipat sa para sa trabaho"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Lumipat sa personal"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Na-block ng iyong admin"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Hindi pinapahintulutan ang pag-access ng data ng trabaho mula sa isang personal na app"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Hindi pinapahintulutan ang pag-access ng personal na data mula sa isang app para sa trabaho"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Naka-pause ang mga app para sa trabaho"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Para buksan ang mga larawan sa trabaho, i-on ang iyong mga app sa trabaho at subukan ulit"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Ang mga larawang pipiliin mo lang ang maa-access ng app na ito"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}one{<xliff:g id="COUNT_1">^1</xliff:g> item}other{<xliff:g id="COUNT_1">^1</xliff:g> na item}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Magdagdag (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Camera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Mga Download"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Mga Paborito"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Mga Screenshot"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Gumagalaw na Larawan"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> na kinuha noong <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Kinuha ang video noong <xliff:g id="TIME">%1$s</xliff:g> na may tagal na <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Larawan"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Gumagalaw na Larawan"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"I-mute ang video"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"I-unmute ang video"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"I-play ang video"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"I-pause ang video"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Available na ang cloud media sa <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"hindi pinili"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Payagan ang <xliff:g id="APP_NAME_0">^1</xliff:g> na baguhin ang audio file na ito?}one{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na baguhin ang <xliff:g id="COUNT">^2</xliff:g> audio file?}other{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na baguhin ang <xliff:g id="COUNT">^2</xliff:g> na audio file?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Binabago ang audio file…}one{Nagbabago ng <xliff:g id="COUNT">^1</xliff:g> audio file…}other{Nagbabago ng <xliff:g id="COUNT">^1</xliff:g> na audio file…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Payagan ang <xliff:g id="APP_NAME_0">^1</xliff:g> na baguhin ang video na ito?}one{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na baguhin ang <xliff:g id="COUNT">^2</xliff:g> video?}other{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na baguhin ang <xliff:g id="COUNT">^2</xliff:g> na video?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Binabago ang video…}one{Nagbabago ng <xliff:g id="COUNT">^1</xliff:g> video…}other{Nagbabago ng <xliff:g id="COUNT">^1</xliff:g> na video…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Payagan ang <xliff:g id="APP_NAME_0">^1</xliff:g> na baguhin ang larawang ito?}one{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na baguhin ang <xliff:g id="COUNT">^2</xliff:g> larawan?}other{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na baguhin ang <xliff:g id="COUNT">^2</xliff:g> na larawan?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Binabago ang larawan…}one{Nagbabago ng <xliff:g id="COUNT">^1</xliff:g> larawan…}other{Nagbabago ng <xliff:g id="COUNT">^1</xliff:g> na larawan…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Payagan ang <xliff:g id="APP_NAME_0">^1</xliff:g> na baguhin ang item na ito?}one{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na baguhin ang <xliff:g id="COUNT">^2</xliff:g> item?}other{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na baguhin ang <xliff:g id="COUNT">^2</xliff:g> na item?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Binabago ang item…}one{Nagbabago ng <xliff:g id="COUNT">^1</xliff:g> item…}other{Nagbabago ng <xliff:g id="COUNT">^1</xliff:g> na item…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Payagan ang <xliff:g id="APP_NAME_0">^1</xliff:g> na ilipat sa trash ang audio file na ito?}one{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na ilipat sa trash ang <xliff:g id="COUNT">^2</xliff:g> audio file?}other{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na ilipat sa trash ang <xliff:g id="COUNT">^2</xliff:g> na audio file?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Inililipat sa trash ang audio file…}one{Naglilipat ng <xliff:g id="COUNT">^1</xliff:g> audio file sa trash…}other{Naglilipat ng <xliff:g id="COUNT">^1</xliff:g> na audio file sa trash…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Payagan ang <xliff:g id="APP_NAME_0">^1</xliff:g> na ilipat sa trash ang video na ito?}one{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na ilipat sa trash ang <xliff:g id="COUNT">^2</xliff:g> video?}other{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na ilipat sa trash ang <xliff:g id="COUNT">^2</xliff:g> na video?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Inililipat sa trash ang video…}one{Naglilipat ng <xliff:g id="COUNT">^1</xliff:g> video sa trash…}other{Naglilipat ng <xliff:g id="COUNT">^1</xliff:g> na video sa trash…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Payagan ang <xliff:g id="APP_NAME_0">^1</xliff:g> na ilipat sa trash ang larawang ito?}one{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na ilipat sa trash ang <xliff:g id="COUNT">^2</xliff:g> larawan?}other{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na ilipat sa trash ang <xliff:g id="COUNT">^2</xliff:g> na larawan?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Inililipat sa trash ang larawan…}one{Naglilipat ng <xliff:g id="COUNT">^1</xliff:g> larawan sa trash…}other{Naglilipat ng <xliff:g id="COUNT">^1</xliff:g> na larawan sa trash…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Payagan ang <xliff:g id="APP_NAME_0">^1</xliff:g> na ilipat sa trash ang item na ito?}one{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na ilipat sa trash ang <xliff:g id="COUNT">^2</xliff:g> item?}other{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na ilipat sa trash ang <xliff:g id="COUNT">^2</xliff:g> na item?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Inililipat sa trash ang item…}one{Naglilipat ng <xliff:g id="COUNT">^1</xliff:g> item sa trash…}other{Naglilipat ng <xliff:g id="COUNT">^1</xliff:g> na item sa trash…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Payagan ang <xliff:g id="APP_NAME_0">^1</xliff:g> na alisin sa trash ang audio file na ito?}one{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na alisin sa trash ang <xliff:g id="COUNT">^2</xliff:g> audio file?}other{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na alisin sa trash ang <xliff:g id="COUNT">^2</xliff:g> na audio file?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Inaalis sa trash ang audio file…}one{Nag-aalis ng <xliff:g id="COUNT">^1</xliff:g> audio file sa trash…}other{Nag-aalis ng <xliff:g id="COUNT">^1</xliff:g> na audio file sa trash…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Payagan ang <xliff:g id="APP_NAME_0">^1</xliff:g> na alisin sa trash ang video na ito?}one{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na alisin sa trash ang <xliff:g id="COUNT">^2</xliff:g> video?}other{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na alisin sa trash ang <xliff:g id="COUNT">^2</xliff:g> na video?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Inaalis sa trash ang video…}one{Nag-aalis ng <xliff:g id="COUNT">^1</xliff:g> video sa trash…}other{Nag-aalis ng <xliff:g id="COUNT">^1</xliff:g> na video sa trash…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Payagan ang <xliff:g id="APP_NAME_0">^1</xliff:g> na alisin sa trash ang larawang ito?}one{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na alisin sa trash ang <xliff:g id="COUNT">^2</xliff:g> larawan?}other{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na alisin sa trash ang <xliff:g id="COUNT">^2</xliff:g> na larawan?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Inaalis sa trash ang larawan…}one{Nag-aalis ng <xliff:g id="COUNT">^1</xliff:g> larawan sa trash…}other{Nag-aalis ng <xliff:g id="COUNT">^1</xliff:g> na larawan sa trash…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Payagan ang <xliff:g id="APP_NAME_0">^1</xliff:g> na alisin sa trash ang item na ito?}one{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na alisin sa trash ang <xliff:g id="COUNT">^2</xliff:g> item?}other{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na alisin sa trash ang <xliff:g id="COUNT">^2</xliff:g> na item?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Inaalis sa trash ang item…}one{Nag-aalis ng <xliff:g id="COUNT">^1</xliff:g> item sa trash…}other{Nag-aalis ng <xliff:g id="COUNT">^1</xliff:g> na item sa trash…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Payagan ang <xliff:g id="APP_NAME_0">^1</xliff:g> na i-delete ang audio file na ito?}one{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na i-delete ang <xliff:g id="COUNT">^2</xliff:g> audio file?}other{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na i-delete ang <xliff:g id="COUNT">^2</xliff:g> na audio file?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Dine-delete ang audio file…}one{Nagde-delete ng <xliff:g id="COUNT">^1</xliff:g> audio file…}other{Nagde-delete ng <xliff:g id="COUNT">^1</xliff:g> na audio file…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Payagan ang <xliff:g id="APP_NAME_0">^1</xliff:g> na i-delete ang video na ito?}one{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na i-delete ang <xliff:g id="COUNT">^2</xliff:g> video?}other{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na i-delete ang <xliff:g id="COUNT">^2</xliff:g> na video?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Dine-delete ang video…}one{Nagde-delete ng <xliff:g id="COUNT">^1</xliff:g> video…}other{Nagde-delete ng <xliff:g id="COUNT">^1</xliff:g> na video…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Payagan ang <xliff:g id="APP_NAME_0">^1</xliff:g> na i-delete ang larawang ito?}one{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na i-delete ang <xliff:g id="COUNT">^2</xliff:g> larawan?}other{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na i-delete ang <xliff:g id="COUNT">^2</xliff:g> na larawan?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Dine-delete ang larawan…}one{Nagde-delete ng <xliff:g id="COUNT">^1</xliff:g> larawan…}other{Nagde-delete ng <xliff:g id="COUNT">^1</xliff:g> na larawan…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Payagan ang <xliff:g id="APP_NAME_0">^1</xliff:g> na i-delete ang item na ito?}one{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na i-delete ang <xliff:g id="COUNT">^2</xliff:g> item?}other{Payagan ang <xliff:g id="APP_NAME_1">^1</xliff:g> na i-delete ang <xliff:g id="COUNT">^2</xliff:g> na item?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Dine-delete ang item…}one{Nagde-delete ng <xliff:g id="COUNT">^1</xliff:g> item…}other{Nagde-delete ng <xliff:g id="COUNT">^1</xliff:g> na item…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"Hindi nakakapagproseso ng mga media file ang <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Nakansela ang pagpoproseso ng media"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Error sa pagpoproseso ng media"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index e3efff8..8b826d0 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Fotoğraf seçici"</string>
<string name="artist_label" msgid="8105600993099120273">"Sanatçı"</string>
<string name="unknown" msgid="2059049215682829375">"Bilinmiyor"</string>
<string name="root_images" msgid="5861633549189045666">"Resimler"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Devam"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"İzin ver"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Reddet"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Ayrıca <xliff:g id="COUNT_1">^1</xliff:g> ek öğe</item>
- <item quantity="one">Ayrıca <xliff:g id="COUNT_0">^1</xliff:g> ek öğe</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Ayrıca <xliff:g id="COUNT_0">^1</xliff:g> ek öğe}other{Ayrıca <xliff:g id="COUNT_1">^1</xliff:g> ek öğe}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Geçici uygulama dosyalarını temizleyin"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> bazı geçici dosyaları temizlemek istiyor. Bu, pil veya hücresel veri kullanımında artışa yol açabilir."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Geçici uygulama dosyaları temizleniyor…"</string>
<string name="clear" msgid="5524638938415865915">"Temizle"</string>
<string name="allow" msgid="8885707816848569619">"İzin ver"</string>
<string name="deny" msgid="6040983710442068936">"Reddet"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> ses dosyasını değiştirmesine izin verilsin mi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu ses dosyasını değiştirmesine izin verilsin mi?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ses dosyası değiştiriliyor…</item>
- <item quantity="one">Ses dosyası değiştiriliyor…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> videoyu değiştirmesine izin verilsin mi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu videoyu değiştirmesine izin verilsin mi?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> video değiştiriliyor…</item>
- <item quantity="one">Video değiştiriliyor…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> fotoğrafı değiştirmesine izin verilsin mi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu fotoğrafı değiştirmesine izin verilsin mi?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> fotoğraf değiştiriliyor…</item>
- <item quantity="one">Fotoğraf değiştiriliyor…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> öğeyi değiştirmesine izin verilsin mi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu öğeyi değiştirmesine izin verilsin mi?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> öğe değiştiriliyor…</item>
- <item quantity="one">Öğe değiştiriliyor…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> ses dosyasını çöp kutusuna taşımasına izin verilsin mi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu ses dosyasını çöp kutusuna taşımasına izin verilsin mi?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ses dosyası çöp kutusuna taşınıyor…</item>
- <item quantity="one">Ses dosyası çöp kutusuna taşınıyor…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> videoyu çöp kutusuna taşımasına izin verilsin mi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu videoyu çöp kutusuna taşımasına izin verilsin mi?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> video çöp kutusuna taşınıyor…</item>
- <item quantity="one">Video çöp kutusuna taşınıyor…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> fotoğrafı çöp kutusuna taşımasına izin verilsin mi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu fotoğrafı çöp kutusuna taşımasına izin verilsin mi?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> fotoğraf çöp kutusuna taşınıyor…</item>
- <item quantity="one">Fotoğraf çöp kutusuna taşınıyor…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> öğeyi çöp kutusuna taşımasına izin verilsin mi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu öğeyi çöp kutusuna taşımasına izin verilsin mi?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> öğe çöp kutusuna taşınıyor…</item>
- <item quantity="one">Öğe çöp kutusuna taşınıyor…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> ses dosyasını çöp kutusundan geri yüklemesine izin verilsin mi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu ses dosyasını çöp kutusundan geri yüklemesine izin verilsin mi?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ses dosyası çöp kutusundan geri yükleniyor…</item>
- <item quantity="one">Ses dosyası çöp kutusundan geri yükleniyor…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> videoyu çöp kutusundan geri yüklemesine izin verilsin mi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu videoyu çöp kutusundan geri yüklemesine izin verilsin mi?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> video çöp kutusundan geri yükleniyor…</item>
- <item quantity="one">Video çöp kutusundan geri yükleniyor…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> fotoğrafı çöp kutusundan geri yüklemesine izin verilsin mi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu fotoğrafı çöp kutusundan geri yüklemesine izin verilsin mi?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> fotoğraf çöp kutusundan geri yükleniyor…</item>
- <item quantity="one">Fotoğraf çöp kutusundan geri yükleniyor…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> öğeyi çöp kutusundan geri yüklemesine izin verilsin mi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu öğeyi çöp kutusundan geri yüklemesine izin verilsin mi?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> öğe çöp kutusundan geri yükleniyor…</item>
- <item quantity="one">Öğe çöp kutusundan geri yükleniyor…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> ses dosyasını silmesine izin verilsin mi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu ses dosyasını silmesine izin verilsin mi?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ses dosyası siliniyor…</item>
- <item quantity="one">Ses dosyası siliniyor…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> videoyu silmesine izin verilsin mi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu videoyu silmesine izin verilsin mi?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> video siliniyor…</item>
- <item quantity="one">Video siliniyor…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> fotoğrafı silmesine izin verilsin mi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu fotoğrafı silmesine izin verilsin mi?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> fotoğraf siliniyor…</item>
- <item quantity="one">Fotoğraf siliniyor…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> öğeyi silmesine izin verilsin mi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu öğeyi silmesine izin verilsin mi?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> öğe siliniyor…</item>
- <item quantity="one">Öğe siliniyor…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Ekle"</string>
+ <string name="deselect" msgid="4297825044827769490">"Seçimi kaldır"</string>
+ <string name="deselected" msgid="8488133193326208475">"Seçimi kaldırıldı"</string>
+ <string name="select" msgid="2704765470563027689">"Seç"</string>
+ <string name="selected" msgid="9151797369975828124">"Seçildi"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{En fazla <xliff:g id="COUNT_0">^1</xliff:g> öğe seçin}other{En fazla <xliff:g id="COUNT_1">^1</xliff:g> öğe seçin}}"</string>
+ <string name="recent" msgid="6694613584743207874">"En son"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Fotoğraf veya video yok"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Albüm yok"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Seçilenleri görüntüle"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Fotoğraflar"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albümler"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Önizle"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"İş profiline geç"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Kişisel profile geç"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Yöneticiniz tarafından engellendi"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Kişisel uygulamadan iş verilerine erişmeye izin verilmiyor"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"İş uygulamasından kişisel verilere erişmeye izin verilmiyor"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"İş uygulamaları duraklatıldı"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"İş fotoğraflarını açmak için iş uygulamalarını açıp tekrar deneyin"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Bu uygulama yalnızca seçtiğiniz fotoğraflara erişebilir"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> öğe}other{<xliff:g id="COUNT_1">^1</xliff:g> öğe}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Ekle (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"İndirilenler"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Favoriler"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Ekran görüntüleri"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Hareketli Fotoğraf"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="TIME">%2$s</xliff:g> tarihinde çekilen <xliff:g id="ITEM_NAME">%1$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Video <xliff:g id="TIME">%1$s</xliff:g> tarihinde <xliff:g id="DURATION">%2$s</xliff:g> süreyle kaydedildi"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Fotoğraf"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Hareketli Fotoğraf"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Videonun sesini kapat"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Videonun sesini aç"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Videoyu oynat"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Videoyu duraklat"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Bulut üzerinde saklanan medya dosyaları artık <xliff:g id="PKG_NAME">%1$s</xliff:g> uygulamasından kullanılabilir"</string>
+ <string name="not_selected" msgid="2244008151669896758">"seçili değil"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu ses dosyasını değiştirmesine izin verilsin mi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> ses dosyasını değiştirmesine izin verilsin mi?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Ses dosyası değiştiriliyor…}other{<xliff:g id="COUNT">^1</xliff:g> ses dosyası değiştiriliyor…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu videoyu değiştirmesine izin verilsin mi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> videoyu değiştirmesine izin verilsin mi?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Video değiştiriliyor…}other{<xliff:g id="COUNT">^1</xliff:g> video değiştiriliyor…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu fotoğrafı değiştirmesine izin verilsin mi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> fotoğrafı değiştirmesine izin verilsin mi?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Fotoğraf değiştiriliyor…}other{<xliff:g id="COUNT">^1</xliff:g> fotoğraf değiştiriliyor…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu öğeyi değiştirmesine izin verilsin mi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> öğeyi değiştirmesine izin verilsin mi?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Öğe değiştiriliyor…}other{<xliff:g id="COUNT">^1</xliff:g> öğe değiştiriliyor…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu ses dosyasını çöp kutusuna taşımasına izin verilsin mi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> ses dosyasını çöp kutusuna taşımasına izin verilsin mi?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Ses dosyası çöp kutusuna taşınıyor…}other{<xliff:g id="COUNT">^1</xliff:g> ses dosyası çöp kutusuna taşınıyor…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu videoyu çöp kutusuna taşımasına izin verilsin mi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> videoyu çöp kutusuna taşımasına izin verilsin mi?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Video çöp kutusuna taşınıyor…}other{<xliff:g id="COUNT">^1</xliff:g> video çöp kutusuna taşınıyor…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu fotoğrafı çöp kutusuna taşımasına izin verilsin mi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> fotoğrafı çöp kutusuna taşımasına izin verilsin mi?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Fotoğraf çöp kutusuna taşınıyor…}other{<xliff:g id="COUNT">^1</xliff:g> fotoğraf çöp kutusuna taşınıyor…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu öğeyi çöp kutusuna taşımasına izin verilsin mi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> öğeyi çöp kutusuna taşımasına izin verilsin mi?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Öğe çöp kutusuna taşınıyor…}other{<xliff:g id="COUNT">^1</xliff:g> öğe çöp kutusuna taşınıyor…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu ses dosyasını çöp kutusundan geri yüklemesine izin verilsin mi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> ses dosyasını çöp kutusundan geri yüklemesine izin verilsin mi?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Ses dosyası çöp kutusundan geri yükleniyor…}other{<xliff:g id="COUNT">^1</xliff:g> ses dosyası çöp kutusundan geri yükleniyor…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu videoyu çöp kutusundan geri yüklemesine izin verilsin mi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> videoyu çöp kutusundan geri yüklemesine izin verilsin mi?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Video çöp kutusundan geri yükleniyor…}other{<xliff:g id="COUNT">^1</xliff:g> video çöp kutusundan geri yükleniyor…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu fotoğrafı çöp kutusundan geri yüklemesine izin verilsin mi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> fotoğrafı çöp kutusundan geri yüklemesine izin verilsin mi?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Fotoğraf çöp kutusundan geri yükleniyor…}other{<xliff:g id="COUNT">^1</xliff:g> fotoğraf çöp kutusundan geri yükleniyor…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu öğeyi çöp kutusundan geri yüklemesine izin verilsin mi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> öğeyi çöp kutusundan geri yüklemesine izin verilsin mi?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Öğe çöp kutusundan geri yükleniyor…}other{<xliff:g id="COUNT">^1</xliff:g> öğe çöp kutusundan geri yükleniyor…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu ses dosyasını silmesine izin verilsin mi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> ses dosyasını silmesine izin verilsin mi?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Ses dosyası siliniyor…}other{<xliff:g id="COUNT">^1</xliff:g> ses dosyası siliniyor…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu videoyu silmesine izin verilsin mi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> videoyu silmesine izin verilsin mi?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Video siliniyor…}other{<xliff:g id="COUNT">^1</xliff:g> video siliniyor…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu fotoğrafı silmesine izin verilsin mi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> fotoğrafı silmesine izin verilsin mi?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Fotoğraf siliniyor…}other{<xliff:g id="COUNT">^1</xliff:g> fotoğraf siliniyor…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> uygulamasının bu öğeyi silmesine izin verilsin mi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> uygulamasının <xliff:g id="COUNT">^2</xliff:g> öğeyi silmesine izin verilsin mi?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Öğe siliniyor…}other{<xliff:g id="COUNT">^1</xliff:g> öğe siliniyor…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g>, medya dosyalarını işleyemez"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Medya işleme iptal edildi"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Medya işleme hatası"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index d03fb4c..8a90aa1 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Медіа-файли"</string>
<string name="storage_description" msgid="4081716890357580107">"Локальна пам’ять"</string>
<string name="app_label" msgid="9035307001052716210">"Сховище медіа-файлів"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"Засіб вибору фотографій"</string>
<string name="artist_label" msgid="8105600993099120273">"Виконавець"</string>
<string name="unknown" msgid="2059049215682829375">"Невідомо"</string>
<string name="root_images" msgid="5861633549189045666">"Зображення"</string>
@@ -29,216 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Продовжити"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Надати"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Заборонити"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="few">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="many">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">Ще <xliff:g id="COUNT_1">^1</xliff:g> додатковий елемент</item>
- <item quantity="few">Ще <xliff:g id="COUNT_1">^1</xliff:g> додаткові елементи</item>
- <item quantity="many">Ще <xliff:g id="COUNT_1">^1</xliff:g> додаткових елементів</item>
- <item quantity="other">Ще <xliff:g id="COUNT_1">^1</xliff:g> додаткового елемента</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}one{+<xliff:g id="COUNT_1">^1</xliff:g>}few{+<xliff:g id="COUNT_1">^1</xliff:g>}many{+<xliff:g id="COUNT_1">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{І ще <xliff:g id="COUNT_0">^1</xliff:g> додатковий об’єкт}one{І ще <xliff:g id="COUNT_1">^1</xliff:g> додатковий об’єкт}few{І ще <xliff:g id="COUNT_1">^1</xliff:g> додаткові об’єкти}many{І ще <xliff:g id="COUNT_1">^1</xliff:g> додаткових об’єктів}other{І ще <xliff:g id="COUNT_1">^1</xliff:g> додаткового об’єкта}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Видалити тимчасові файли додатків"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"Додаток <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> хоче видалити деякі тимчасові файли. Це може збільшити використання акумулятора або мобільного трафіку."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Видалення тимчасових файлів додатків…"</string>
<string name="clear" msgid="5524638938415865915">"Очистити"</string>
<string name="allow" msgid="8885707816848569619">"Дозволити"</string>
<string name="deny" msgid="6040983710442068936">"Заборонити"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> аудіофайл?</item>
- <item quantity="few">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> аудіофайли?</item>
- <item quantity="many">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> аудіофайлів?</item>
- <item quantity="other">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> аудіофайлу?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one">Змінення <xliff:g id="COUNT">^1</xliff:g> аудіофайлу…</item>
- <item quantity="few">Змінення <xliff:g id="COUNT">^1</xliff:g> аудіофайлів…</item>
- <item quantity="many">Змінення <xliff:g id="COUNT">^1</xliff:g> аудіофайлів…</item>
- <item quantity="other">Змінення <xliff:g id="COUNT">^1</xliff:g> аудіофайлу…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> відео?</item>
- <item quantity="few">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> відео?</item>
- <item quantity="many">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> відео?</item>
- <item quantity="other">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> відео?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one">Змінення <xliff:g id="COUNT">^1</xliff:g> відео…</item>
- <item quantity="few">Змінення <xliff:g id="COUNT">^1</xliff:g> відео…</item>
- <item quantity="many">Змінення <xliff:g id="COUNT">^1</xliff:g> відео…</item>
- <item quantity="other">Змінення <xliff:g id="COUNT">^1</xliff:g> відео…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> фотографію?</item>
- <item quantity="few">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> фотографії?</item>
- <item quantity="many">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> фотографій?</item>
- <item quantity="other">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> фотографії?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one">Змінення <xliff:g id="COUNT">^1</xliff:g> фотографії…</item>
- <item quantity="few">Змінення <xliff:g id="COUNT">^1</xliff:g> фотографій…</item>
- <item quantity="many">Змінення <xliff:g id="COUNT">^1</xliff:g> фотографій…</item>
- <item quantity="other">Змінення <xliff:g id="COUNT">^1</xliff:g> фотографії…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> елемент?</item>
- <item quantity="few">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> елементи?</item>
- <item quantity="many">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> елементів?</item>
- <item quantity="other">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> елемента?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one">Змінення <xliff:g id="COUNT">^1</xliff:g> об\'єкта…</item>
- <item quantity="few">Змінення <xliff:g id="COUNT">^1</xliff:g> об\'єктів…</item>
- <item quantity="many">Змінення <xliff:g id="COUNT">^1</xliff:g> об\'єктів…</item>
- <item quantity="other">Змінення <xliff:g id="COUNT">^1</xliff:g> об\'єкта…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> аудіофайл у кошик?</item>
- <item quantity="few">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> аудіофайли в кошик?</item>
- <item quantity="many">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> аудіофайлів у кошик?</item>
- <item quantity="other">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> аудіофайлу в кошик?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one">Переміщення <xliff:g id="COUNT">^1</xliff:g> аудіофайлу в кошик…</item>
- <item quantity="few">Переміщення <xliff:g id="COUNT">^1</xliff:g> аудіофайлів у кошик…</item>
- <item quantity="many">Переміщення <xliff:g id="COUNT">^1</xliff:g> аудіофайлів у кошик…</item>
- <item quantity="other">Переміщення <xliff:g id="COUNT">^1</xliff:g> аудіофайлу в кошик…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> відео в кошик?</item>
- <item quantity="few">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> відео в кошик?</item>
- <item quantity="many">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> відео в кошик?</item>
- <item quantity="other">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> відео в кошик?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one">Переміщення <xliff:g id="COUNT">^1</xliff:g> відео в кошик…</item>
- <item quantity="few">Переміщення <xliff:g id="COUNT">^1</xliff:g> відео в кошик…</item>
- <item quantity="many">Переміщення <xliff:g id="COUNT">^1</xliff:g> відео в кошик…</item>
- <item quantity="other">Переміщення <xliff:g id="COUNT">^1</xliff:g> відео в кошик…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> фотографію в кошик?</item>
- <item quantity="few">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> фотографії в кошик?</item>
- <item quantity="many">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> фотографій у кошик?</item>
- <item quantity="other">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> фотографії в кошик?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one">Переміщення <xliff:g id="COUNT">^1</xliff:g> фотографії в кошик…</item>
- <item quantity="few">Переміщення <xliff:g id="COUNT">^1</xliff:g> фотографій у кошик…</item>
- <item quantity="many">Переміщення <xliff:g id="COUNT">^1</xliff:g> фотографій у кошик…</item>
- <item quantity="other">Переміщення <xliff:g id="COUNT">^1</xliff:g> фотографії в кошик…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> елемент у кошик?</item>
- <item quantity="few">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> елементи в кошик?</item>
- <item quantity="many">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> елементів у кошик?</item>
- <item quantity="other">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> елемента в кошик?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one">Переміщення <xliff:g id="COUNT">^1</xliff:g> об\'єкта в кошик…</item>
- <item quantity="few">Переміщення <xliff:g id="COUNT">^1</xliff:g> об\'єктів у кошик…</item>
- <item quantity="many">Переміщення <xliff:g id="COUNT">^1</xliff:g> об\'єктів у кошик…</item>
- <item quantity="other">Переміщення <xliff:g id="COUNT">^1</xliff:g> об\'єкта в кошик…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> аудіофайл?</item>
- <item quantity="few">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> аудіофайли?</item>
- <item quantity="many">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> аудіофайлів?</item>
- <item quantity="other">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> аудіофайлу?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one">Відновлення <xliff:g id="COUNT">^1</xliff:g> аудіофайлу з кошика…</item>
- <item quantity="few">Відновлення <xliff:g id="COUNT">^1</xliff:g> аудіофайлів із кошика…</item>
- <item quantity="many">Відновлення <xliff:g id="COUNT">^1</xliff:g> аудіофайлів із кошика…</item>
- <item quantity="other">Відновлення <xliff:g id="COUNT">^1</xliff:g> аудіофайлу з кошика…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> відео?</item>
- <item quantity="few">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> відео?</item>
- <item quantity="many">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> відео?</item>
- <item quantity="other">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> відео?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one">Відновлення <xliff:g id="COUNT">^1</xliff:g> відео з кошика…</item>
- <item quantity="few">Відновлення <xliff:g id="COUNT">^1</xliff:g> відео з кошика…</item>
- <item quantity="many">Відновлення <xliff:g id="COUNT">^1</xliff:g> відео з кошика…</item>
- <item quantity="other">Відновлення <xliff:g id="COUNT">^1</xliff:g> відео з кошика…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> фотографію?</item>
- <item quantity="few">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> фотографії?</item>
- <item quantity="many">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> фотографій?</item>
- <item quantity="other">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> фотографії?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one">Відновлення <xliff:g id="COUNT">^1</xliff:g> фотографії з кошика…</item>
- <item quantity="few">Відновлення <xliff:g id="COUNT">^1</xliff:g> фотографій із кошика…</item>
- <item quantity="many">Відновлення <xliff:g id="COUNT">^1</xliff:g> фотографій із кошика…</item>
- <item quantity="other">Відновлення <xliff:g id="COUNT">^1</xliff:g> фотографії з кошика…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> елемент?</item>
- <item quantity="few">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> елементи?</item>
- <item quantity="many">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> елементів?</item>
- <item quantity="other">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> елемента?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one">Відновлення <xliff:g id="COUNT">^1</xliff:g> об\'єкта з кошика…</item>
- <item quantity="few">Відновлення <xliff:g id="COUNT">^1</xliff:g> об\'єктів із кошика…</item>
- <item quantity="many">Відновлення <xliff:g id="COUNT">^1</xliff:g> об\'єктів із кошика…</item>
- <item quantity="other">Відновлення <xliff:g id="COUNT">^1</xliff:g> об\'єкта з кошика…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> аудіофайл?</item>
- <item quantity="few">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> аудіофайли?</item>
- <item quantity="many">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> аудіофайлів?</item>
- <item quantity="other">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> аудіофайлу?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one">Видалення <xliff:g id="COUNT">^1</xliff:g> аудіофайлу…</item>
- <item quantity="few">Видалення <xliff:g id="COUNT">^1</xliff:g> аудіофайлів…</item>
- <item quantity="many">Видалення <xliff:g id="COUNT">^1</xliff:g> аудіофайлів…</item>
- <item quantity="other">Видалення <xliff:g id="COUNT">^1</xliff:g> аудіофайлу…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> відео?</item>
- <item quantity="few">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> відео?</item>
- <item quantity="many">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> відео?</item>
- <item quantity="other">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> відео?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one">Видалення <xliff:g id="COUNT">^1</xliff:g> відео…</item>
- <item quantity="few">Видалення <xliff:g id="COUNT">^1</xliff:g> відео…</item>
- <item quantity="many">Видалення <xliff:g id="COUNT">^1</xliff:g> відео…</item>
- <item quantity="other">Видалення <xliff:g id="COUNT">^1</xliff:g> відео…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> фотографію?</item>
- <item quantity="few">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> фотографії?</item>
- <item quantity="many">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> фотографій?</item>
- <item quantity="other">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> фотографії?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one">Видалення <xliff:g id="COUNT">^1</xliff:g> фотографії…</item>
- <item quantity="few">Видалення <xliff:g id="COUNT">^1</xliff:g> фотографій…</item>
- <item quantity="many">Видалення <xliff:g id="COUNT">^1</xliff:g> фотографій…</item>
- <item quantity="other">Видалення <xliff:g id="COUNT">^1</xliff:g> фотографії…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> елемент?</item>
- <item quantity="few">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> елементи?</item>
- <item quantity="many">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> елементів?</item>
- <item quantity="other">Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> елемента?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one">Видалення <xliff:g id="COUNT">^1</xliff:g> об\'єкта…</item>
- <item quantity="few">Видалення <xliff:g id="COUNT">^1</xliff:g> об\'єктів…</item>
- <item quantity="many">Видалення <xliff:g id="COUNT">^1</xliff:g> об\'єктів…</item>
- <item quantity="other">Видалення <xliff:g id="COUNT">^1</xliff:g> об\'єкта…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Додати"</string>
+ <string name="deselect" msgid="4297825044827769490">"Не вибирати"</string>
+ <string name="deselected" msgid="8488133193326208475">"Не вибрано"</string>
+ <string name="select" msgid="2704765470563027689">"Вибрати"</string>
+ <string name="selected" msgid="9151797369975828124">"Вибрано"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Виберіть не більше ніж <xliff:g id="COUNT_0">^1</xliff:g> об’єкт}one{Виберіть не більше ніж <xliff:g id="COUNT_1">^1</xliff:g> об’єкт}few{Виберіть не більше ніж <xliff:g id="COUNT_1">^1</xliff:g> об’єкти}many{Виберіть не більше ніж <xliff:g id="COUNT_1">^1</xliff:g> об’єктів}other{Виберіть не більше ніж <xliff:g id="COUNT_1">^1</xliff:g> об’єкта}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Нещодавні"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Немає фото чи відео"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Немає альбомів"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Переглянути вибране"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Фото"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Альбоми"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Попередній перегляд"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Перейти в робочий профіль"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Перейти в особистий профіль"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Заблоковано адміністратором"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Доступ до робочих даних з особистого додатка заблокований"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Доступ до персональних даних із робочого додатка заблокований"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Робочі додатки призупинено"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Щоб відкрити робочі фотографії, увімкніть робочі додатки й повторіть спробу"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Цей додаток має доступ лише до вибраних вами фотографій"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> об’єкт}one{<xliff:g id="COUNT_1">^1</xliff:g> об’єкт}few{<xliff:g id="COUNT_1">^1</xliff:g> об’єкти}many{<xliff:g id="COUNT_1">^1</xliff:g> об’єктів}other{<xliff:g id="COUNT_1">^1</xliff:g> об’єкта}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Додати (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Камера"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Завантаження"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Вибране"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Знімки екрана"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Фото з рухом"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> (знято <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Дата зйомки: <xliff:g id="TIME">%1$s</xliff:g>, тривалість: <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Фотографія"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Фото з рухом"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Вимкнути звук у відео"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Увімкнути звук у відео"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Відтворити відео"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Призупинити відео"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Тепер медіаконтент із хмари доступний у додатку <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"не вибрано"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Дозволити додатку <xliff:g id="APP_NAME_0">^1</xliff:g> змінити цей аудіофайл?}one{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> аудіофайл?}few{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> аудіофайли?}many{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> аудіофайлів?}other{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> аудіофайлу?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Змінення аудіофайлу…}one{Змінення <xliff:g id="COUNT">^1</xliff:g> аудіофайлу…}few{Змінення <xliff:g id="COUNT">^1</xliff:g> аудіофайлів…}many{Змінення <xliff:g id="COUNT">^1</xliff:g> аудіофайлів…}other{Змінення <xliff:g id="COUNT">^1</xliff:g> аудіофайлу…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Дозволити додатку <xliff:g id="APP_NAME_0">^1</xliff:g> змінити це відео?}one{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> відео?}few{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> відео?}many{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> відео?}other{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> відео?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Змінення відео…}one{Змінення <xliff:g id="COUNT">^1</xliff:g> відео…}few{Змінення <xliff:g id="COUNT">^1</xliff:g> відео…}many{Змінення <xliff:g id="COUNT">^1</xliff:g> відео…}other{Змінення <xliff:g id="COUNT">^1</xliff:g> відео…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Дозволити додатку <xliff:g id="APP_NAME_0">^1</xliff:g> змінити цю фотографію?}one{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> фотографію?}few{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> фотографії?}many{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> фотографій?}other{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> фотографії?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Змінення фотографії…}one{Змінення <xliff:g id="COUNT">^1</xliff:g> фотографії…}few{Змінення <xliff:g id="COUNT">^1</xliff:g> фотографій…}many{Змінення <xliff:g id="COUNT">^1</xliff:g> фотографій…}other{Змінення <xliff:g id="COUNT">^1</xliff:g> фотографії…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Дозволити додатку <xliff:g id="APP_NAME_0">^1</xliff:g> змінити цей об’єкт?}one{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> об’єкт?}few{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> об’єкти?}many{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> об’єктів?}other{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> змінити <xliff:g id="COUNT">^2</xliff:g> об’єкта?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Змінення об’єкта…}one{Змінення <xliff:g id="COUNT">^1</xliff:g> об’єкта…}few{Змінення <xliff:g id="COUNT">^1</xliff:g> об’єктів…}many{Змінення <xliff:g id="COUNT">^1</xliff:g> об’єктів…}other{Змінення <xliff:g id="COUNT">^1</xliff:g> об’єкта…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Дозволити додатку <xliff:g id="APP_NAME_0">^1</xliff:g> перемістити цей аудіофайл у кошик?}one{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> аудіофайл у кошик?}few{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> аудіофайли в кошик?}many{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> аудіофайлів у кошик?}other{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> аудіофайлу в кошик?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Переміщення аудіофайлу в кошик…}one{Переміщення <xliff:g id="COUNT">^1</xliff:g> аудіофайлу в кошик…}few{Переміщення <xliff:g id="COUNT">^1</xliff:g> аудіофайлів у кошик…}many{Переміщення <xliff:g id="COUNT">^1</xliff:g> аудіофайлів у кошик…}other{Переміщення <xliff:g id="COUNT">^1</xliff:g> аудіофайлу в кошик…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Дозволити додатку <xliff:g id="APP_NAME_0">^1</xliff:g> перемістити це відео в кошик?}one{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> відео в кошик?}few{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> відео в кошик?}many{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> відео в кошик?}other{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> відео в кошик?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Переміщення відео в кошик…}one{Переміщення <xliff:g id="COUNT">^1</xliff:g> відео в кошик…}few{Переміщення <xliff:g id="COUNT">^1</xliff:g> відео в кошик…}many{Переміщення <xliff:g id="COUNT">^1</xliff:g> відео в кошик…}other{Переміщення <xliff:g id="COUNT">^1</xliff:g> відео в кошик…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Дозволити додатку <xliff:g id="APP_NAME_0">^1</xliff:g> перемістити цю фотографію в кошик?}one{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> фотографію в кошик?}few{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> фотографії в кошик?}many{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> фотографій у кошик?}other{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> фотографії в кошик?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Переміщення фотографії в кошик…}one{Переміщення <xliff:g id="COUNT">^1</xliff:g> фотографії в кошик…}few{Переміщення <xliff:g id="COUNT">^1</xliff:g> фотографій у кошик…}many{Переміщення <xliff:g id="COUNT">^1</xliff:g> фотографій у кошик…}other{Переміщення <xliff:g id="COUNT">^1</xliff:g> фотографії в кошик…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Дозволити додатку <xliff:g id="APP_NAME_0">^1</xliff:g> перемістити цей об’єкт у кошик?}one{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> об’єкт у кошик?}few{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> об’єкти в кошик?}many{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> об’єктів у кошик?}other{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> перемістити <xliff:g id="COUNT">^2</xliff:g> об’єкта в кошик?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Переміщення об’єкта в кошик…}one{Переміщення <xliff:g id="COUNT">^1</xliff:g> об’єкта в кошик…}few{Переміщення <xliff:g id="COUNT">^1</xliff:g> об’єктів у кошик…}many{Переміщення <xliff:g id="COUNT">^1</xliff:g> об’єктів у кошик…}other{Переміщення <xliff:g id="COUNT">^1</xliff:g> об’єкта в кошик…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Дозволити додатку <xliff:g id="APP_NAME_0">^1</xliff:g> відновити цей аудіофайл із кошика?}one{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> аудіофайл із кошика?}few{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> аудіофайли з кошика?}many{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> аудіофайлів із кошика?}other{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> аудіофайлу з кошика?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Відновлення аудіофайлу з кошика…}one{Відновлення <xliff:g id="COUNT">^1</xliff:g> аудіофайлу з кошика…}few{Відновлення <xliff:g id="COUNT">^1</xliff:g> аудіофайлів із кошика…}many{Відновлення <xliff:g id="COUNT">^1</xliff:g> аудіофайлів із кошика…}other{Відновлення <xliff:g id="COUNT">^1</xliff:g> аудіофайлу з кошика…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Дозволити додатку <xliff:g id="APP_NAME_0">^1</xliff:g> відновити це відео з кошика?}one{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> відео з кошика?}few{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> відео з кошика?}many{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> відео з кошика?}other{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> відео з кошика?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Відновлення відео з кошика…}one{Відновлення <xliff:g id="COUNT">^1</xliff:g> відео з кошика…}few{Відновлення <xliff:g id="COUNT">^1</xliff:g> відео з кошика…}many{Відновлення <xliff:g id="COUNT">^1</xliff:g> відео з кошика…}other{Відновлення <xliff:g id="COUNT">^1</xliff:g> відео з кошика…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Дозволити додатку <xliff:g id="APP_NAME_0">^1</xliff:g> відновити цю фотографію з кошика?}one{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> фотографію з кошика?}few{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> фотографії з кошика?}many{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> фотографій із кошика?}other{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> фотографії з кошика?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Відновлення фотографії з кошика…}one{Відновлення <xliff:g id="COUNT">^1</xliff:g> фотографії з кошика…}few{Відновлення <xliff:g id="COUNT">^1</xliff:g> фотографій із кошика…}many{Відновлення <xliff:g id="COUNT">^1</xliff:g> фотографій із кошика…}other{Відновлення <xliff:g id="COUNT">^1</xliff:g> фотографії з кошика…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Дозволити додатку <xliff:g id="APP_NAME_0">^1</xliff:g> відновити цей об’єкт із кошика?}one{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> об’єкт із кошика?}few{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> об’єкти з кошика?}many{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> об’єктів із кошика?}other{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> відновити <xliff:g id="COUNT">^2</xliff:g> об’єкта з кошика?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Відновлення об’єкта з кошика…}one{Відновлення <xliff:g id="COUNT">^1</xliff:g> об’єкта з кошика…}few{Відновлення <xliff:g id="COUNT">^1</xliff:g> об’єктів із кошика…}many{Відновлення <xliff:g id="COUNT">^1</xliff:g> об’єктів із кошика…}other{Відновлення <xliff:g id="COUNT">^1</xliff:g> об’єкта з кошика…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Дозволити додатку <xliff:g id="APP_NAME_0">^1</xliff:g> видалити цей аудіофайл?}one{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> аудіофайл?}few{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> аудіофайли?}many{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> аудіофайлів?}other{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> аудіофайлу?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Видалення аудіофайлу…}one{Видалення <xliff:g id="COUNT">^1</xliff:g> аудіофайлу…}few{Видалення <xliff:g id="COUNT">^1</xliff:g> аудіофайлів…}many{Видалення <xliff:g id="COUNT">^1</xliff:g> аудіофайлів…}other{Видалення <xliff:g id="COUNT">^1</xliff:g> аудіофайлу…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Дозволити додатку <xliff:g id="APP_NAME_0">^1</xliff:g> видалити це відео?}one{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> відео?}few{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> відео?}many{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> відео?}other{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> відео?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Видалення відео…}one{Видалення <xliff:g id="COUNT">^1</xliff:g> відео…}few{Видалення <xliff:g id="COUNT">^1</xliff:g> відео…}many{Видалення <xliff:g id="COUNT">^1</xliff:g> відео…}other{Видалення <xliff:g id="COUNT">^1</xliff:g> відео…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Дозволити додатку <xliff:g id="APP_NAME_0">^1</xliff:g> видалити цю фотографію?}one{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> фотографію?}few{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> фотографії?}many{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> фотографій?}other{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> фотографії?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Видалення фотографії…}one{Видалення <xliff:g id="COUNT">^1</xliff:g> фотографії…}few{Видалення <xliff:g id="COUNT">^1</xliff:g> фотографій…}many{Видалення <xliff:g id="COUNT">^1</xliff:g> фотографій…}other{Видалення <xliff:g id="COUNT">^1</xliff:g> фотографії…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Дозволити додатку <xliff:g id="APP_NAME_0">^1</xliff:g> видалити цей об’єкт?}one{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> об’єкт?}few{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> об’єкти?}many{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> об’єктів?}other{Дозволити додатку <xliff:g id="APP_NAME_1">^1</xliff:g> видалити <xliff:g id="COUNT">^2</xliff:g> об’єкта?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Видалення об’єкта…}one{Видалення <xliff:g id="COUNT">^1</xliff:g> об’єкта…}few{Видалення <xliff:g id="COUNT">^1</xliff:g> об’єктів…}many{Видалення <xliff:g id="COUNT">^1</xliff:g> об’єктів…}other{Видалення <xliff:g id="COUNT">^1</xliff:g> об’єкта…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"Додаток <xliff:g id="APP_NAME">%s</xliff:g> не може обробляти медіафайли"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Обробку медіафайлів скасовано"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Не вдалось обробити медіафайли"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index cddbb63..a7b76d8 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"میڈیا"</string>
<string name="storage_description" msgid="4081716890357580107">"مقامی اسٹوریج"</string>
<string name="app_label" msgid="9035307001052716210">"میڈیا اسٹوریج"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"تصویر منتخب کنندہ"</string>
<string name="artist_label" msgid="8105600993099120273">"فنکار"</string>
<string name="unknown" msgid="2059049215682829375">"نامعلوم"</string>
<string name="root_images" msgid="5861633549189045666">"تصاوير"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"جاری رکھیں"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"اجازت دیں"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"مسترد کریں"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other"><xliff:g id="COUNT_1">^1</xliff:g>+</item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">مزید <xliff:g id="COUNT_1">^1</xliff:g> اضافی آئٹمز</item>
- <item quantity="one">مزید <xliff:g id="COUNT_0">^1</xliff:g> اضافی آئٹم</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{مزید <xliff:g id="COUNT_0">^1</xliff:g> اضافی آئٹم}other{مزید <xliff:g id="COUNT_1">^1</xliff:g> اضافی آئٹمز}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"ایپ کی عارضی فائلز کو صاف کریں"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> کچھ عارضی فائلز کو صاف کرنا چاہتی ہے۔ اس کی وجہ سے بیٹری یا سیلولر ڈیٹا کے استعمال میں اضافہ ہو سکتا ہے۔"</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"ایپ کی عارضی فائلز کو صاف کیا جا رہا ہے…"</string>
<string name="clear" msgid="5524638938415865915">"صاف کریں"</string>
<string name="allow" msgid="8885707816848569619">"اجازت دیں"</string>
<string name="deny" msgid="6040983710442068936">"مسترد کریں"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> آڈیو فائلز میں ترمیم کرنے کی اجازت دیں؟</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> کو اس آڈیو فائل میں ترمیم کرنے کی اجازت دیں؟</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> آڈیو فائلز میں ترمیم کی جا رہی ہے…</item>
- <item quantity="one">آڈیو فائل میں ترمیم کی جا رہی ہے…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> ویڈیوز میں ترمیم کرنے کی اجازت دیں؟</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> کو اس ویڈیو میں ترمیم کرنے کی اجازت دیں؟</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ویڈیوز میں ترمیم کی جا رہی ہے…</item>
- <item quantity="one">ویڈیو میں ترمیم کی جا رہی ہے…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> تصاویر میں ترمیم کرنے کی اجازت دیں؟</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> کو اس تصویر میں ترمیم کرنے کی اجازت دیں؟</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> تصاویر میں ترمیم کی جا رہی ہے…</item>
- <item quantity="one">تصویر میں ترمیم کی جا رہی ہے…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> آئٹمز میں ترمیم کرنے کی اجازت دیں؟</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> کو اس آئٹم میں ترمیم کرنے کی اجازت دیں؟</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> آئٹمز میں ترمیم کی جا رہی ہے…</item>
- <item quantity="one">آئٹم میں ترمیم کی جا رہی ہے…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> آڈیو فائلز کو کوڑے دان میں منتقل کرنے کی اجازت دیں؟</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> کو اس آڈیو فائل کو کوڑے دان میں منتقل کرنے کی اجازت دیں؟</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> آڈیو فائلز کو کوڑے دان میں منتقل کیا جا رہا ہے…</item>
- <item quantity="one">آڈیو فائل کو کوڑے دان میں منتقل کیا جا رہا ہے…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> ویڈیوز کو کوڑے دان میں منتقل کرنے کی اجازت دیں؟</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> کو اس ویڈیو کو کوڑے دان میں منتقل کرنے کی اجازت دیں؟</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ویڈیوز کو کوڑے دان میں منتقل کیا جا رہا ہے…</item>
- <item quantity="one">ویڈیو کو کوڑے دان میں منتقل کیا جا رہا ہے…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> تصاویر کو کوڑے دان میں منتقل کرنے کی اجازت دیں؟</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> کو اس تصویر کو کوڑے دان میں منتقل کرنے کی اجازت دیں؟</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> تصاویر کو کوڑے دان میں منتقل کیا جا رہا ہے…</item>
- <item quantity="one">تصویر کو کوڑے دان میں منتقل کیا جا رہا ہے…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> آئٹمز کو کوڑے دان میں منتقل کرنے کی اجازت دیں؟</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> کو اس آئٹم کو کوڑے دان میں منتقل کرنے کی اجازت دیں؟</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> آئٹمز کو کوڑے دان میں منتقل کیا جا رہا ہے…</item>
- <item quantity="one">آئٹم کو کوڑے دان میں منتقل کیا جا رہا ہے…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> آڈیو فائلز کو کوڑے دان سے باہر منتقل کرنے کی اجازت دیں؟</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> کو اس آڈیو فائل کو کوڑے دان سے باہر منتقل کرنے کی اجازت دیں؟</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> آڈیو فائلز کو کوڑے دان سے باہر منتقل کیا جا ریا ہے…</item>
- <item quantity="one">آڈیو فائل کو کوڑے دان سے باہر منتقل کیا جا ریا ہے…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> ویڈیوز کو کوڑے دان سے باہر منتقل کرنے کی اجازت دیں؟</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> کو اس ویڈیو کو کوڑے دان سے باہر منتقل کرنے کی اجازت دیں؟</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ویڈیوز کو کوڑے دان سے باہر منتقل کیا جا رہا ہے…</item>
- <item quantity="one">ویڈیو کو کوڑے دان سے باہر منتقل کیا جا رہا ہے…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> تصاویر کو کوڑے دان سے باہر منتقل کرنے کی اجازت دیں؟</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> کو اس تصویر کو کوڑے دان سے باہر منتقل کرنے کی اجازت دیں؟</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> تصاویر کو کوڑے دان سے باہر منتقل کیا جا رہا ہے…</item>
- <item quantity="one">تصویر کو کوڑے دان سے باہر منتقل کیا جا رہا ہے…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> آئٹمز کو کوڑے دان سے باہر منتقل کرنے کی اجازت دیں؟</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> کو اس آئٹم کو کوڑے دان سے باہر منتقل کرنے کی اجازت دیں؟</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> آئٹمز کو کوڑے دان سے باہر منتقل کیا جا رہا ہے…</item>
- <item quantity="one">آئٹم کو کوڑے دان سے باہر منتقل کیا جا رہا ہے…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> آڈیو فائلز کو حذف کرنے کی اجازت دیں؟</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> کو اس آڈیو فائل کو حذف کرنے کی اجازت دیں؟</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> آڈیو فائلز حذف کی جا رہی ہیں…</item>
- <item quantity="one">آڈیو فائل حذف کی جا رہی ہے…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> ویڈیوز کو حذف کرنے کی اجازت دیں؟</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> کو اس ویڈیو کو حذف کرنے کی اجازت دیں؟</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ویڈیوز حذف کی جا رہی ہیں…</item>
- <item quantity="one">ویڈیو حذف کی جا رہی ہے…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> تصاویر کو حذف کرنے کی اجازت دیں؟</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> کو اس تصویر کو حذف کرنے کی اجازت دیں؟</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> تصاویر حذف کی جا رہی ہیں…</item>
- <item quantity="one">تصویر حذف کی جا رہی ہے…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> آئٹمز کو حذف کرنے کی اجازت دیں؟</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> کو اس آئٹم کو حذف کرنے کی اجازت دیں؟</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> آئٹمز حذف کئے جا رہے ہیں…</item>
- <item quantity="one">آئٹم حذف کیا جا رہا ہے…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"شامل کریں"</string>
+ <string name="deselect" msgid="4297825044827769490">"غیر منتخب کریں"</string>
+ <string name="deselected" msgid="8488133193326208475">"غیر منتخب کردہ"</string>
+ <string name="select" msgid="2704765470563027689">"منتخب کریں"</string>
+ <string name="selected" msgid="9151797369975828124">"منتخب کردہ"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> آئٹم تک منتخب کریں}other{<xliff:g id="COUNT_1">^1</xliff:g> آئٹمز تک منتخب کریں}}"</string>
+ <string name="recent" msgid="6694613584743207874">"حالیہ"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"کوئی تصویر یا ویڈیو نہیں"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"کوئی البم نہیں"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"منتخب کردہ دیکھیں"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"تصاویر"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"البمز"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"پیش منظر"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"کام پر سوئچ کریں"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"ذاتی پروفائل پر سوئچ کریں"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"آپ کے منتظم کے ذریعے مسدود کردہ ہے"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"ذاتی ایپ سے دفتری ڈیٹا تک رسائی کی اجازت نہیں ہے"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"ورک ایپ سے ذاتی ڈیٹا تک رسائی کی اجازت نہیں ہے"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"ورک ایپس موقوف ہیں"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"کام کی تصاویر کھولنے کے لیے اپنی ورک ایپس آن کریں، پھر دوبارہ کوشش کریں"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"یہ ایپ صرف آپ کی منتخب کردہ تصاویر تک رسائی حاصل کر سکتی ہے"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> آئٹم}other{<xliff:g id="COUNT_1">^1</xliff:g> آئٹمز}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"(<xliff:g id="COUNT">^1</xliff:g>) شامل کریں"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"کیمرا"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"ڈاؤن لوڈز"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"پسندیدہ"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"اسکرین شاٹس"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"موشَن تصویر"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="TIME">%2$s</xliff:g> پر <xliff:g id="ITEM_NAME">%1$s</xliff:g> لیا گیا"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"<xliff:g id="TIME">%1$s</xliff:g> کو <xliff:g id="DURATION">%2$s</xliff:g> دورانیے کے ساتھ بنائی گئی ویڈیو"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"تصویر"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"موشَن تصویر"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"ویڈیو خاموش کریں"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"ویڈیو کی آواز چالو کریں"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"ویڈیو چلائیں"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"ویڈیو موقوف کریں"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"کلاؤڈ میڈیا اب <xliff:g id="PKG_NAME">%1$s</xliff:g> سے دستیاب ہے"</string>
+ <string name="not_selected" msgid="2244008151669896758">"غیر منتخب کردہ"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> کو اس آڈیو فائل میں ترمیم کرنے کی اجازت دیں؟}other{<xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> آڈیو فائلز میں ترمیم کرنے کی اجازت دیں؟}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{آڈیو فائل میں ترمیم کی جا رہی ہے…}other{<xliff:g id="COUNT">^1</xliff:g> آڈیو فائلز میں ترمیم کی جا رہی ہے…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> کو اس ویڈیو میں ترمیم کرنے کی اجازت دیں؟}other{<xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> ویڈیوز میں ترمیم کرنے کی اجازت دیں؟}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{ویڈیو میں ترمیم کی جا رہی ہے…}other{<xliff:g id="COUNT">^1</xliff:g> ویڈیوز میں ترمیم کی جا رہی ہے…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> کو اس تصویر میں ترمیم کرنے کی اجازت دیں؟}other{<xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> تصاویر میں ترمیم کرنے کی اجازت دیں؟}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{تصویر میں ترمیم کی جا رہی ہے…}other{<xliff:g id="COUNT">^1</xliff:g> تصاویر میں ترمیم کی جا رہی ہے…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> کو اس آئٹم میں ترمیم کرنے کی اجازت دیں؟}other{<xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> آئٹمز میں ترمیم کرنے کی اجازت دیں؟}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{آئٹم میں ترمیم کی جا رہی ہے…}other{<xliff:g id="COUNT">^1</xliff:g> آئٹمز میں ترمیم کی جا رہی ہے…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> کو اس آڈیو فائل کو کوڑے دان میں منتقل کرنے کی اجازت دیں؟}other{<xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> آڈیو فائلز کو کوڑے دان میں منتقل کرنے کی اجازت دیں؟}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{آڈیو فائل کو کوڑے دان میں منتقل کیا جا رہا ہے…}other{<xliff:g id="COUNT">^1</xliff:g> آڈیو فائلز کو کوڑے دان میں منتقل کیا جا رہا ہے…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> کو اس ویڈیو کو کوڑے دان میں منتقل کرنے کی اجازت دیں؟}other{<xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> ویڈیوز کو کوڑے دان میں منتقل کرنے کی اجازت دیں؟}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{ویڈیو کو کوڑے دان میں منتقل کیا جا رہا ہے…}other{<xliff:g id="COUNT">^1</xliff:g> ویڈیوز کو کوڑے دان میں منتقل کیا جا رہا ہے…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> کو اس تصویر کو کوڑے دان میں منتقل کرنے کی اجازت دیں؟}other{<xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> تصاویر کو کوڑے دان میں منتقل کرنے کی اجازت دیں؟}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{تصویر کو کوڑے دان میں منتقل کیا جا رہا ہے…}other{<xliff:g id="COUNT">^1</xliff:g> تصاویر کو کوڑے دان میں منتقل کیا جا رہا ہے…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> کو اس آئٹم کو کوڑے دان میں منتقل کرنے کی اجازت دیں؟}other{<xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> آئٹمز کو کوڑے دان میں منتقل کرنے کی اجازت دیں؟}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{آئٹم کو کوڑے دان میں منتقل کیا جا رہا ہے…}other{<xliff:g id="COUNT">^1</xliff:g> آئٹمز کو کوڑے دان میں منتقل کیا جا رہا ہے…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> کو اس آڈیو فائل کو کوڑے دان سے باہر منتقل کرنے کی اجازت دیں؟}other{<xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> آڈیو فائلز کو کوڑے دان سے باہر منتقل کرنے کی اجازت دیں؟}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{آڈیو فائل کو کوڑے دان سے باہر منتقل کیا جا ریا ہے…}other{<xliff:g id="COUNT">^1</xliff:g> آڈیو فائلز کو کوڑے دان سے باہر منتقل کیا جا ریا ہے…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> کو اس ویڈیو کو کوڑے دان سے باہر منتقل کرنے کی اجازت دیں؟}other{<xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> ویڈیوز کو کوڑے دان سے باہر منتقل کرنے کی اجازت دیں؟}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{ویڈیو کو کوڑے دان سے باہر منتقل کیا جا رہا ہے…}other{<xliff:g id="COUNT">^1</xliff:g> ویڈیوز کو کوڑے دان سے باہر منتقل کیا جا رہا ہے…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> کو اس تصویر کو کوڑے دان سے باہر منتقل کرنے کی اجازت دیں؟}other{<xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> تصاویر کو کوڑے دان سے باہر منتقل کرنے کی اجازت دیں؟}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{تصویر کو کوڑے دان سے باہر منتقل کیا جا رہا ہے…}other{<xliff:g id="COUNT">^1</xliff:g> تصاویر کو کوڑے دان سے باہر منتقل کیا جا رہا ہے…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> کو اس آئٹم کو کوڑے دان سے باہر منتقل کرنے کی اجازت دیں؟}other{<xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> آئٹمز کو کوڑے دان سے باہر منتقل کرنے کی اجازت دیں؟}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{آئٹم کو کوڑے دان سے باہر منتقل کیا جا رہا ہے…}other{<xliff:g id="COUNT">^1</xliff:g> آئٹمز کو کوڑے دان سے باہر منتقل کیا جا رہا ہے…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> کو اس آڈیو فائل کو حذف کرنے کی اجازت دیں؟}other{<xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> آڈیو فائلز کو حذف کرنے کی اجازت دیں؟}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{آڈیو فائل حذف کی جا رہی ہے…}other{<xliff:g id="COUNT">^1</xliff:g> آڈیو فائلز حذف کی جا رہی ہیں…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> کو اس ویڈیو کو حذف کرنے کی اجازت دیں؟}other{<xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> ویڈیوز کو حذف کرنے کی اجازت دیں؟}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{ویڈیو حذف کی جا رہی ہے…}other{<xliff:g id="COUNT">^1</xliff:g> ویڈیوز حذف کی جا رہی ہیں…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> کو اس تصویر کو حذف کرنے کی اجازت دیں؟}other{<xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> تصاویر کو حذف کرنے کی اجازت دیں؟}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{تصویر حذف کی جا رہی ہے…}other{<xliff:g id="COUNT">^1</xliff:g> تصاویر حذف کی جا رہی ہیں…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> کو اس آئٹم کو حذف کرنے کی اجازت دیں؟}other{<xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g id="COUNT">^2</xliff:g> آئٹمز کو حذف کرنے کی اجازت دیں؟}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{آئٹم حذف کیا جا رہا ہے…}other{<xliff:g id="COUNT">^1</xliff:g> آئٹمز حذف کئے جا رہے ہیں…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> میڈیا فائلز پر کارروائی نہیں کر سکتی"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"میڈیا پر کارروائی منسوخ ہو گئی"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"میڈیا پر کارروائی کرنے میں خرابی"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 35d3e80..9677a11 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Multimedia"</string>
<string name="storage_description" msgid="4081716890357580107">"Mahalliy xotira"</string>
<string name="app_label" msgid="9035307001052716210">"Multimedia xotirasi"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"Surat tanlash oynasi"</string>
<string name="artist_label" msgid="8105600993099120273">"Ijrochi"</string>
<string name="unknown" msgid="2059049215682829375">"Noaniq"</string>
<string name="root_images" msgid="5861633549189045666">"Rasmlar"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Davom etish"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Ruxsat"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Rad etish"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Yana <xliff:g id="COUNT_1">^1</xliff:g> ta qoʻshimcha element</item>
- <item quantity="one">Yana <xliff:g id="COUNT_0">^1</xliff:g> ta qoʻshimcha element</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Yana <xliff:g id="COUNT_0">^1</xliff:g> ta qoʻshimcha element}other{Yana <xliff:g id="COUNT_1">^1</xliff:g> ta qoʻshimcha element}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Ilovaga oid vaqtincha fayllarni tozalash"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ayrim vaqtincha fayllarni tozalamoqchi. Bunda batareya quvvati yoki internet trafigi ortiqcha sarflanishi mumkin."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Ilovaga oid vaqtincha fayllar tozalanmoqda…"</string>
<string name="clear" msgid="5524638938415865915">"Tozalash"</string>
<string name="allow" msgid="8885707816848569619">"Ruxsat"</string>
<string name="deny" msgid="6040983710442068936">"Rad etish"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta audio faylni oʻzgartirishi uchun ruxsat berilsinmi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu audio faylni oʻzgartirishi uchun ruxsat berilsinmi?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ta audio fayl oʻzgartirilmoqda…</item>
- <item quantity="one">Audio fayl oʻzgartirilmoqda…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta videoni oʻzgartirishi uchun ruxsat berilsinmi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu videoni oʻzgartirishi uchun ruxsat berilsinmi?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ta video oʻzgartirilmoqda…</item>
- <item quantity="one">Video oʻzgartirilmoqda…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> uchun <xliff:g id="COUNT">^2</xliff:g> ta suratni oʻzgartirishga ruxsat berilsinmi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> uchun bu suratni oʻzgartirishga ruxsat berilsinmi?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ta rasm oʻzgartirilmoqda…</item>
- <item quantity="one">Rasm oʻzgartirilmoqda…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta elementni oʻzgartirishi uchun ruxsat berilsinmi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu elementni oʻzgartirishi uchun ruxsat berilsinmi?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ta element oʻzgartirilmoqda…</item>
- <item quantity="one">Element oʻzgartirilmoqda…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta audio faylni chiqitdonga tashlashi uchun ruxsat berilsinmi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu audio faylni chiqitdonga tashlashi uchun ruxsat berilsinmi?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ta audio fayl chiqitdonga tashlanmoqda…</item>
- <item quantity="one">Audio fayl chiqitdonga tashlanmoqda…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta videoni chiqitdonga tashlashi uchun ruxsat berilsinmi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu videoni chiqitdonga tashlashi uchun ruxsat berilsinmi?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ta video chiqitdonga tashlanmoqda…</item>
- <item quantity="one">Video chiqitdonga tashlanmoqda…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> uchun <xliff:g id="COUNT">^2</xliff:g> ta suratni chiqitdonga tashlashga ruxsat berilsinmi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> uchun bu suratni chiqitdonga tashlashga ruxsat berilsinmi?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ta rasm chiqitdonga tashlanmoqda…</item>
- <item quantity="one">Rasm chiqitdonga tashlanmoqda…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta elementni chiqitdonga tashlashi uchun ruxsat berilsinmi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu elementni chiqitdonga tashlashi uchun ruxsat berilsinmi?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ta element chiqitdonga tashlanmoqda…</item>
- <item quantity="one">Element chiqitdonga tashlanmoqda…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta audio faylni chiqitdondan chiqarib olishi uchun ruxsat berilsinmi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu audio faylni chiqitdondan chiqarib olishi uchun ruxsat berilsinmi?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ta audio fayl chiqitdondan chiqarilmoqda…</item>
- <item quantity="one">Audio fayl chiqitdondan chiqarilmoqda…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta videoni chiqitdondan chiqarib olishi uchun ruxsat berilsinmi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu videoni chiqitdondan chiqarib olishi uchun ruxsat berilsinmi?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ta video chiqitdondan chiqarilmoqda…</item>
- <item quantity="one">Video chiqitdondan chiqarilmoqda…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> uchun <xliff:g id="COUNT">^2</xliff:g> ta suratni chiqitdondan qayta tiklashiga ruxsat berilsinmi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> uchun bu suratni chiqitdondan qayta tiklashiga ruxsat berilsinmi?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ta rasm chiqitdondan chiqarilmoqda…</item>
- <item quantity="one">Rasm chiqitdondan chiqarilmoqda…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta elementni chiqitdondan chiqarib olishi uchun ruxsat berilsinmi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu elementni chiqitdondan chiqarib olishi uchun ruxsat berilsinmi?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ta element chiqitdondan chiqarilmoqda…</item>
- <item quantity="one">Element chiqitdondan chiqarilmoqda…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta audio faylni oʻchirib tashlashi uchun ruxsat berilsinmi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu audio faylni oʻchirib tashlashi uchun ruxsat berilsinmi?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ta audio fayl oʻchirilmoqda…</item>
- <item quantity="one">Audio fayl oʻchirilmoqda…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta videoni oʻchirib tashlashi uchun ruxsat berilsinmi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu videoni oʻchirib tashlashi uchun ruxsat berilsinmi?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ta video oʻchirilmoqda…</item>
- <item quantity="one">Video oʻchirilmoqda…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> uchun <xliff:g id="COUNT">^2</xliff:g> ta suratni oʻchirishga ruxsat berilsinmi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> uchun bu suratni oʻchirishga ruxsat berilsinmi?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ta rasm oʻchirilmoqda…</item>
- <item quantity="one">Rasm oʻchirilmoqda…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta elementni oʻchirib tashlashi uchun ruxsat berilsinmi?</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu elementni oʻchirib tashlashi uchun ruxsat berilsinmi?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> ta element oʻchirilmoqda…</item>
- <item quantity="one">Element oʻchirilmoqda…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Kiritish"</string>
+ <string name="deselect" msgid="4297825044827769490">"Tanlovni bekor qilish"</string>
+ <string name="deselected" msgid="8488133193326208475">"Tanlovi yechilgan"</string>
+ <string name="select" msgid="2704765470563027689">"Tanlash"</string>
+ <string name="selected" msgid="9151797369975828124">"Tanlangan"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> tagcha elementni tanlang}other{<xliff:g id="COUNT_1">^1</xliff:g> tagcha elementni tanlang}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Oxirgi"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Surat yoki video kiritilmagan"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Albom kiritilmagan"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Tanlanganni koʻrish"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Suratlar"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Albomlar"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Razm solish"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Ish profiliga oʻtish"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Shaxsiy profilga oʻtish"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Administratoringiz tomonidan bloklangan"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Shaxsiy ilovadan ishga oid maʼlumotlarga kirish taqiqlangan"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Ishga oid ilovadan shaxsiy maʼlumotlarga kirish taqiqlangan"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Ishga oid ilovalar pauza qilingan"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Ishga oid suratlarni ochish uchun ishga oid ilovalarni yoqib, keyin qaytadan urining"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Bu ilova faqat siz tanlagan suratlar bilan ishlay oladi"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> ta narsa}other{<xliff:g id="COUNT_1">^1</xliff:g> ta narsa}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Kiritish (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Yuklanmalar"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Sevimlilar"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Skrinshotlar"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Harakatli surat"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> olingan sana: <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Video tasvirga olingan sana: <xliff:g id="TIME">%1$s</xliff:g>, davomiyligi: <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Surat"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Harakatli surat"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Video ovozini oʻchirish"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Video ovozini yoqish"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Videoni ochish"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Videoni pauza qilish"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Endi <xliff:g id="PKG_NAME">%1$s</xliff:g> bulutli media kontenti mavjud"</string>
+ <string name="not_selected" msgid="2244008151669896758">"tanlanmagan"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu audio faylni oʻzgartirishi uchun ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta audio faylni oʻzgartirishi uchun ruxsat berilsinmi?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Audio fayl oʻzgartirilmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta audio fayl oʻzgartirilmoqda…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu videoni oʻzgartirishi uchun ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta videoni oʻzgartirishi uchun ruxsat berilsinmi?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Video oʻzgartirilmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta video oʻzgartirilmoqda…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> uchun bu suratni oʻzgartirishga ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> uchun <xliff:g id="COUNT">^2</xliff:g> ta suratni oʻzgartirishga ruxsat berilsinmi?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Rasm oʻzgartirilmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta rasm oʻzgartirilmoqda…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu elementni oʻzgartirishi uchun ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta elementni oʻzgartirishi uchun ruxsat berilsinmi?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Element oʻzgartirilmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta element oʻzgartirilmoqda…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu audio faylni chiqitdonga tashlashi uchun ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta audio faylni chiqitdonga tashlashi uchun ruxsat berilsinmi?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Audio fayl chiqitdonga tashlanmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta audio fayl chiqitdonga tashlanmoqda…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu videoni chiqitdonga tashlashi uchun ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta videoni chiqitdonga tashlashi uchun ruxsat berilsinmi?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Video chiqitdonga tashlanmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta video chiqitdonga tashlanmoqda…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> uchun bu suratni chiqitdonga tashlashga ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> uchun <xliff:g id="COUNT">^2</xliff:g> ta suratni chiqitdonga tashlashga ruxsat berilsinmi?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Rasm chiqitdonga tashlanmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta rasm chiqitdonga tashlanmoqda…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu elementni chiqitdonga tashlashi uchun ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta elementni chiqitdonga tashlashi uchun ruxsat berilsinmi?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Element chiqitdonga tashlanmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta element chiqitdonga tashlanmoqda…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu audio faylni chiqitdondan chiqarib olishi uchun ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta audio faylni chiqitdondan chiqarib olishi uchun ruxsat berilsinmi?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Audio fayl chiqitdondan chiqarilmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta audio fayl chiqitdondan chiqarilmoqda…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu videoni chiqitdondan chiqarib olishi uchun ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta videoni chiqitdondan chiqarib olishi uchun ruxsat berilsinmi?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Video chiqitdondan chiqarilmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta video chiqitdondan chiqarilmoqda…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> uchun bu suratni chiqitdondan qayta tiklashga ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> uchun <xliff:g id="COUNT">^2</xliff:g> ta suratni chiqitdondan qayta tiklashga ruxsat berilsinmi?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Rasm chiqitdondan chiqarilmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta rasm chiqitdondan chiqarilmoqda…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu elementni chiqitdondan chiqarib olishi uchun ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta elementni chiqitdondan chiqarib olishi uchun ruxsat berilsinmi?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Element chiqitdondan chiqarilmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta element chiqitdondan chiqarilmoqda…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu audio faylni oʻchirib tashlashi uchun ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta audio faylni oʻchirib tashlashi uchun ruxsat berilsinmi?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Audio fayl oʻchirilmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta audio fayl oʻchirilmoqda…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu videoni oʻchirib tashlashi uchun ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta videoni oʻchirib tashlashi uchun ruxsat berilsinmi?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Video oʻchirilmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta video oʻchirilmoqda…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> uchun bu suratni oʻchirishga ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> uchun <xliff:g id="COUNT">^2</xliff:g> ta suratni oʻchirishga ruxsat berilsinmi?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Rasm oʻchirilmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta rasm oʻchirilmoqda…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu elementni oʻchirib tashlashi uchun ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta elementni oʻchirib tashlashi uchun ruxsat berilsinmi?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Element oʻchirilmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta element oʻchirilmoqda…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> media fayllarni ijro qila olmaydi"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Mediaga ishlov berish bekor qilindi"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Mediaga ishlov berishda xatolik yuz berdi"</string>
diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml
new file mode 100644
index 0000000..53fac06
--- /dev/null
+++ b/res/values-v31/colors.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<resources>
+
+ <!-- PhotoPicker -->
+ <color name="picker_background_color">@color/surface_light</color>
+
+ <!-- PhotoPicker check button -->
+ <color name="picker_unselected_check_color">@android:color/system_neutral1_100</color>
+
+ <!-- PhotoPicker Preview -->
+ <color name="preview_highlight_color">@android:color/system_accent1_100</color>
+
+</resources>
diff --git a/res/values-v31/dimens.xml b/res/values-v31/dimens.xml
new file mode 100644
index 0000000..e9b11ee
--- /dev/null
+++ b/res/values-v31/dimens.xml
@@ -0,0 +1,36 @@
+<?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.
+-->
+
+<resources>
+
+ <dimen name="picker_tab_radius">12dp</dimen>
+ <dimen name="picker_tab_height">36dp</dimen>
+ <dimen name="picker_tab_width">96dp</dimen>
+ <dimen name="picker_tab_min_width">104dp</dimen>
+
+ <dimen name="picker_bottom_bar_size">72dp</dimen>
+
+ <dimen name="picker_recycler_view_bottom_padding">92dp</dimen>
+
+ <dimen name="picker_toolbar_title_text_size">22sp</dimen>
+
+ <dimen name="preview_mute_marginBottom">88dp</dimen>
+
+ <!-- PhotoPicker Work Profile -->
+ <dimen name="picker_profile_dialog_radius">28dp</dimen>
+ <dimen name="picker_profile_dialog_scrim">0.8</dimen>
+
+</resources>
diff --git a/res/values-v31/styles.xml b/res/values-v31/styles.xml
new file mode 100644
index 0000000..1c7f9b5
--- /dev/null
+++ b/res/values-v31/styles.xml
@@ -0,0 +1,39 @@
+<?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.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <style name="PickerMaterialTheme" parent="@style/Theme.Material3.DayNight.NoActionBar">
+ <item name="materialAlertDialogTheme">@style/ProfileDialogTheme</item>
+ <item name="pickerDragBarColor">@android:color/system_neutral2_100</item>
+ <item name="pickerHighlightColor">@android:color/system_accent1_100</item>
+ <item name="pickerHighlightTextColor">?android:attr/textColorPrimary</item>
+ <item name="pickerProfileButtonColor">@android:color/system_accent1_100</item>
+ <item name="pickerDisabledProfileButtonColor">?android:attr/colorBackground</item>
+ <item name="pickerProfileButtonTextColor">?android:attr/textColorPrimary</item>
+ <item name="pickerDisabledProfileButtonTextColor">?android:attr/textColorTertiaryInverse</item>
+ <item name="pickerSelectedTabBackgroundColor">@android:color/system_accent1_100</item>
+ <item name="pickerSelectedTabTextColor">?android:attr/textColorPrimary</item>
+ <item name="pickerTabBackgroundColor">?android:attr/colorBackground</item>
+ <item name="pickerTextColor">?android:attr/textColorPrimary</item>
+ <item name="pickerSelectedColor">@android:color/system_accent1_600</item>
+ <item name="pickerProfileDialogButtonAndIconColor">@android:color/system_accent1_600</item>
+ <item name="pickerProfileDialogTitleColor">?android:attr/textColorPrimary</item>
+ <item name="pickerProfileDialogBodyColor">?android:attr/textColorSecondary</item>
+ <item name="pickerProfileDialogBackgroundColor">@color/surface_light</item>
+ </style>
+
+</resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 87f6201..5566ffc 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Phương tiện"</string>
<string name="storage_description" msgid="4081716890357580107">"Bộ nhớ cục bộ"</string>
<string name="app_label" msgid="9035307001052716210">"Bộ nhớ phương tiện"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"Bộ chọn ảnh"</string>
<string name="artist_label" msgid="8105600993099120273">"Nghệ sĩ"</string>
<string name="unknown" msgid="2059049215682829375">"Không xác định"</string>
<string name="root_images" msgid="5861633549189045666">"Hình ảnh"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Tiếp tục"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Cho phép"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Từ chối"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">Và <xliff:g id="COUNT_1">^1</xliff:g> mục khác</item>
- <item quantity="one">Và <xliff:g id="COUNT_0">^1</xliff:g> mục khác</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Và <xliff:g id="COUNT_0">^1</xliff:g> mục khác}other{Và <xliff:g id="COUNT_1">^1</xliff:g> mục khác}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Xóa tệp ứng dụng tạm thời"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> muốn xóa một số tệp tạm thời. Điều này có thể làm tăng mức sử dụng pin hoặc dữ liệu di động."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Đang xóa tệp ứng dụng tạm thời…"</string>
<string name="clear" msgid="5524638938415865915">"Xóa"</string>
<string name="allow" msgid="8885707816848569619">"Cho phép"</string>
<string name="deny" msgid="6040983710442068936">"Từ chối"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> sửa đổi <xliff:g id="COUNT">^2</xliff:g> tệp âm thanh?</item>
- <item quantity="one">Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> sửa đổi tệp âm thanh này?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">Đang sửa đổi <xliff:g id="COUNT">^1</xliff:g> tệp âm thanh…</item>
- <item quantity="one">Đang sửa đổi tệp âm thanh…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> sửa đổi <xliff:g id="COUNT">^2</xliff:g> video?</item>
- <item quantity="one">Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> sửa đổi video này?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">Đang sửa đổi <xliff:g id="COUNT">^1</xliff:g> video…</item>
- <item quantity="one">Đang sửa đổi video…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> sửa đổi <xliff:g id="COUNT">^2</xliff:g> ảnh?</item>
- <item quantity="one">Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> sửa đổi ảnh này?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">Đang sửa đổi <xliff:g id="COUNT">^1</xliff:g> ảnh…</item>
- <item quantity="one">Đang sửa đổi ảnh…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> sửa đổi <xliff:g id="COUNT">^2</xliff:g> mục?</item>
- <item quantity="one">Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> sửa đổi mục này?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">Đang sửa đổi <xliff:g id="COUNT">^1</xliff:g> mục…</item>
- <item quantity="one">Đang sửa đổi mục…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> chuyển <xliff:g id="COUNT">^2</xliff:g> tệp âm thanh vào thùng rác?</item>
- <item quantity="one">Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> chuyển tệp âm thanh này vào thùng rác?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">Đang chuyển <xliff:g id="COUNT">^1</xliff:g> tệp âm thanh vào thùng rác…</item>
- <item quantity="one">Đang chuyển tệp âm thanh vào thùng rác…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> chuyển <xliff:g id="COUNT">^2</xliff:g> video vào thùng rác?</item>
- <item quantity="one">Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> chuyển video này vào thùng rác?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">Đang chuyển <xliff:g id="COUNT">^1</xliff:g> video vào thùng rác…</item>
- <item quantity="one">Đang chuyển video vào thùng rác…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> chuyển <xliff:g id="COUNT">^2</xliff:g> ảnh vào thùng rác?</item>
- <item quantity="one">Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> chuyển ảnh này vào thùng rác?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">Đang chuyển <xliff:g id="COUNT">^1</xliff:g> ảnh vào thùng rác…</item>
- <item quantity="one">Đang chuyển ảnh vào thùng rác…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> chuyển <xliff:g id="COUNT">^2</xliff:g> mục vào thùng rác?</item>
- <item quantity="one">Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> chuyển mục này vào thùng rác?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">Đang chuyển <xliff:g id="COUNT">^1</xliff:g> mục vào thùng rác…</item>
- <item quantity="one">Đang chuyển mục vào thùng rác…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> chuyển <xliff:g id="COUNT">^2</xliff:g> tệp âm thanh ra khỏi thùng rác?</item>
- <item quantity="one">Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> chuyển tệp âm thanh này ra khỏi thùng rác?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">Đang chuyển <xliff:g id="COUNT">^1</xliff:g> tệp âm thanh ra khỏi thùng rác…</item>
- <item quantity="one">Đang chuyển tệp âm thanh ra khỏi thùng rác…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> chuyển <xliff:g id="COUNT">^2</xliff:g> video ra khỏi thùng rác?</item>
- <item quantity="one">Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> chuyển video này ra khỏi thùng rác?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">Đang chuyển <xliff:g id="COUNT">^1</xliff:g> video ra khỏi thùng rác…</item>
- <item quantity="one">Đang chuyển video ra khỏi thùng rác…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> chuyển <xliff:g id="COUNT">^2</xliff:g> ảnh ra khỏi thùng rác?</item>
- <item quantity="one">Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> chuyển ảnh này ra khỏi thùng rác?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">Đang chuyển <xliff:g id="COUNT">^1</xliff:g> ảnh ra khỏi thùng rác…</item>
- <item quantity="one">Đang chuyển ảnh ra khỏi thùng rác…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> chuyển <xliff:g id="COUNT">^2</xliff:g> mục ra khỏi thùng rác?</item>
- <item quantity="one">Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> chuyển mục này ra khỏi thùng rác?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">Đang chuyển <xliff:g id="COUNT">^1</xliff:g> mục ra khỏi thùng rác…</item>
- <item quantity="one">Đang chuyển mục ra khỏi thùng rác…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> xóa <xliff:g id="COUNT">^2</xliff:g> tệp âm thanh?</item>
- <item quantity="one">Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> xóa tệp âm thanh này?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">Đang xóa <xliff:g id="COUNT">^1</xliff:g> tệp âm thanh…</item>
- <item quantity="one">Đang xóa tệp âm thanh…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> xóa <xliff:g id="COUNT">^2</xliff:g> video?</item>
- <item quantity="one">Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> xóa video này?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">Đang xóa <xliff:g id="COUNT">^1</xliff:g> video…</item>
- <item quantity="one">Đang xóa video…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> xóa <xliff:g id="COUNT">^2</xliff:g> ảnh?</item>
- <item quantity="one">Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> xóa ảnh này?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">Đang xóa <xliff:g id="COUNT">^1</xliff:g> ảnh…</item>
- <item quantity="one">Đang xóa ảnh…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> xóa <xliff:g id="COUNT">^2</xliff:g> mục?</item>
- <item quantity="one">Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> xóa mục này?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">Đang xóa <xliff:g id="COUNT">^1</xliff:g> mục…</item>
- <item quantity="one">Đang xóa mục…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Thêm"</string>
+ <string name="deselect" msgid="4297825044827769490">"Bỏ chọn"</string>
+ <string name="deselected" msgid="8488133193326208475">"Đã bỏ chọn"</string>
+ <string name="select" msgid="2704765470563027689">"Chọn"</string>
+ <string name="selected" msgid="9151797369975828124">"Đã chọn"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Chọn tối đa <xliff:g id="COUNT_0">^1</xliff:g> mục}other{Chọn tối đa <xliff:g id="COUNT_1">^1</xliff:g> mục}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Gần đây"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Không có ảnh hoặc video nào"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Không có đĩa nhạc nào"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Xem các mục được chọn"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Ảnh"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Album"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Xem trước"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Chuyển sang hồ sơ công việc"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Chuyển sang hồ sơ cá nhân"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Bị quản trị viên của bạn chặn"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Bạn không được phép truy cập dữ liệu công việc từ một ứng dụng cá nhân"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Bạn không được phép truy cập dữ liệu cá nhân từ một ứng dụng công việc"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Ứng dụng công việc đã tạm dừng"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Để mở ảnh trong hồ sơ công việc, hãy bật ứng dụng công việc rồi thử lại"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Ứng dụng này chỉ có thể truy cập vào những bức ảnh bạn chọn"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> mục}other{<xliff:g id="COUNT_1">^1</xliff:g> mục}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Thêm (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Máy ảnh"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Tệp đã tải xuống"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Mục yêu thích"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Ảnh chụp màn hình"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Ảnh chuyển động"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> được chụp vào <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Video được quay lúc <xliff:g id="TIME">%1$s</xliff:g> với thời lượng <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Ảnh"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Ảnh chuyển động"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Tắt tiếng video"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Bật tiếng video"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Phát video"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Tạm dừng video"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Hiện đã có phương tiện đám mây từ <xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"chưa được chọn"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> sửa đổi tệp âm thanh này?}other{Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> sửa đổi <xliff:g id="COUNT">^2</xliff:g> tệp âm thanh?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Đang sửa đổi tệp âm thanh…}other{Đang sửa đổi <xliff:g id="COUNT">^1</xliff:g> tệp âm thanh…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> sửa đổi video này?}other{Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> sửa đổi <xliff:g id="COUNT">^2</xliff:g> video?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Đang sửa đổi video…}other{Đang sửa đổi <xliff:g id="COUNT">^1</xliff:g> video…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> sửa đổi ảnh này?}other{Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> sửa đổi <xliff:g id="COUNT">^2</xliff:g> ảnh?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Đang sửa đổi ảnh…}other{Đang sửa đổi <xliff:g id="COUNT">^1</xliff:g> ảnh…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> sửa đổi mục này?}other{Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> sửa đổi <xliff:g id="COUNT">^2</xliff:g> mục?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Đang sửa đổi mục…}other{Đang sửa đổi <xliff:g id="COUNT">^1</xliff:g> mục…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> chuyển tệp âm thanh này vào thùng rác?}other{Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> chuyển <xliff:g id="COUNT">^2</xliff:g> tệp âm thanh vào thùng rác?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Đang chuyển tệp âm thanh vào thùng rác…}other{Đang chuyển <xliff:g id="COUNT">^1</xliff:g> tệp âm thanh vào thùng rác…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> chuyển video này vào thùng rác?}other{Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> chuyển <xliff:g id="COUNT">^2</xliff:g> video vào thùng rác?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Đang chuyển video vào thùng rác…}other{Đang chuyển <xliff:g id="COUNT">^1</xliff:g> video vào thùng rác…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> chuyển ảnh này vào thùng rác?}other{Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> chuyển <xliff:g id="COUNT">^2</xliff:g> ảnh vào thùng rác?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Đang chuyển ảnh vào thùng rác…}other{Đang chuyển <xliff:g id="COUNT">^1</xliff:g> ảnh vào thùng rác…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> chuyển mục này vào thùng rác?}other{Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> chuyển <xliff:g id="COUNT">^2</xliff:g> mục vào thùng rác?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Đang chuyển mục vào thùng rác…}other{Đang chuyển <xliff:g id="COUNT">^1</xliff:g> mục vào thùng rác…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> chuyển tệp âm thanh này ra khỏi thùng rác?}other{Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> chuyển <xliff:g id="COUNT">^2</xliff:g> tệp âm thanh ra khỏi thùng rác?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Đang chuyển tệp âm thanh ra khỏi thùng rác…}other{Đang chuyển <xliff:g id="COUNT">^1</xliff:g> tệp âm thanh ra khỏi thùng rác…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> chuyển video này ra khỏi thùng rác?}other{Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> chuyển <xliff:g id="COUNT">^2</xliff:g> video ra khỏi thùng rác?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Đang chuyển video ra khỏi thùng rác…}other{Đang chuyển <xliff:g id="COUNT">^1</xliff:g> video ra khỏi thùng rác…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> chuyển ảnh này ra khỏi thùng rác?}other{Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> chuyển <xliff:g id="COUNT">^2</xliff:g> ảnh ra khỏi thùng rác?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Đang chuyển ảnh ra khỏi thùng rác…}other{Đang chuyển <xliff:g id="COUNT">^1</xliff:g> ảnh ra khỏi thùng rác…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> chuyển mục này ra khỏi thùng rác?}other{Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> chuyển <xliff:g id="COUNT">^2</xliff:g> mục ra khỏi thùng rác?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Đang chuyển mục ra khỏi thùng rác…}other{Đang chuyển <xliff:g id="COUNT">^1</xliff:g> mục ra khỏi thùng rác…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> xoá tệp âm thanh này?}other{Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> xoá <xliff:g id="COUNT">^2</xliff:g> tệp âm thanh?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Đang xoá tệp âm thanh…}other{Đang xoá <xliff:g id="COUNT">^1</xliff:g> tệp âm thanh…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> xoá video này?}other{Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> xoá <xliff:g id="COUNT">^2</xliff:g> video?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Đang xoá video…}other{Đang xoá <xliff:g id="COUNT">^1</xliff:g> video…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> xoá ảnh này?}other{Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> xoá <xliff:g id="COUNT">^2</xliff:g> ảnh?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Đang xoá ảnh…}other{Đang xoá <xliff:g id="COUNT">^1</xliff:g> ảnh…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Cho phép <xliff:g id="APP_NAME_0">^1</xliff:g> xoá mục này?}other{Cho phép <xliff:g id="APP_NAME_1">^1</xliff:g> xoá <xliff:g id="COUNT">^2</xliff:g> mục?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Đang xoá mục…}other{Đang xoá <xliff:g id="COUNT">^1</xliff:g> mục…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"<xliff:g id="APP_NAME">%s</xliff:g> không thể xử lý các tệp nội dung nghe nhìn"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Đã hủy quá trình xử lý nội dung nghe nhìn"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Lỗi khi xử lý nội dung nghe nhìn"</string>
diff --git a/res/values-watch/dimens.xml b/res/values-watch/dimens.xml
new file mode 100644
index 0000000..ed5fa00
--- /dev/null
+++ b/res/values-watch/dimens.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<resources>
+ <dimen name="permission_dialog_width">200dp</dimen>
+</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 0950c2d..8fbfa4a 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"媒体"</string>
<string name="storage_description" msgid="4081716890357580107">"本地存储空间"</string>
<string name="app_label" msgid="9035307001052716210">"媒体存储设备"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"照片选择器"</string>
<string name="artist_label" msgid="8105600993099120273">"音乐人"</string>
<string name="unknown" msgid="2059049215682829375">"未知"</string>
<string name="root_images" msgid="5861633549189045666">"图片"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"继续"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"允许"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"拒绝"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">另外 <xliff:g id="COUNT_1">^1</xliff:g> 项</item>
- <item quantity="one">另外 <xliff:g id="COUNT_0">^1</xliff:g> 项</item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">还有 <xliff:g id="COUNT_1">^1</xliff:g> 项</item>
- <item quantity="one">还有 <xliff:g id="COUNT_0">^1</xliff:g> 项</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{另外 <xliff:g id="COUNT_0">^1</xliff:g> 项}other{另外 <xliff:g id="COUNT_1">^1</xliff:g> 项}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{还有 <xliff:g id="COUNT_0">^1</xliff:g> 项}other{还有 <xliff:g id="COUNT_1">^1</xliff:g> 项}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"清除临时应用文件"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>请求清除一些临时文件。这可能消耗更多电量或数据流量。"</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"正在清除临时应用文件…"</string>
<string name="clear" msgid="5524638938415865915">"清除"</string>
<string name="allow" msgid="8885707816848569619">"允许"</string>
<string name="deny" msgid="6040983710442068936">"拒绝"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">要允许<xliff:g id="APP_NAME_1">^1</xliff:g>修改这 <xliff:g id="COUNT">^2</xliff:g> 个音频文件吗?</item>
- <item quantity="one">要允许<xliff:g id="APP_NAME_0">^1</xliff:g>修改这个音频文件吗?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">正在修改 <xliff:g id="COUNT">^1</xliff:g> 个音频文件…</item>
- <item quantity="one">正在修改音频文件…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">要允许<xliff:g id="APP_NAME_1">^1</xliff:g>修改这 <xliff:g id="COUNT">^2</xliff:g> 个视频吗?</item>
- <item quantity="one">要允许<xliff:g id="APP_NAME_0">^1</xliff:g>修改这个视频吗?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">正在修改 <xliff:g id="COUNT">^1</xliff:g> 个视频…</item>
- <item quantity="one">正在修改视频…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">要允许<xliff:g id="APP_NAME_1">^1</xliff:g>修改这 <xliff:g id="COUNT">^2</xliff:g> 张照片吗?</item>
- <item quantity="one">要允许<xliff:g id="APP_NAME_0">^1</xliff:g>修改这张照片吗?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">正在修改 <xliff:g id="COUNT">^1</xliff:g> 张照片…</item>
- <item quantity="one">正在修改照片…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">要允许<xliff:g id="APP_NAME_1">^1</xliff:g>修改这 <xliff:g id="COUNT">^2</xliff:g> 项内容吗?</item>
- <item quantity="one">要允许<xliff:g id="APP_NAME_0">^1</xliff:g>修改这项内容吗?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">正在修改 <xliff:g id="COUNT">^1</xliff:g> 项内容…</item>
- <item quantity="one">正在修改内容…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">要允许<xliff:g id="APP_NAME_1">^1</xliff:g>将这 <xliff:g id="COUNT">^2</xliff:g> 个音频文件移入回收站吗?</item>
- <item quantity="one">要允许<xliff:g id="APP_NAME_0">^1</xliff:g>将这个音频文件移入回收站吗?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">正在将 <xliff:g id="COUNT">^1</xliff:g> 个音频文件移入回收站…</item>
- <item quantity="one">正在将音频文件移入回收站…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">要允许<xliff:g id="APP_NAME_1">^1</xliff:g>将这 <xliff:g id="COUNT">^2</xliff:g> 个视频移入回收站吗?</item>
- <item quantity="one">要允许<xliff:g id="APP_NAME_0">^1</xliff:g>将这个视频移入回收站吗?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">正在将 <xliff:g id="COUNT">^1</xliff:g> 个视频移入回收站…</item>
- <item quantity="one">正在将视频移入回收站…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">要允许<xliff:g id="APP_NAME_1">^1</xliff:g>将这 <xliff:g id="COUNT">^2</xliff:g> 张照片移入回收站吗?</item>
- <item quantity="one">要允许<xliff:g id="APP_NAME_0">^1</xliff:g>将这张照片移入回收站吗?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">正在将 <xliff:g id="COUNT">^1</xliff:g> 张照片移入回收站…</item>
- <item quantity="one">正在将照片移入回收站…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">要允许<xliff:g id="APP_NAME_1">^1</xliff:g>将这 <xliff:g id="COUNT">^2</xliff:g> 项内容移入回收站吗?</item>
- <item quantity="one">要允许<xliff:g id="APP_NAME_0">^1</xliff:g>将这项内容移入回收站吗?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">正在将 <xliff:g id="COUNT">^1</xliff:g> 项内容移入回收站…</item>
- <item quantity="one">正在将内容移入回收站…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">要允许<xliff:g id="APP_NAME_1">^1</xliff:g>将这 <xliff:g id="COUNT">^2</xliff:g> 个音频文件从回收站中移出吗?</item>
- <item quantity="one">要允许<xliff:g id="APP_NAME_0">^1</xliff:g>将这个音频文件从回收站中移出吗?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">正在将 <xliff:g id="COUNT">^1</xliff:g> 个音频文件从回收站中移出…</item>
- <item quantity="one">正在将音频文件从回收站中移出…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">要允许<xliff:g id="APP_NAME_1">^1</xliff:g>将这 <xliff:g id="COUNT">^2</xliff:g> 个视频从回收站中移出吗?</item>
- <item quantity="one">要允许<xliff:g id="APP_NAME_0">^1</xliff:g>将这个视频从回收站中移出吗?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">正在将 <xliff:g id="COUNT">^1</xliff:g> 个视频从回收站中移出…</item>
- <item quantity="one">正在将视频从回收站中移出…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">要允许<xliff:g id="APP_NAME_1">^1</xliff:g>将这 <xliff:g id="COUNT">^2</xliff:g> 张照片从回收站中移出吗?</item>
- <item quantity="one">要允许<xliff:g id="APP_NAME_0">^1</xliff:g>将这张照片从回收站中移出吗?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">正在将 <xliff:g id="COUNT">^1</xliff:g> 张照片从回收站中移出…</item>
- <item quantity="one">正在将照片从回收站中移出…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">要允许<xliff:g id="APP_NAME_1">^1</xliff:g>将这 <xliff:g id="COUNT">^2</xliff:g> 项内容从回收站中移出吗?</item>
- <item quantity="one">要允许<xliff:g id="APP_NAME_0">^1</xliff:g>将这项内容从回收站中移出吗?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">正在将 <xliff:g id="COUNT">^1</xliff:g> 项内容从回收站中移出…</item>
- <item quantity="one">正在将内容从回收站中移出…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">要允许<xliff:g id="APP_NAME_1">^1</xliff:g>删除这 <xliff:g id="COUNT">^2</xliff:g> 个音频文件吗?</item>
- <item quantity="one">要允许<xliff:g id="APP_NAME_0">^1</xliff:g>删除这个音频文件吗?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">正在删除 <xliff:g id="COUNT">^1</xliff:g> 个音频文件…</item>
- <item quantity="one">正在删除音频文件…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">要允许<xliff:g id="APP_NAME_1">^1</xliff:g>删除这 <xliff:g id="COUNT">^2</xliff:g> 个视频吗?</item>
- <item quantity="one">要允许<xliff:g id="APP_NAME_0">^1</xliff:g>删除这个视频吗?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">正在删除 <xliff:g id="COUNT">^1</xliff:g> 个视频…</item>
- <item quantity="one">正在删除视频…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">要允许<xliff:g id="APP_NAME_1">^1</xliff:g>删除这 <xliff:g id="COUNT">^2</xliff:g> 张照片吗?</item>
- <item quantity="one">要允许<xliff:g id="APP_NAME_0">^1</xliff:g>删除这张照片吗?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">正在删除 <xliff:g id="COUNT">^1</xliff:g> 张照片…</item>
- <item quantity="one">正在删除照片…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">要允许<xliff:g id="APP_NAME_1">^1</xliff:g>删除这 <xliff:g id="COUNT">^2</xliff:g> 项内容吗?</item>
- <item quantity="one">要允许<xliff:g id="APP_NAME_0">^1</xliff:g>删除这项内容吗?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">正在删除 <xliff:g id="COUNT">^1</xliff:g> 项内容…</item>
- <item quantity="one">正在删除内容…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"添加"</string>
+ <string name="deselect" msgid="4297825044827769490">"取消选择"</string>
+ <string name="deselected" msgid="8488133193326208475">"已取消选中"</string>
+ <string name="select" msgid="2704765470563027689">"选择"</string>
+ <string name="selected" msgid="9151797369975828124">"已选中"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{最多可选择 <xliff:g id="COUNT_0">^1</xliff:g> 项}other{最多可选择 <xliff:g id="COUNT_1">^1</xliff:g> 项}}"</string>
+ <string name="recent" msgid="6694613584743207874">"最近"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"无照片或视频"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"无影集"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"查看所选内容"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"照片"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"影集"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"预览"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"切换到工作资料"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"切换到个人资料"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"已被您的管理员禁止"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"不允许通过个人应用访问工作数据"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"不允许通过工作应用访问个人数据"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"工作应用已暂停"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"如需打开工作照片,请打开您的工作应用,然后重试"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"此应用只能访问您选择的照片"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> 个}other{<xliff:g id="COUNT_1">^1</xliff:g> 个}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"添加(<xliff:g id="COUNT">^1</xliff:g> 项)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"相机"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"下载内容"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"收藏"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"屏幕截图"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"动态照片"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"拍摄于 <xliff:g id="TIME">%2$s</xliff:g>的<xliff:g id="ITEM_NAME">%1$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"视频拍摄于 <xliff:g id="TIME">%1$s</xliff:g>,时长 <xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"照片"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"动态照片"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"将视频静音"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"将视频取消静音"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"播放视频"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"暂停视频"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"现在可以从“<xliff:g id="PKG_NAME">%1$s</xliff:g>”获取云端媒体"</string>
+ <string name="not_selected" msgid="2244008151669896758">"未选择"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{要允许<xliff:g id="APP_NAME_0">^1</xliff:g>修改这个音频文件吗?}other{要允许<xliff:g id="APP_NAME_1">^1</xliff:g>修改这 <xliff:g id="COUNT">^2</xliff:g> 个音频文件吗?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{正在修改音频文件…}other{正在修改 <xliff:g id="COUNT">^1</xliff:g> 个音频文件…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{要允许<xliff:g id="APP_NAME_0">^1</xliff:g>修改这个视频吗?}other{要允许<xliff:g id="APP_NAME_1">^1</xliff:g>修改这 <xliff:g id="COUNT">^2</xliff:g> 个视频吗?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{正在修改视频…}other{正在修改 <xliff:g id="COUNT">^1</xliff:g> 个视频…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{要允许<xliff:g id="APP_NAME_0">^1</xliff:g>修改这张照片吗?}other{要允许<xliff:g id="APP_NAME_1">^1</xliff:g>修改这 <xliff:g id="COUNT">^2</xliff:g> 张照片吗?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{正在修改照片…}other{正在修改 <xliff:g id="COUNT">^1</xliff:g> 张照片…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{要允许<xliff:g id="APP_NAME_0">^1</xliff:g>修改这项内容吗?}other{要允许<xliff:g id="APP_NAME_1">^1</xliff:g>修改这 <xliff:g id="COUNT">^2</xliff:g> 项内容吗?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{正在修改内容…}other{正在修改 <xliff:g id="COUNT">^1</xliff:g> 项内容…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{要允许<xliff:g id="APP_NAME_0">^1</xliff:g>将这个音频文件移入回收站吗?}other{要允许<xliff:g id="APP_NAME_1">^1</xliff:g>将这 <xliff:g id="COUNT">^2</xliff:g> 个音频文件移入回收站吗?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{正在将音频文件移入回收站…}other{正在将 <xliff:g id="COUNT">^1</xliff:g> 个音频文件移入回收站…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{要允许<xliff:g id="APP_NAME_0">^1</xliff:g>将这个视频移入回收站吗?}other{要允许<xliff:g id="APP_NAME_1">^1</xliff:g>将这 <xliff:g id="COUNT">^2</xliff:g> 个视频移入回收站吗?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{正在将视频移入回收站…}other{正在将 <xliff:g id="COUNT">^1</xliff:g> 个视频移入回收站…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{要允许<xliff:g id="APP_NAME_0">^1</xliff:g>将这张照片移入回收站吗?}other{要允许<xliff:g id="APP_NAME_1">^1</xliff:g>将这 <xliff:g id="COUNT">^2</xliff:g> 张照片移入回收站吗?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{正在将照片移入回收站…}other{正在将 <xliff:g id="COUNT">^1</xliff:g> 张照片移入回收站…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{要允许<xliff:g id="APP_NAME_0">^1</xliff:g>将这项内容移入回收站吗?}other{要允许<xliff:g id="APP_NAME_1">^1</xliff:g>将这 <xliff:g id="COUNT">^2</xliff:g> 项内容移入回收站吗?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{正在将内容移入回收站…}other{正在将 <xliff:g id="COUNT">^1</xliff:g> 项内容移入回收站…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{要允许<xliff:g id="APP_NAME_0">^1</xliff:g>将这个音频文件从回收站中移出吗?}other{要允许<xliff:g id="APP_NAME_1">^1</xliff:g>将这 <xliff:g id="COUNT">^2</xliff:g> 个音频文件从回收站中移出吗?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{正在将音频文件从回收站中移出…}other{正在将 <xliff:g id="COUNT">^1</xliff:g> 个音频文件从回收站中移出…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{要允许<xliff:g id="APP_NAME_0">^1</xliff:g>将这个视频从回收站中移出吗?}other{要允许<xliff:g id="APP_NAME_1">^1</xliff:g>将这 <xliff:g id="COUNT">^2</xliff:g> 个视频从回收站中移出吗?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{正在将视频从回收站中移出…}other{正在将 <xliff:g id="COUNT">^1</xliff:g> 个视频从回收站中移出…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{要允许<xliff:g id="APP_NAME_0">^1</xliff:g>将这张照片从回收站中移出吗?}other{要允许<xliff:g id="APP_NAME_1">^1</xliff:g>将这 <xliff:g id="COUNT">^2</xliff:g> 张照片从回收站中移出吗?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{正在将照片从回收站中移出…}other{正在将 <xliff:g id="COUNT">^1</xliff:g> 张照片从回收站中移出…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{要允许<xliff:g id="APP_NAME_0">^1</xliff:g>将这项内容从回收站中移出吗?}other{要允许<xliff:g id="APP_NAME_1">^1</xliff:g>将这 <xliff:g id="COUNT">^2</xliff:g> 项内容从回收站中移出吗?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{正在将内容从回收站中移出…}other{正在将 <xliff:g id="COUNT">^1</xliff:g> 项内容从回收站中移出…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{要允许<xliff:g id="APP_NAME_0">^1</xliff:g>删除这个音频文件吗?}other{要允许<xliff:g id="APP_NAME_1">^1</xliff:g>删除这 <xliff:g id="COUNT">^2</xliff:g> 个音频文件吗?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{正在删除音频文件…}other{正在删除 <xliff:g id="COUNT">^1</xliff:g> 个音频文件…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{要允许<xliff:g id="APP_NAME_0">^1</xliff:g>删除这个视频吗?}other{要允许<xliff:g id="APP_NAME_1">^1</xliff:g>删除这 <xliff:g id="COUNT">^2</xliff:g> 个视频吗?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{正在删除视频…}other{正在删除 <xliff:g id="COUNT">^1</xliff:g> 个视频…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{要允许<xliff:g id="APP_NAME_0">^1</xliff:g>删除这张照片吗?}other{要允许<xliff:g id="APP_NAME_1">^1</xliff:g>删除这 <xliff:g id="COUNT">^2</xliff:g> 张照片吗?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{正在删除照片…}other{正在删除 <xliff:g id="COUNT">^1</xliff:g> 张照片…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{要允许<xliff:g id="APP_NAME_0">^1</xliff:g>删除这项内容吗?}other{要允许<xliff:g id="APP_NAME_1">^1</xliff:g>删除这 <xliff:g id="COUNT">^2</xliff:g> 项内容吗?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{正在删除内容…}other{正在删除 <xliff:g id="COUNT">^1</xliff:g> 项内容…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"“<xliff:g id="APP_NAME">%s</xliff:g>”无法处理媒体文件"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"已取消处理媒体内容"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"处理媒体内容时出错"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 51d8c9e..5c3fdd4 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"媒體"</string>
<string name="storage_description" msgid="4081716890357580107">"本機儲存空間"</string>
<string name="app_label" msgid="9035307001052716210">"媒體儲存空間"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"相片點選器"</string>
<string name="artist_label" msgid="8105600993099120273">"歌手"</string>
<string name="unknown" msgid="2059049215682829375">"不明"</string>
<string name="root_images" msgid="5861633549189045666">"相片"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"繼續"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"允許"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"拒絕"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">還有 <xliff:g id="COUNT_1">^1</xliff:g> 個項目</item>
- <item quantity="one">還有 <xliff:g id="COUNT_0">^1</xliff:g> 個項目</item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">以及另外 <xliff:g id="COUNT_1">^1</xliff:g> 個項目</item>
- <item quantity="one">以及另外 <xliff:g id="COUNT_0">^1</xliff:g> 個項目</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{還有 <xliff:g id="COUNT_0">^1</xliff:g> 個項目}other{還有 <xliff:g id="COUNT_1">^1</xliff:g> 個項目}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{還有另外 <xliff:g id="COUNT_0">^1</xliff:g> 個項目}other{還有另外 <xliff:g id="COUNT_1">^1</xliff:g> 個項目}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"清除應用程式暫存檔案"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」要求清除部分臨時檔案。這可能會導致耗電量或流動數據用量增加。"</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"正在清除應用程式暫存檔案…"</string>
<string name="clear" msgid="5524638938415865915">"清除"</string>
<string name="allow" msgid="8885707816848569619">"允許"</string>
<string name="deny" msgid="6040983710442068936">"拒絕"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 修改 <xliff:g id="COUNT">^2</xliff:g> 部影片嗎?</item>
- <item quantity="one">允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 修改此影片嗎?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">正在修改 <xliff:g id="COUNT">^1</xliff:g> 個音訊檔案…</item>
- <item quantity="one">正在修改音訊檔案…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 修改 <xliff:g id="COUNT">^2</xliff:g> 部影片嗎?</item>
- <item quantity="one">允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 修改此影片嗎?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">正在修改 <xliff:g id="COUNT">^1</xliff:g> 部影片…</item>
- <item quantity="one">正在修改影片…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 修改 <xliff:g id="COUNT">^2</xliff:g> 張相片嗎?</item>
- <item quantity="one">允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 修改此相片嗎?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">正在修改 <xliff:g id="COUNT">^1</xliff:g> 張相片…</item>
- <item quantity="one">正在修改相片…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 修改 <xliff:g id="COUNT">^2</xliff:g> 個項目嗎?</item>
- <item quantity="one">允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 修改此項目嗎?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">正在修改 <xliff:g id="COUNT">^1</xliff:g> 個項目…</item>
- <item quantity="one">正在修改項目…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 將 <xliff:g id="COUNT">^2</xliff:g> 個音訊檔案移至垃圾桶嗎?</item>
- <item quantity="one">允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 將此音訊檔案移至垃圾桶嗎?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">正在將 <xliff:g id="COUNT">^1</xliff:g> 個音訊檔案移至垃圾桶…</item>
- <item quantity="one">正在將音訊檔案移至垃圾桶…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 將 <xliff:g id="COUNT">^2</xliff:g> 部影片移至垃圾桶嗎?</item>
- <item quantity="one">允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 將此影片移至垃圾桶嗎?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">正在將 <xliff:g id="COUNT">^1</xliff:g> 部影片移至垃圾桶…</item>
- <item quantity="one">正在將影片移至垃圾桶…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 將 <xliff:g id="COUNT">^2</xliff:g> 張相片移至垃圾桶嗎?</item>
- <item quantity="one">允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 將此相片移至垃圾桶嗎?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">正在將 <xliff:g id="COUNT">^1</xliff:g> 張相片移至垃圾桶…</item>
- <item quantity="one">正在將相片移至垃圾桶…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 將 <xliff:g id="COUNT">^2</xliff:g> 個項目移至垃圾桶嗎?</item>
- <item quantity="one">允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 將此項目移至垃圾桶嗎?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">正在將 <xliff:g id="COUNT">^1</xliff:g> 個項目移至垃圾桶…</item>
- <item quantity="one">正在將項目移至垃圾桶…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 將 <xliff:g id="COUNT">^2</xliff:g> 個音訊檔案移出垃圾桶嗎?</item>
- <item quantity="one">允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 將此音訊檔案移出垃圾桶嗎?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">正在將 <xliff:g id="COUNT">^1</xliff:g> 個音訊檔案移出垃圾桶…</item>
- <item quantity="one">正在將音訊檔案移出垃圾桶…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 將 <xliff:g id="COUNT">^2</xliff:g> 部影片移出垃圾桶嗎?</item>
- <item quantity="one">允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 將此影片移出垃圾桶嗎?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">正在將 <xliff:g id="COUNT">^1</xliff:g> 部影片移出垃圾桶…</item>
- <item quantity="one">正在將影片移出垃圾桶…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 將 <xliff:g id="COUNT">^2</xliff:g> 張相片移出垃圾桶嗎?</item>
- <item quantity="one">允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 將此相片移出垃圾桶嗎?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">正在將 <xliff:g id="COUNT">^1</xliff:g> 張相片移出垃圾桶…</item>
- <item quantity="one">正在將相片移出垃圾桶…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 將 <xliff:g id="COUNT">^2</xliff:g> 個項目移出垃圾桶嗎?</item>
- <item quantity="one">允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 將此項目移出垃圾桶嗎?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">正在將 <xliff:g id="COUNT">^1</xliff:g> 個項目移出垃圾桶…</item>
- <item quantity="one">正在將項目移出垃圾桶…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 刪除 <xliff:g id="COUNT">^2</xliff:g> 個音訊檔案嗎?</item>
- <item quantity="one">允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 刪除此音訊檔案嗎?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">正在刪除 <xliff:g id="COUNT">^1</xliff:g> 個音訊檔案…</item>
- <item quantity="one">正在刪除音訊檔案…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 刪除 <xliff:g id="COUNT">^2</xliff:g> 部影片嗎?</item>
- <item quantity="one">允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 刪除此影片嗎?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">正在刪除 <xliff:g id="COUNT">^1</xliff:g> 部影片…</item>
- <item quantity="one">正在刪除影片…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 刪除 <xliff:g id="COUNT">^2</xliff:g> 張相片嗎?</item>
- <item quantity="one">允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 刪除此相片嗎?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">正在刪除 <xliff:g id="COUNT">^1</xliff:g> 張相片…</item>
- <item quantity="one">正在刪除相片…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 刪除 <xliff:g id="COUNT">^2</xliff:g> 個項目嗎?</item>
- <item quantity="one">允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 刪除此項目嗎?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">正在刪除 <xliff:g id="COUNT">^1</xliff:g> 個項目…</item>
- <item quantity="one">正在刪除項目…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"新增"</string>
+ <string name="deselect" msgid="4297825044827769490">"取消選取"</string>
+ <string name="deselected" msgid="8488133193326208475">"已取消選取"</string>
+ <string name="select" msgid="2704765470563027689">"選取"</string>
+ <string name="selected" msgid="9151797369975828124">"已選取"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{選取最多 <xliff:g id="COUNT_0">^1</xliff:g> 個項目}other{選取最多 <xliff:g id="COUNT_1">^1</xliff:g> 個項目}}"</string>
+ <string name="recent" msgid="6694613584743207874">"最近"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"沒有相片或影片"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"沒有相簿"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"查看所選項目"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"相片"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"相簿"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"預覽"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"切換至工作設定檔"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"切換至個人設定檔"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"管理員已禁止此操作"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"個人應用程式不得存取工作資料"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"工作應用程式不得存取個人資料"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"已暫停工作應用程式"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"如要開啟工作相片,請開啟工作應用程式,然後再試一次"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"此應用程式只能存取您選取的相片"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> 個項目}other{<xliff:g id="COUNT_1">^1</xliff:g> 個項目}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"新增 (<xliff:g id="COUNT">^1</xliff:g> 個)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"相機"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"下載"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"我的最愛"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"螢幕截圖"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"動態相片"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g>拍攝時間:<xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"影片拍攝日期:<xliff:g id="TIME">%1$s</xliff:g>,片長:<xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"相片"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"動態相片"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"將影片靜音"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"將影片取消靜音"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"播放影片"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"暫停影片"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"現可透過「<xliff:g id="PKG_NAME">%1$s</xliff:g>」使用雲端媒體"</string>
+ <string name="not_selected" msgid="2244008151669896758">"未揀"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 修改此影片嗎?}other{允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 修改 <xliff:g id="COUNT">^2</xliff:g> 部影片嗎?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{正在修改音訊檔案…}other{正在修改 <xliff:g id="COUNT">^1</xliff:g> 個音訊檔案…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 修改此影片嗎?}other{允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 修改 <xliff:g id="COUNT">^2</xliff:g> 部影片嗎?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{正在修改影片…}other{正在修改 <xliff:g id="COUNT">^1</xliff:g> 部影片…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 修改此相片嗎?}other{允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 修改 <xliff:g id="COUNT">^2</xliff:g> 張相片嗎?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{正在修改相片…}other{正在修改 <xliff:g id="COUNT">^1</xliff:g> 張相片…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 修改此項目嗎?}other{允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 修改 <xliff:g id="COUNT">^2</xliff:g> 個項目嗎?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{正在修改項目…}other{正在修改 <xliff:g id="COUNT">^1</xliff:g> 個項目…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 將此音訊檔案移至垃圾桶嗎?}other{允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 將 <xliff:g id="COUNT">^2</xliff:g> 個音訊檔案移至垃圾桶嗎?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{正在將音訊檔案移至垃圾桶…}other{正在將 <xliff:g id="COUNT">^1</xliff:g> 個音訊檔案移至垃圾桶…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 將此影片移至垃圾桶嗎?}other{允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 將 <xliff:g id="COUNT">^2</xliff:g> 部影片移至垃圾桶嗎?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{正在將影片移至垃圾桶…}other{正在將 <xliff:g id="COUNT">^1</xliff:g> 部影片移至垃圾桶…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 將此相片移至垃圾桶嗎?}other{允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 將 <xliff:g id="COUNT">^2</xliff:g> 張相片移至垃圾桶嗎?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{正在將相片移至垃圾桶…}other{正在將 <xliff:g id="COUNT">^1</xliff:g> 張相片移至垃圾桶…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 將此項目移至垃圾桶嗎?}other{允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 將 <xliff:g id="COUNT">^2</xliff:g> 個項目移至垃圾桶嗎?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{正在將項目移至垃圾桶…}other{正在將 <xliff:g id="COUNT">^1</xliff:g> 個項目移至垃圾桶…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 將此音訊檔案移出垃圾桶嗎?}other{允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 將 <xliff:g id="COUNT">^2</xliff:g> 個音訊檔案移出垃圾桶嗎?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{正在將音訊檔案移出垃圾桶…}other{正在將 <xliff:g id="COUNT">^1</xliff:g> 個音訊檔案移出垃圾桶…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 將此影片移出垃圾桶嗎?}other{允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 將 <xliff:g id="COUNT">^2</xliff:g> 部影片移出垃圾桶嗎?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{正在將影片移出垃圾桶…}other{正在將 <xliff:g id="COUNT">^1</xliff:g> 部影片移出垃圾桶…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 將此相片移出垃圾桶嗎?}other{允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 將 <xliff:g id="COUNT">^2</xliff:g> 張相片移出垃圾桶嗎?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{正在將相片移出垃圾桶…}other{正在將 <xliff:g id="COUNT">^1</xliff:g> 張相片移出垃圾桶…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 將此項目移出垃圾桶嗎?}other{允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 將 <xliff:g id="COUNT">^2</xliff:g> 個項目移出垃圾桶嗎?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{正在將項目移出垃圾桶…}other{正在將 <xliff:g id="COUNT">^1</xliff:g> 個項目移出垃圾桶…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 刪除此音訊檔案嗎?}other{允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 刪除 <xliff:g id="COUNT">^2</xliff:g> 個音訊檔案嗎?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{正在刪除音訊檔案…}other{正在刪除 <xliff:g id="COUNT">^1</xliff:g> 個音訊檔案…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 刪除此影片嗎?}other{允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 刪除 <xliff:g id="COUNT">^2</xliff:g> 部影片嗎?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{正在刪除影片…}other{正在刪除 <xliff:g id="COUNT">^1</xliff:g> 部影片…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 刪除此相片嗎?}other{允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 刪除 <xliff:g id="COUNT">^2</xliff:g> 張相片嗎?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{正在刪除相片…}other{正在刪除 <xliff:g id="COUNT">^1</xliff:g> 張相片…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{允許 <xliff:g id="APP_NAME_0">^1</xliff:g> 刪除此項目嗎?}other{允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 刪除 <xliff:g id="COUNT">^2</xliff:g> 個項目嗎?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{正在刪除項目…}other{正在刪除 <xliff:g id="COUNT">^1</xliff:g> 個項目…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"「<xliff:g id="APP_NAME">%s</xliff:g>」無法處理媒體檔案"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"已取消處理媒體"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"處理媒體時發生錯誤"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index a67a29e..1dd87a6 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -19,6 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"媒體"</string>
<string name="storage_description" msgid="4081716890357580107">"本機儲存空間"</string>
<string name="app_label" msgid="9035307001052716210">"媒體儲存空間"</string>
+ <string name="picker_app_label" msgid="5487796494405738578">"相片挑選器"</string>
<string name="artist_label" msgid="8105600993099120273">"演出者"</string>
<string name="unknown" msgid="2059049215682829375">"不明"</string>
<string name="root_images" msgid="5861633549189045666">"圖片"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"繼續"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"允許"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"拒絕"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">還有 <xliff:g id="COUNT_1">^1</xliff:g> 個項目</item>
- <item quantity="one">還有 <xliff:g id="COUNT_0">^1</xliff:g> 個項目</item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="other">還有另外 <xliff:g id="COUNT_1">^1</xliff:g> 個項目</item>
- <item quantity="one">還有另外 <xliff:g id="COUNT_0">^1</xliff:g> 個項目</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{還有 <xliff:g id="COUNT_0">^1</xliff:g> 個項目}other{還有 <xliff:g id="COUNT_1">^1</xliff:g> 個項目}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{還有另外 <xliff:g id="COUNT_0">^1</xliff:g> 個項目}other{還有另外 <xliff:g id="COUNT_1">^1</xliff:g> 個項目}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"清除應用程式暫存檔案"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」想要清除某些暫存檔案,這可能會導致耗電量或行動數據用量增加。"</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"正在清除應用程式暫存檔案…"</string>
<string name="clear" msgid="5524638938415865915">"清除"</string>
<string name="allow" msgid="8885707816848569619">"允許"</string>
<string name="deny" msgid="6040983710442068936">"拒絕"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">要允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」修改這 <xliff:g id="COUNT">^2</xliff:g> 個音訊檔案嗎?</item>
- <item quantity="one">要允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」修改這個音訊檔案嗎?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="other">正在修改 <xliff:g id="COUNT">^1</xliff:g> 個音訊檔案…</item>
- <item quantity="one">正在修改音訊檔案…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="other">要允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」修改這 <xliff:g id="COUNT">^2</xliff:g> 部影片嗎?</item>
- <item quantity="one">要允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」修改這部影片嗎?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="other">正在修改 <xliff:g id="COUNT">^1</xliff:g> 部影片…</item>
- <item quantity="one">正在修改影片…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="other">要允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」修改這 <xliff:g id="COUNT">^2</xliff:g> 張相片嗎?</item>
- <item quantity="one">要允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」修改這張相片嗎?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="other">正在修改 <xliff:g id="COUNT">^1</xliff:g> 張相片…</item>
- <item quantity="one">正在修改相片…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="other">要允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」修改這 <xliff:g id="COUNT">^2</xliff:g> 個項目嗎?</item>
- <item quantity="one">要允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」修改這個項目嗎?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="other">正在修改 <xliff:g id="COUNT">^1</xliff:g> 個項目…</item>
- <item quantity="one">正在修改項目…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="other">要允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」將這 <xliff:g id="COUNT">^2</xliff:g> 個音訊檔案移至垃圾桶嗎?</item>
- <item quantity="one">要允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」將這個音訊檔案移至垃圾桶嗎?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="other">正在將 <xliff:g id="COUNT">^1</xliff:g> 個音訊檔案移入垃圾桶…</item>
- <item quantity="one">正在將音訊檔案移入垃圾桶…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="other">要允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」將這 <xliff:g id="COUNT">^2</xliff:g> 部影片移至垃圾桶嗎?</item>
- <item quantity="one">要允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」將這部影片移至垃圾桶嗎?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="other">正在將 <xliff:g id="COUNT">^1</xliff:g> 部影片移入垃圾桶…</item>
- <item quantity="one">正在將影片移入垃圾桶…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="other">要允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」將這 <xliff:g id="COUNT">^2</xliff:g> 張相片移至垃圾桶嗎?</item>
- <item quantity="one">要允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」將這張相片移至垃圾桶嗎?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="other">正在將 <xliff:g id="COUNT">^1</xliff:g> 張相片移入垃圾桶…</item>
- <item quantity="one">正在將相片移入垃圾桶…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="other">要允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」將這 <xliff:g id="COUNT">^2</xliff:g> 個項目移至垃圾桶嗎?</item>
- <item quantity="one">要允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」將這個項目移至垃圾桶嗎?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="other">正在將 <xliff:g id="COUNT">^1</xliff:g> 個項目移入垃圾桶…</item>
- <item quantity="one">正在將項目移入垃圾桶…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="other">要允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」將這 <xliff:g id="COUNT">^2</xliff:g> 個音訊檔案移出垃圾桶嗎?</item>
- <item quantity="one">要允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」將這個音訊檔案移出垃圾桶嗎?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="other">正在將 <xliff:g id="COUNT">^1</xliff:g> 個音訊檔案移出垃圾桶…</item>
- <item quantity="one">正在將音訊檔案移出垃圾桶…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="other">要允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」將這 <xliff:g id="COUNT">^2</xliff:g> 部影片移出垃圾桶嗎?</item>
- <item quantity="one">要允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」將這部影片移出垃圾桶嗎?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="other">正在將 <xliff:g id="COUNT">^1</xliff:g> 部影片移出垃圾桶…</item>
- <item quantity="one">正在將影片移出垃圾桶…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="other">要允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」將這 <xliff:g id="COUNT">^2</xliff:g> 張相片移出垃圾桶嗎?</item>
- <item quantity="one">要允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」將這張相片移出垃圾桶嗎?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="other">正在將 <xliff:g id="COUNT">^1</xliff:g> 張相片移出垃圾桶…</item>
- <item quantity="one">正在將相片移出垃圾桶…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="other">要允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」將這 <xliff:g id="COUNT">^2</xliff:g> 個項目移出垃圾桶嗎?</item>
- <item quantity="one">要允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」將這個項目移出垃圾桶嗎?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="other">正在將 <xliff:g id="COUNT">^1</xliff:g> 個項目移出垃圾桶…</item>
- <item quantity="one">正在將項目移出垃圾桶…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="other">要允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」刪除這 <xliff:g id="COUNT">^2</xliff:g> 個音訊檔案嗎?</item>
- <item quantity="one">要允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」刪除這個音訊檔案嗎?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="other">正在刪除 <xliff:g id="COUNT">^1</xliff:g> 個音訊檔案…</item>
- <item quantity="one">正在刪除音訊檔案…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="other">要允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」刪除這 <xliff:g id="COUNT">^2</xliff:g> 部影片嗎?</item>
- <item quantity="one">要允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」刪除這部影片嗎?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="other">正在刪除 <xliff:g id="COUNT">^1</xliff:g> 部影片…</item>
- <item quantity="one">正在刪除影片…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="other">要允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」刪除這 <xliff:g id="COUNT">^2</xliff:g> 張相片嗎?</item>
- <item quantity="one">要允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」刪除這張相片嗎?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="other">刪除 <xliff:g id="COUNT">^1</xliff:g> 張相片…</item>
- <item quantity="one">正在刪除相片…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="other">要允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」刪除這 <xliff:g id="COUNT">^2</xliff:g> 個項目嗎?</item>
- <item quantity="one">要允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」刪除這個項目嗎?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="other">正在刪除 <xliff:g id="COUNT">^1</xliff:g> 個項目…</item>
- <item quantity="one">正在刪除項目…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"新增"</string>
+ <string name="deselect" msgid="4297825044827769490">"取消選取"</string>
+ <string name="deselected" msgid="8488133193326208475">"已取消選取"</string>
+ <string name="select" msgid="2704765470563027689">"選取"</string>
+ <string name="selected" msgid="9151797369975828124">"已選取"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{最多可選取 <xliff:g id="COUNT_0">^1</xliff:g> 個項目}other{最多可選取 <xliff:g id="COUNT_1">^1</xliff:g> 個項目}}"</string>
+ <string name="recent" msgid="6694613584743207874">"最近"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"沒有相片或影片"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"沒有相簿"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"查看所選項目"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"相片"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"相簿"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"預覽"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"切換至工作資料夾"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"切換至個人資料夾"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"管理員已禁止這項操作"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"個人應用程式不得存取工作資料"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"工作應用程式不得存取個人資料"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"工作應用程式目前為暫停狀態"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"如要開啟工作資料夾的相片或影片,請開啟工作應用程式,然後再試一次"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"這個應用程式只能存取你選取的相片"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> 個項目}other{<xliff:g id="COUNT_1">^1</xliff:g> 個項目}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"新增 (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"相機"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"下載的內容"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"收藏的內容"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"螢幕截圖"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"動態相片"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g>拍攝時間:<xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"影片拍攝日期:<xliff:g id="TIME">%1$s</xliff:g>,片長:<xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"相片"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"動態相片"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"將影片靜音"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"將影片取消靜音"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"播放影片"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"暫停播放影片"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"現在可以透過「<xliff:g id="PKG_NAME">%1$s</xliff:g>」存取雲端媒體"</string>
+ <string name="not_selected" msgid="2244008151669896758">"未選取"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」修改這個音訊檔案嗎?}other{允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」修改這 <xliff:g id="COUNT">^2</xliff:g> 個音訊檔案嗎?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{正在修改音訊檔案…}other{正在修改 <xliff:g id="COUNT">^1</xliff:g> 個音訊檔案…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」修改這部影片嗎?}other{允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」修改這 <xliff:g id="COUNT">^2</xliff:g> 部影片嗎?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{正在修改影片…}other{正在修改 <xliff:g id="COUNT">^1</xliff:g> 部影片…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」修改這張相片嗎?}other{允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」修改這 <xliff:g id="COUNT">^2</xliff:g> 張相片嗎?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{正在修改相片…}other{正在修改 <xliff:g id="COUNT">^1</xliff:g> 張相片…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」修改這個項目嗎?}other{允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」修改這 <xliff:g id="COUNT">^2</xliff:g> 個項目嗎?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{正在修改項目…}other{正在修改 <xliff:g id="COUNT">^1</xliff:g> 個項目…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」將這個音訊檔案移入垃圾桶嗎?}other{允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」將這 <xliff:g id="COUNT">^2</xliff:g> 個音訊檔案移入垃圾桶嗎?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{正在將音訊檔案移入垃圾桶…}other{正在將 <xliff:g id="COUNT">^1</xliff:g> 個音訊檔案移入垃圾桶…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」將這部影片移至垃圾桶嗎?}other{允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」將這 <xliff:g id="COUNT">^2</xliff:g> 部影片移至垃圾桶嗎?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{正在將影片移入垃圾桶…}other{正在將 <xliff:g id="COUNT">^1</xliff:g> 部影片移入垃圾桶…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」將這張相片移入垃圾桶嗎?}other{允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」將這 <xliff:g id="COUNT">^2</xliff:g> 張相片移入垃圾桶嗎?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{正在將相片移入垃圾桶…}other{正在將 <xliff:g id="COUNT">^1</xliff:g> 張相片移入垃圾桶…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」將這個項目移入垃圾桶嗎?}other{允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」將這 <xliff:g id="COUNT">^2</xliff:g> 個項目移入垃圾桶嗎?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{正在將項目移入垃圾桶…}other{正在將 <xliff:g id="COUNT">^1</xliff:g> 個項目移入垃圾桶…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」將這個音訊檔案移出垃圾桶嗎?}other{允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」將這 <xliff:g id="COUNT">^2</xliff:g> 個音訊檔案移出垃圾桶嗎?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{正在將音訊檔案移出垃圾桶…}other{正在將 <xliff:g id="COUNT">^1</xliff:g> 個音訊檔案移出垃圾桶…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」將這部影片移出垃圾桶嗎?}other{允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」將這 <xliff:g id="COUNT">^2</xliff:g> 部影片移出垃圾桶嗎?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{正在將影片移出垃圾桶…}other{正在將 <xliff:g id="COUNT">^1</xliff:g> 部影片移出垃圾桶…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」將這張相片移出垃圾桶嗎?}other{允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」將這 <xliff:g id="COUNT">^2</xliff:g> 張相片移出垃圾桶嗎?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{正在將相片移出垃圾桶…}other{正在將 <xliff:g id="COUNT">^1</xliff:g> 張相片移出垃圾桶…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」將這個項目移出垃圾桶嗎?}other{允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」將這 <xliff:g id="COUNT">^2</xliff:g> 個項目移出垃圾桶嗎?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{正在將項目移出垃圾桶…}other{正在將 <xliff:g id="COUNT">^1</xliff:g> 個項目移出垃圾桶…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」刪除這個音訊檔案嗎?}other{允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」刪除這 <xliff:g id="COUNT">^2</xliff:g> 個音訊檔案嗎?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{正在刪除音訊檔案…}other{正在刪除 <xliff:g id="COUNT">^1</xliff:g> 個音訊檔案…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」刪除這部影片嗎?}other{允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」刪除這 <xliff:g id="COUNT">^2</xliff:g> 部影片嗎?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{正在刪除影片…}other{正在刪除 <xliff:g id="COUNT">^1</xliff:g> 部影片…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」刪除這張相片嗎?}other{允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」刪除這 <xliff:g id="COUNT">^2</xliff:g> 張相片嗎?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{正在刪除相片…}other{刪除 <xliff:g id="COUNT">^1</xliff:g> 張相片…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{允許「<xliff:g id="APP_NAME_0">^1</xliff:g>」刪除這個項目嗎?}other{允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」刪除這 <xliff:g id="COUNT">^2</xliff:g> 個項目嗎?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{正在刪除項目…}other{正在刪除 <xliff:g id="COUNT">^1</xliff:g> 個項目…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"「<xliff:g id="APP_NAME">%s</xliff:g>」無法處理媒體檔案"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"已取消處理媒體"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"處理媒體時發生錯誤"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 36d01de..9b06733 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -19,6 +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>
+ <string name="picker_app_label" msgid="5487796494405738578">"Isikhi sesithombe"</string>
<string name="artist_label" msgid="8105600993099120273">"Umculi"</string>
<string name="unknown" msgid="2059049215682829375">"Akwaziwa"</string>
<string name="root_images" msgid="5861633549189045666">"Izithombe"</string>
@@ -29,148 +30,87 @@
<string name="permission_required_action" msgid="706370952366113539">"Qhubeka"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Vumela"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Phika"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- </plurals>
- <plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
- <item quantity="one">Kanye no-<xliff:g id="COUNT_1">^1</xliff:g> izinto ezingeziwe</item>
- <item quantity="other">Kanye no-<xliff:g id="COUNT_1">^1</xliff:g> izinto ezingeziwe</item>
- </plurals>
+ <string name="permission_more_thumb" msgid="1938863829470531577">"{count,plural, =1{+<xliff:g id="COUNT_0">^1</xliff:g>}one{+<xliff:g id="COUNT_1">^1</xliff:g>}other{+<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="permission_more_text" msgid="2471785045095597753">"{count,plural, =1{Kanye nento engeziwe engu-<xliff:g id="COUNT_0">^1</xliff:g>}one{Kanye nezinto ezingeziwe ezingu-<xliff:g id="COUNT_1">^1</xliff:g>}other{Kanye nezinto ezingeziwe ezingu-<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
<string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Sula amafayela ohlelo lokusebenza wesikhashana"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"I-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ingathanda ukusula amanye amafayela esikhashana. Lokhu kungaholela ekusetshenzisweni kwebhethri okuphezulu noma kwedatha yeselula."</string>
<string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Isula amafayela wohlelo lokusebenza wesikhashana…"</string>
<string name="clear" msgid="5524638938415865915">"Sula"</string>
<string name="allow" msgid="8885707816848569619">"Vumela"</string>
<string name="deny" msgid="6040983710442068936">"Nqaba"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuguqula amafayela omsindo angu-<xliff:g id="COUNT">^2</xliff:g>?</item>
- <item quantity="other">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuguqula amafayela omsindo angu-<xliff:g id="COUNT">^2</xliff:g>?</item>
- </plurals>
- <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
- <item quantity="one">Ilungisa amafayela womsindo angu-<xliff:g id="COUNT">^1</xliff:g>…</item>
- <item quantity="other">Ilungisa amafayela womsindo angu-<xliff:g id="COUNT">^1</xliff:g>…</item>
- </plurals>
- <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
- <item quantity="one">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuguqula amavidiyo angu-<xliff:g id="COUNT">^2</xliff:g>?</item>
- <item quantity="other">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuguqula amavidiyo angu-<xliff:g id="COUNT">^2</xliff:g>?</item>
- </plurals>
- <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
- <item quantity="one">Ilungisa amavidiyo angu-<xliff:g id="COUNT">^1</xliff:g>…</item>
- <item quantity="other">Ilungisa amavidiyo angu-<xliff:g id="COUNT">^1</xliff:g>…</item>
- </plurals>
- <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
- <item quantity="one">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuguqula izithombe ezingu-<xliff:g id="COUNT">^2</xliff:g>?</item>
- <item quantity="other">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuguqula izithombe ezingu-<xliff:g id="COUNT">^2</xliff:g>?</item>
- </plurals>
- <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
- <item quantity="one">Ilungisa izithombe ezingu-<xliff:g id="COUNT">^1</xliff:g>…</item>
- <item quantity="other">Ilungisa izithombe ezingu-<xliff:g id="COUNT">^1</xliff:g>…</item>
- </plurals>
- <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
- <item quantity="one">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuguqula izinto ezingu-<xliff:g id="COUNT">^2</xliff:g>?</item>
- <item quantity="other">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuguqula izinto ezingu-<xliff:g id="COUNT">^2</xliff:g>?</item>
- </plurals>
- <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
- <item quantity="one">Ilungisa izinto ezingu-<xliff:g id="COUNT">^1</xliff:g>…</item>
- <item quantity="other">Ilungisa izinto ezingu-<xliff:g id="COUNT">^1</xliff:g>…</item>
- </plurals>
- <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
- <item quantity="one">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa amafayela omsindo angu-<xliff:g id="COUNT">^2</xliff:g> kudoti?</item>
- <item quantity="other">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa amafayela omsindo angu-<xliff:g id="COUNT">^2</xliff:g> kudoti?</item>
- </plurals>
- <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
- <item quantity="one">Ihambisa amafayela womsindo angu-<xliff:g id="COUNT">^1</xliff:g> kudoti…</item>
- <item quantity="other">Ihambisa amafayela womsindo angu-<xliff:g id="COUNT">^1</xliff:g> kudoti…</item>
- </plurals>
- <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
- <item quantity="one">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa amavidiyo angu-<xliff:g id="COUNT">^2</xliff:g> kudoti?</item>
- <item quantity="other">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa amavidiyo angu-<xliff:g id="COUNT">^2</xliff:g> kudoti?</item>
- </plurals>
- <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
- <item quantity="one">Ihambisa amavidiyo angu-<xliff:g id="COUNT">^1</xliff:g> kudoti…</item>
- <item quantity="other">Ihambisa amavidiyo angu-<xliff:g id="COUNT">^1</xliff:g> kudoti…</item>
- </plurals>
- <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
- <item quantity="one">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa izithombe ezingu ku-<xliff:g id="COUNT">^2</xliff:g>?</item>
- <item quantity="other">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa izithombe ezingu ku-<xliff:g id="COUNT">^2</xliff:g>?</item>
- </plurals>
- <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
- <item quantity="one">Ihambisa izithombe ezingu-<xliff:g id="COUNT">^1</xliff:g> kudoti…</item>
- <item quantity="other">Ihambisa izithombe ezingu-<xliff:g id="COUNT">^1</xliff:g> kudoti…</item>
- </plurals>
- <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
- <item quantity="one">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa izinto ezingu-<xliff:g id="COUNT">^2</xliff:g> kudoti?</item>
- <item quantity="other">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa izinto ezingu-<xliff:g id="COUNT">^2</xliff:g> kudoti?</item>
- </plurals>
- <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
- <item quantity="one">Ihambisa izinto ezingu-<xliff:g id="COUNT">^1</xliff:g> kudoti…</item>
- <item quantity="other">Ihambisa izinto ezingu-<xliff:g id="COUNT">^1</xliff:g> kudoti…</item>
- </plurals>
- <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
- <item quantity="one">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa amafayela omsindo angu-<xliff:g id="COUNT">^2</xliff:g> ngaphandle kwedoti?</item>
- <item quantity="other">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa amafayela omsindo angu-<xliff:g id="COUNT">^2</xliff:g> ngaphandle kwedoti?</item>
- </plurals>
- <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
- <item quantity="one">Ikhipha amafayela omsindo angu-<xliff:g id="COUNT">^1</xliff:g> kudoti…</item>
- <item quantity="other">Ikhipha amafayela omsindo angu-<xliff:g id="COUNT">^1</xliff:g> kudoti…</item>
- </plurals>
- <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
- <item quantity="one">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa amavidiyo angu-<xliff:g id="COUNT">^2</xliff:g> ngaphandle kwedoti?</item>
- <item quantity="other">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa amavidiyo angu-<xliff:g id="COUNT">^2</xliff:g> ngaphandle kwedoti?</item>
- </plurals>
- <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
- <item quantity="one">Ikhipha amavidiyo angu-<xliff:g id="COUNT">^1</xliff:g> kudoti…</item>
- <item quantity="other">Ikhipha amavidiyo angu-<xliff:g id="COUNT">^1</xliff:g> kudoti…</item>
- </plurals>
- <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
- <item quantity="one">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa izithombe ezingu-<xliff:g id="COUNT">^2</xliff:g> ngaphandle kwedoti?</item>
- <item quantity="other">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa izithombe ezingu-<xliff:g id="COUNT">^2</xliff:g> ngaphandle kwedoti?</item>
- </plurals>
- <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
- <item quantity="one">Ikhipha izithombe ezingu-<xliff:g id="COUNT">^1</xliff:g> kudoti…</item>
- <item quantity="other">Ikhipha izithombe ezingu-<xliff:g id="COUNT">^1</xliff:g> kudoti…</item>
- </plurals>
- <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
- <item quantity="one">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa izinto ezingu-<xliff:g id="COUNT">^2</xliff:g> ngaphandle kwedoti?</item>
- <item quantity="other">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa izinto ezingu-<xliff:g id="COUNT">^2</xliff:g> ngaphandle kwedoti?</item>
- </plurals>
- <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
- <item quantity="one">Ikhipha izinto ezingu-<xliff:g id="COUNT">^1</xliff:g> kudoti…</item>
- <item quantity="other">Ikhipha izinto ezingu-<xliff:g id="COUNT">^1</xliff:g> kudoti…</item>
- </plurals>
- <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
- <item quantity="one">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukususa amafayela omsindo angu-<xliff:g id="COUNT">^2</xliff:g>?</item>
- <item quantity="other">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukususa amafayela omsindo angu-<xliff:g id="COUNT">^2</xliff:g>?</item>
- </plurals>
- <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
- <item quantity="one">Isusa amafayela omsindo angu-<xliff:g id="COUNT">^1</xliff:g>…</item>
- <item quantity="other">Isusa amafayela omsindo angu-<xliff:g id="COUNT">^1</xliff:g>…</item>
- </plurals>
- <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
- <item quantity="one">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukususa amavidiyo angu-<xliff:g id="COUNT">^2</xliff:g>?</item>
- <item quantity="other">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukususa amavidiyo angu-<xliff:g id="COUNT">^2</xliff:g>?</item>
- </plurals>
- <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
- <item quantity="one">Isusa amavidiyo angu-<xliff:g id="COUNT">^1</xliff:g>…</item>
- <item quantity="other">Isusa amavidiyo angu-<xliff:g id="COUNT">^1</xliff:g>…</item>
- </plurals>
- <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
- <item quantity="one">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukususa izithombe ezingu-<xliff:g id="COUNT">^2</xliff:g>?</item>
- <item quantity="other">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukususa izithombe ezingu-<xliff:g id="COUNT">^2</xliff:g>?</item>
- </plurals>
- <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
- <item quantity="one">Isusa izithombe ezingu-<xliff:g id="COUNT">^1</xliff:g>…</item>
- <item quantity="other">Isusa izithombe ezingu-<xliff:g id="COUNT">^1</xliff:g>…</item>
- </plurals>
- <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
- <item quantity="one">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukususa izinto ezingu-<xliff:g id="COUNT">^2</xliff:g>?</item>
- <item quantity="other">Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukususa izinto ezingu-<xliff:g id="COUNT">^2</xliff:g>?</item>
- </plurals>
- <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
- <item quantity="one">Isusa izinto ezingu-<xliff:g id="COUNT">^1</xliff:g>…</item>
- <item quantity="other">Isusa izinto ezingu-<xliff:g id="COUNT">^1</xliff:g>…</item>
- </plurals>
+ <string name="add" msgid="2894574044585549298">"Engeza"</string>
+ <string name="deselect" msgid="4297825044827769490">"Susa ukukhetha"</string>
+ <string name="deselected" msgid="8488133193326208475">"Okususwe ekukhethweni"</string>
+ <string name="select" msgid="2704765470563027689">"Khetha"</string>
+ <string name="selected" msgid="9151797369975828124">"Okukhethiwe"</string>
+ <string name="select_up_to" msgid="6994294169508439957">"{count,plural, =1{Khetha into efika kwengu-<xliff:g id="COUNT_0">^1</xliff:g>}one{Khetha izinto ezifika kwezingu-<xliff:g id="COUNT_1">^1</xliff:g>}other{Khetha izinto ezifika kwezingu-<xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="recent" msgid="6694613584743207874">"Okwakamuva"</string>
+ <string name="picker_photos_empty_message" msgid="5980619500554575558">"Azikho izithombe noma amavidiyo"</string>
+ <string name="picker_albums_empty_message" msgid="8341079772950966815">"Awekho ama-albhamu"</string>
+ <string name="picker_view_selected" msgid="2266031384396143883">"Ukubuka kukhethiwe"</string>
+ <string name="picker_photos" msgid="7415035516411087392">"Izithombe"</string>
+ <string name="picker_albums" msgid="4822511902115299142">"Ama-albhamu"</string>
+ <string name="picker_preview" msgid="6257414886055861039">"Hlola kuqala"</string>
+ <string name="picker_work_profile" msgid="2083221066869141576">"Shintshela kokmsebenzi"</string>
+ <string name="picker_personal_profile" msgid="639484258397758406">"Shintshela kokomuntu siqu"</string>
+ <string name="picker_profile_admin_title" msgid="4172022376418293777">"Kuvinjwe ngumphathi wakho"</string>
+ <string name="picker_profile_admin_msg_from_personal" msgid="1941639895084555723">"Ukufinyelela idatha evela ku-app yomuntu siqu akuvunyelwe"</string>
+ <string name="picker_profile_admin_msg_from_work" msgid="8048524337462790110">"Ukufinyelela idatha yomuntu siqu evela ku-app yomsebenzi akuvunyelwe"</string>
+ <string name="picker_profile_work_paused_title" msgid="382212880704235925">"Ama-app okusebenza aphunyuziwe"</string>
+ <string name="picker_profile_work_paused_msg" msgid="6321552322125246726">"Ukuze uvule izithombe zomsebenzi, vula ama-app wakho womsebenzi bese uzama futhi"</string>
+ <string name="picker_privacy_message" msgid="9132700451027116817">"Le app ingafinyelela izithombe ozikhethayo kuphela"</string>
+ <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{into <xliff:g id="COUNT_0">^1</xliff:g>}one{izinto <xliff:g id="COUNT_1">^1</xliff:g>}other{izinto <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
+ <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Engeza (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+ <string name="picker_category_camera" msgid="4857367052026843664">"Ikhamera"</string>
+ <string name="picker_category_downloads" msgid="793866660287361900">"Okulandiwe"</string>
+ <string name="picker_category_favorites" msgid="7008495397818966088">"Izintandokazi"</string>
+ <string name="picker_category_screenshots" msgid="7216102327587644284">"Izithombe-skrini"</string>
+ <!-- no translation found for picker_category_videos (1478458836380241356) -->
+ <skip />
+ <string name="picker_motion_photo_text" msgid="5016603812468180816">"Isithombe Esinyakazayo"</string>
+ <string name="picker_item_content_desc" msgid="7680591530155286423">"<xliff:g id="ITEM_NAME">%1$s</xliff:g> thatha ngo-<xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="picker_video_item_content_desc" msgid="7828900089119214801">"Ividiyo ethathwe ngo-<xliff:g id="TIME">%1$s</xliff:g> yobude besikhathi obungu-<xliff:g id="DURATION">%2$s</xliff:g>"</string>
+ <string name="picker_photo" msgid="1739342083494962153">"Isithombe"</string>
+ <string name="picker_gif" msgid="8333318083107368726">"I-GIF"</string>
+ <string name="picker_motion_photo" msgid="4385182195289546308">"Isithombe Esinyakazayo"</string>
+ <string name="picker_mute_video" msgid="2496585809229800096">"Thulisa ividiyo"</string>
+ <string name="picker_unmute_video" msgid="6611741290641963568">"Susa ukuthula kuvidiyo"</string>
+ <string name="picker_play_video" msgid="5158816108935317185">"Dlala ividiyo"</string>
+ <string name="picker_pause_video" msgid="7239492902901477371">"Misa ividiyo"</string>
+ <string name="picker_cloud_sync" msgid="997251377538536319">"Imidiya ye-cloud manje iyatholakala kusuka ku-<xliff:g id="PKG_NAME">%1$s</xliff:g>"</string>
+ <string name="not_selected" msgid="2244008151669896758">"akukhethiwe"</string>
+ <string name="permission_write_audio" msgid="8819694245323580601">"{count,plural, =1{Vumela i-<xliff:g id="APP_NAME_0">^1</xliff:g> ukuguqula leli fayela lomsindo?}one{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuguqula amafayela omsindo angu-<xliff:g id="COUNT">^2</xliff:g>?}other{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuguqula amafayela omsindo angu-<xliff:g id="COUNT">^2</xliff:g>?}}"</string>
+ <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Ilungisa ifayela lomsindo…}one{Ilungisa amafayela womsindo angu-<xliff:g id="COUNT">^1</xliff:g>…}other{Ilungisa amafayela womsindo angu-<xliff:g id="COUNT">^1</xliff:g>…}}"</string>
+ <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{Vumela i-<xliff:g id="APP_NAME_0">^1</xliff:g> ukuguqula le vidiyo?}one{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuguqula amavidiyo angu-<xliff:g id="COUNT">^2</xliff:g>?}other{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuguqula amavidiyo angu-<xliff:g id="COUNT">^2</xliff:g>?}}"</string>
+ <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Ilungisa ividiyo…}one{Ilungisa amavidiyo angu-<xliff:g id="COUNT">^1</xliff:g>…}other{Ilungisa amavidiyo angu-<xliff:g id="COUNT">^1</xliff:g>…}}"</string>
+ <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{Vumela i-<xliff:g id="APP_NAME_0">^1</xliff:g> ukuguqula lesi sithombe?}one{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuguqula izithombe ezingu-<xliff:g id="COUNT">^2</xliff:g>?}other{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuguqula izithombe ezingu-<xliff:g id="COUNT">^2</xliff:g>?}}"</string>
+ <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Ilungisa isithombe…}one{Ilungisa izithombe ezingu-<xliff:g id="COUNT">^1</xliff:g>…}other{Ilungisa izithombe ezingu-<xliff:g id="COUNT">^1</xliff:g>…}}"</string>
+ <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{Vumela i-<xliff:g id="APP_NAME_0">^1</xliff:g> ukuguqula le nto?}one{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuguqula izinto ezingu-<xliff:g id="COUNT">^2</xliff:g>?}other{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuguqula izinto ezingu-<xliff:g id="COUNT">^2</xliff:g>?}}"</string>
+ <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Ilungisa into…}one{Ilungisa izinto ezingu-<xliff:g id="COUNT">^1</xliff:g>…}other{Ilungisa izinto ezingu-<xliff:g id="COUNT">^1</xliff:g>…}}"</string>
+ <string name="permission_trash_audio" msgid="6554672354767742206">"{count,plural, =1{Vumela i-<xliff:g id="APP_NAME_0">^1</xliff:g> ukuhambisa leli fayela lomsindo kudoti?}one{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa amafayela omsindo angu-<xliff:g id="COUNT">^2</xliff:g> kudoti?}other{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa amafayela omsindo angu-<xliff:g id="COUNT">^2</xliff:g> kudoti?}}"</string>
+ <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Ihambisa ifayela lomsindo kudoti…}one{Ihambisa amafayela womsindo angu-<xliff:g id="COUNT">^1</xliff:g> kudoti…}other{Ihambisa amafayela womsindo angu-<xliff:g id="COUNT">^1</xliff:g> kudoti…}}"</string>
+ <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{Vumela i-<xliff:g id="APP_NAME_0">^1</xliff:g> ukuhambisa le vidiyo kudoti?}one{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa amavidiyo angu-<xliff:g id="COUNT">^2</xliff:g> kudoti?}other{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa amavidiyo angu-<xliff:g id="COUNT">^2</xliff:g> kudoti?}}"</string>
+ <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Ihambisa ividiyo kudoti…}one{Ihambisa amavidiyo angu-<xliff:g id="COUNT">^1</xliff:g> kudoti…}other{Ihambisa amavidiyo angu-<xliff:g id="COUNT">^1</xliff:g> kudoti…}}"</string>
+ <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{Vumela i-<xliff:g id="APP_NAME_0">^1</xliff:g> ukuhambisa lesi sithombe kudoti?}one{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa izithombe ezingu ku-<xliff:g id="COUNT">^2</xliff:g>?}other{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa izithombe ezingu ku-<xliff:g id="COUNT">^2</xliff:g>?}}"</string>
+ <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Ihambisa isithombe kudoti…}one{Ihambisa izithombe ezingu-<xliff:g id="COUNT">^1</xliff:g> kudoti…}other{Ihambisa izithombe ezingu-<xliff:g id="COUNT">^1</xliff:g> kudoti…}}"</string>
+ <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{Vumela i-<xliff:g id="APP_NAME_0">^1</xliff:g> ukuhambisa le nto kudoti?}one{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa izinto ezingu-<xliff:g id="COUNT">^2</xliff:g> kudoti?}other{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa izinto ezingu-<xliff:g id="COUNT">^2</xliff:g> kudoti?}}"</string>
+ <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Ihambisa into kudoti…}one{Ihambisa izinto ezingu-<xliff:g id="COUNT">^1</xliff:g> kudoti…}other{Ihambisa izinto ezingu-<xliff:g id="COUNT">^1</xliff:g> kudoti…}}"</string>
+ <string name="permission_untrash_audio" msgid="8404597563284002472">"{count,plural, =1{Vumela i-<xliff:g id="APP_NAME_0">^1</xliff:g> ukuhambisa leli fayela lomsindo ngaphandle kwedoti?}one{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa amafayela omsindo angu-<xliff:g id="COUNT">^2</xliff:g> ngaphandle kwedoti?}other{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa amafayela omsindo angu-<xliff:g id="COUNT">^2</xliff:g> ngaphandle kwedoti?}}"</string>
+ <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Ikhipha ifayela lomsindo kudoti…}one{Ikhipha amafayela omsindo angu-<xliff:g id="COUNT">^1</xliff:g> kudoti…}other{Ikhipha amafayela omsindo angu-<xliff:g id="COUNT">^1</xliff:g> kudoti…}}"</string>
+ <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{Vumela i-<xliff:g id="APP_NAME_0">^1</xliff:g> ukuhambisa le vidiyo ngaphandle kwevidiyo?}one{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa amavidiyo angu-<xliff:g id="COUNT">^2</xliff:g> ngaphandle kwedoti?}other{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa amavidiyo angu-<xliff:g id="COUNT">^2</xliff:g> ngaphandle kwedoti?}}"</string>
+ <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Ikhipha ividiyo kudoti…}one{Ikhipha amavidiyo angu-<xliff:g id="COUNT">^1</xliff:g> kudoti…}other{Ikhipha amavidiyo angu-<xliff:g id="COUNT">^1</xliff:g> kudoti…}}"</string>
+ <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{Vumela i-<xliff:g id="APP_NAME_0">^1</xliff:g> ukuhambisa lesi sithombe ngaphandle kwedoti?}one{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa izithombe ezingu-<xliff:g id="COUNT">^2</xliff:g> ngaphandle kwedoti?}other{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa izithombe ezingu-<xliff:g id="COUNT">^2</xliff:g> ngaphandle kwedoti?}}"</string>
+ <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Ikhipha isithombe kudoti…}one{Ikhipha izithombe ezingu-<xliff:g id="COUNT">^1</xliff:g> kudoti…}other{Ikhipha izithombe ezingu-<xliff:g id="COUNT">^1</xliff:g> kudoti…}}"</string>
+ <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{Vumela i-<xliff:g id="APP_NAME_0">^1</xliff:g> ukuhambisa le nto ngaphandle kwedoti?}one{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa izinto ezingu-<xliff:g id="COUNT">^2</xliff:g> ngaphandle kwedoti?}other{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukuhambisa izinto ezingu-<xliff:g id="COUNT">^2</xliff:g> ngaphandle kwedoti?}}"</string>
+ <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Ikhipha into kudoti…}one{Ikhipha izinto ezingu-<xliff:g id="COUNT">^1</xliff:g> kudoti…}other{Ikhipha izinto ezingu-<xliff:g id="COUNT">^1</xliff:g> kudoti…}}"</string>
+ <string name="permission_delete_audio" msgid="3326674742892796627">"{count,plural, =1{Vumela i-<xliff:g id="APP_NAME_0">^1</xliff:g> ukususa leli fayela lomsindo?}one{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukususa amafayela omsindo angu-<xliff:g id="COUNT">^2</xliff:g>?}other{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukususa amafayela omsindo angu-<xliff:g id="COUNT">^2</xliff:g>?}}"</string>
+ <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Isula ifayela lomsindo…}one{Isula amafayela omsindo angu-<xliff:g id="COUNT">^1</xliff:g>…}other{Isula amafayela omsindo angu-<xliff:g id="COUNT">^1</xliff:g>…}}"</string>
+ <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{Vumela i-<xliff:g id="APP_NAME_0">^1</xliff:g> ukususa le vidiyo?}one{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukususa amavidiyo angu-<xliff:g id="COUNT">^2</xliff:g>?}other{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukususa amavidiyo angu-<xliff:g id="COUNT">^2</xliff:g>?}}"</string>
+ <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Isula ividiyo…}one{Isula amavidiyo angu-<xliff:g id="COUNT">^1</xliff:g>…}other{Isula amavidiyo angu-<xliff:g id="COUNT">^1</xliff:g>…}}"</string>
+ <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{Vumela i-<xliff:g id="APP_NAME_0">^1</xliff:g> ukususa lesi sithombe?}one{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukususa izithombe ezingu-<xliff:g id="COUNT">^2</xliff:g>?}other{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukususa izithombe ezingu-<xliff:g id="COUNT">^2</xliff:g>?}}"</string>
+ <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Isula isithombe…}one{Isula izithombe ezingu-<xliff:g id="COUNT">^1</xliff:g>…}other{Isula izithombe ezingu-<xliff:g id="COUNT">^1</xliff:g>…}}"</string>
+ <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{Vumela i-<xliff:g id="APP_NAME_0">^1</xliff:g> ukususa le nto?}one{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukususa izinto ezingu-<xliff:g id="COUNT">^2</xliff:g>?}other{Vumela i-<xliff:g id="APP_NAME_1">^1</xliff:g> ukususa izinto ezingu-<xliff:g id="COUNT">^2</xliff:g>?}}"</string>
+ <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Isula into…}one{Isula izinto ezingu-<xliff:g id="COUNT">^1</xliff:g>…}other{Isula izinto ezingu-<xliff:g id="COUNT">^1</xliff:g>…}}"</string>
<string name="transcode_denied" msgid="6760546817138288976">"I-<xliff:g id="APP_NAME">%s</xliff:g> ayikwazi ukucubungula amafayela emidiya"</string>
<string name="transcode_processing_cancelled" msgid="5340383917746945590">"Ukucubungula imidiya kukhanseliwe"</string>
<string name="transcode_processing_error" msgid="8921643164508407874">"Iphutha lokucubungula imidiya"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
new file mode 100644
index 0000000..631b186
--- /dev/null
+++ b/res/values/attrs.xml
@@ -0,0 +1,66 @@
+<?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.
+-->
+<resources>
+
+ <!-- The color of the drag bar in the photo picker -->
+ <attr name="pickerDragBarColor" format="reference|color" />
+
+ <!-- The highlight color of the photo picker. E.g. Add button -->
+ <attr name="pickerHighlightColor" format="reference|color" />
+
+ <!-- The most prominent highlight text color of the photo picker. E.g. Add button -->
+ <attr name="pickerHighlightTextColor" format="reference|color" />
+
+ <!-- The background color of the profile button. -->
+ <attr name="pickerProfileButtonColor" format="reference|color" />
+
+ <!-- The background color of the profile button when disabled. -->
+ <attr name="pickerDisabledProfileButtonColor" format="reference|color" />
+
+ <!-- The text color of the profile button. -->
+ <attr name="pickerProfileButtonTextColor" format="reference|color" />
+
+ <!-- The text color of the profile button when disabled. -->
+ <attr name="pickerDisabledProfileButtonTextColor" format="reference|color" />
+
+ <!-- The selected background color of the tab. -->
+ <attr name="pickerSelectedTabBackgroundColor" format="reference|color" />
+
+ <!-- The selected text color of the tab. -->
+ <attr name="pickerSelectedTabTextColor" format="reference|color" />
+
+ <!-- The default background color of the tab. -->
+ <attr name="pickerTabBackgroundColor" format="reference|color" />
+
+ <!-- The most prominent text color of the photo picker. -->
+ <attr name="pickerTextColor" format="reference|color" />
+
+ <!-- The selected color of the view selected button and check icon -->
+ <attr name="pickerSelectedColor" format="reference|color" />
+
+ <!-- Photo Picker Profile Dialog button and icon color. -->
+ <attr name="pickerProfileDialogButtonAndIconColor" format="reference|color" />
+
+ <!-- Photo Picker Profile Dialog title color. -->
+ <attr name="pickerProfileDialogTitleColor" format="reference|color" />
+
+ <!-- Photo Picker Profile Dialog body color. -->
+ <attr name="pickerProfileDialogBodyColor" format="reference|color" />
+
+ <!-- Photo Picker Profile Dialog background color. -->
+ <attr name="pickerProfileDialogBackgroundColor" format="reference|color" />
+
+</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index ed0c6c2..39e4cb8 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -17,4 +17,22 @@
<resources>
<color name="thumb_gray_color">#1F000000</color>
<color name="clear_cache_icon_color">#5F6368</color>
+
+ <!-- PhotoPicker -->
+ <color name="picker_background_color">@android:color/white</color>
+ <color name="picker_default_white">@android:color/white</color>
+
+ <!-- PhotoPicker check button -->
+ <color name="picker_unselected_check_color">@android:color/white</color>
+
+ <!-- PhotoPicker gradient colors -->
+ <color name="picker_item_gradient_color">#61202124</color> <!-- 38% opacity -->
+ <color name="preview_gradient_color_light">@android:color/transparent</color>
+ <color name="preview_gradient_color_dark">#80202124</color>
+ <color name="preview_scrim_solid_color">#E6000000</color> <!-- 90% opacity black -->
+
+ <!-- PhotoPicker Preview -->
+ <color name="preview_highlight_color">#8AB4F8</color>
+ <color name="preview_default_grey">#202124</color>
+ <color name="preview_background_color">@android:color/black</color>
</resources>
diff --git a/res/values/config.xml b/res/values/config.xml
new file mode 100644
index 0000000..5705b20
--- /dev/null
+++ b/res/values/config.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+
+<resources>
+ <string name="config_default_cloud_provider_authority" translatable="false"></string>
+ <string-array name="config_supported_transcoding_relative_paths" translatable="false">
+ <item>DCIM/Camera/</item>
+ </string-array>
+ <string-array name="config_supported_uncached_relative_paths" translatable="false">
+ </string-array>
+</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index c3bdf8f..8367561 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -19,4 +19,85 @@
<dimen name="permission_thumb_size">64dp</dimen>
<dimen name="permission_thumb_margin">6dp</dimen>
<dimen name="dialog_space">20dp</dimen>
+
+ <!-- PhotoPicker -->
+ <dimen name="picker_top_corner_radius">28dp</dimen>
+ <dimen name="picker_photo_size">118dp</dimen>
+ <dimen name="picker_album_size">156dp</dimen>
+
+ <dimen name="picker_bottom_bar_size">56dp</dimen>
+ <dimen name="picker_bottom_bar_horizontal_gap">16dp</dimen>
+ <dimen name="picker_bottom_bar_buttons_vertical_gap">10dp</dimen>
+ <dimen name="picker_bottom_bar_elevation">8dp</dimen>
+
+ <dimen name="picker_viewselected_icon_padding">10dp</dimen>
+ <dimen name="picker_viewselected_icon_size">24dp</dimen>
+
+ <dimen name="picker_item_check_size">24dp</dimen>
+ <dimen name="picker_item_check_margin">6dp</dimen>
+ <dimen name="picker_item_badge_margin">4dp</dimen>
+ <dimen name="picker_item_badge_text_margin">3dp</dimen>
+ <dimen name="picker_item_badge_text_size">10dp</dimen>
+ <dimen name="picker_item_gradient_height">40dp</dimen>
+
+ <dimen name="picker_date_header_height">56dp</dimen>
+ <dimen name="picker_date_header_padding">16dp</dimen>
+
+ <dimen name="picker_album_name_min_height">20dp</dimen>
+ <dimen name="picker_album_name_margin">8dp</dimen>
+ <dimen name="picker_album_item_count_height">16dp</dimen>
+ <dimen name="picker_album_item_count_margin">2dp</dimen>
+ <dimen name="picker_album_grid_radius">8dp</dimen>
+ <dimen name="picker_album_item_top_spacing">28dp</dimen>
+ <dimen name="picker_album_item_spacing">16dp</dimen>
+
+ <dimen name="picker_photo_item_spacing">3dp</dimen>
+
+ <!-- Photo Picker recycler view bottom padding for profile button or bottom bar -->
+ <dimen name="picker_recycler_view_bottom_padding">78dp</dimen>
+
+ <dimen name="picker_tab_text_size">14sp</dimen>
+ <dimen name="picker_tab_radius">16dp</dimen>
+ <dimen name="picker_tab_height">32dp</dimen>
+ <dimen name="picker_tab_width">80dp</dimen>
+ <dimen name="picker_tab_min_width">88dp</dimen>
+ <dimen name="picker_tab_horizontal_gap">4dp</dimen>
+
+ <dimen name="picker_drag_margin_top">16dp</dimen>
+ <dimen name="picker_drag_margin_bottom">16dp</dimen>
+
+ <dimen name="picker_privacy_text_margin_top">8dp</dimen>
+ <dimen name="picker_privacy_text_margin_bottom">16dp</dimen>
+ <dimen name="picker_privacy_text_horizontal_gap">12dp</dimen>
+ <dimen name="picker_privacy_text_size">11sp</dimen>
+
+ <dimen name="picker_empty_text_margin">20dp</dimen>
+ <dimen name="picker_empty_text_size">18sp</dimen>
+
+ <dimen name="picker_toolbar_title_text_size">18sp</dimen>
+
+ <!-- PhotoPicker Preview -->
+ <dimen name="preview_buttons_padding_horizontal">16dp</dimen>
+ <dimen name="preview_deselect_padding_start">2dp</dimen>
+ <dimen name="preview_viewpager_margin">20dp</dimen>
+ <dimen name="preview_gif_icon_size">32dp</dimen>
+ <dimen name="preview_add_or_select_width">328dp</dimen>
+ <dimen name="preview_mute_button_size">48dp</dimen>
+ <dimen name="preview_mute_marginEnd">4dp</dimen>
+ <dimen name="preview_mute_marginBottom">56dp</dimen>
+
+ <!-- PhotoPicker Preview text -->
+ <dimen name="preview_special_format_text_size">12sp</dimen>
+ <dimen name="preview_special_format_text_margin">8dp</dimen>
+ <dimen name="preview_special_format_padding_end">16dp</dimen>
+ <dimen name="preview_toolbar_scrim_height">96dp</dimen>
+ <dimen name="preview_deselect_scrim_height">240dp</dimen>
+
+ <!-- PhotoPicker Work Profile -->
+ <dimen name="picker_profile_button_margin_bottom">16dp</dimen>
+ <dimen name="picker_profile_dialog_radius">8dp</dimen>
+ <dimen name="picker_profile_dialog_scrim">0.6</dimen>
+ <dimen name="picker_profile_dialog_title_text_size">14sp</dimen>
+ <dimen name="picker_profile_dialog_icon_height">24dp</dimen>
+ <dimen name="picker_profile_dialog_icon_width">24dp</dimen>
</resources>
diff --git a/res/values/drawables.xml b/res/values/drawables.xml
new file mode 100644
index 0000000..a5a0b3d
--- /dev/null
+++ b/res/values/drawables.xml
@@ -0,0 +1,21 @@
+<!--
+ ~ 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/res/values/overlayable.xml b/res/values/overlayable.xml
new file mode 100644
index 0000000..c35a43c
--- /dev/null
+++ b/res/values/overlayable.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <overlayable name="MediaProviderConfig">
+ <policy type="product|system|vendor">
+ <item type="string" name="config_default_cloud_provider_authority"/>
+ <item type="array" name="config_supported_transcoding_relative_paths"/>
+ <item type="array" name="config_supported_uncached_relative_paths"/>
+ </policy>
+ </overlayable>
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 98c885b..4f1570f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -24,6 +24,9 @@
<!-- Label to show to user for this package. -->
<string name="app_label">Media Storage</string>
+ <!-- Label to show to user for Photo picker. -->
+ <string name="picker_app_label">Photo picker</string>
+
<!-- Description line for music artists in the search/suggestion results -->
<string name="artist_label">Artist</string>
@@ -50,16 +53,16 @@
<string name="grant_dialog_button_deny">Deny</string>
<!-- Text placed over a visual thumbnail indicating that there are more items beyond the number currently displayed on the screen. [CHAR LIMIT=6] -->
- <plurals name="permission_more_thumb">
- <item quantity="one">+<xliff:g id="count" example="1">^1</xliff:g></item>
- <item quantity="other">+<xliff:g id="count" example="42">^1</xliff:g></item>
- </plurals>
+ <string name="permission_more_thumb"> {count, plural,
+ =1 {+<xliff:g id="count" example="1">^1</xliff:g>}
+ other {+<xliff:g id="count" example="42">^1</xliff:g>}
+ }</string>
<!-- Text shown at the end of a list indicating that there are more items beyond the number currently displayed on the screen. [CHAR LIMIT=32] -->
- <plurals name="permission_more_text">
- <item quantity="one">Plus <xliff:g id="count" example="42">^1</xliff:g> additional item</item>
- <item quantity="other">Plus <xliff:g id="count" example="42">^1</xliff:g> additional items</item>
- </plurals>
+ <string name="permission_more_text"> {count, plural,
+ =1 {Plus <xliff:g id="count" example="1">^1</xliff:g> additional item}
+ other {Plus <xliff:g id="count" example="42">^1</xliff:g> additional items}
+ }</string>
<!-- Cache clearing permission dialog warning title. [CHAR LIMIT=NONE] -->
<string name="cache_clearing_dialog_title">Clear temporary app files</string>
@@ -79,179 +82,299 @@
<!-- Deny dialog action text. [CHAR LIMIT=30] -->
<string name="deny">Deny</string>
+ <!-- Add button for PhotoPicker. [CHAR LIMIT=30] -->
+ <string name="add">Add</string>
+
+ <!-- Deselect button for PhotoPicker. [CHAR LIMIT=30] -->
+ <string name="deselect">Deselect</string>
+
+ <!-- Deselected check button for PhotoPicker. [CHAR LIMIT=30] -->
+ <string name="deselected">Deselected</string>
+
+ <!-- Select button for PhotoPicker. [CHAR LIMIT=30] -->
+ <string name="select">Select</string>
+
+ <!-- Selected check button for PhotoPicker. [CHAR LIMIT=30] -->
+ <string name="selected">Selected</string>
+
+ <!-- Select up to max label message for PhotoPicker. [CHAR LIMIT=30] -->
+ <string name="select_up_to"> {count, plural,
+ =1 {Select up to <xliff:g id="count" example="1">^1</xliff:g> item}
+ other {Select up to <xliff:g id="count" example="42">^1</xliff:g> items}
+ }</string>
+
+ <!-- Recent header for PhotoPicker. [CHAR LIMIT=50] -->
+ <string name="recent">Recent</string>
+
+ <!-- The message for empty message on Photos tab in PhotoPicker when the item count is zero. [CHAR LIMIT=NONE] -->
+ <string name="picker_photos_empty_message">No photos or videos</string>
+ <!-- The message for empty message on Albums tab in PhotoPicker when the item count is zero. [CHAR LIMIT=NONE] -->
+ <string name="picker_albums_empty_message">No albums</string>
+
+ <!-- PhotoPicker view selected action text. [CHAR LIMIT=80] -->
+ <string name="picker_view_selected">View selected</string>
+
+ <!-- The text of the photos tab for PhotoPicker. [CHAR LIMIT=30] -->
+ <string name="picker_photos">Photos</string>
+
+ <!-- The text of the albums tab for PhotoPicker. [CHAR LIMIT=30] -->
+ <string name="picker_albums">Albums</string>
+
+ <!-- The text of the preview page for PhotoPicker. [CHAR LIMIT=30] -->
+ <string name="picker_preview">Preview</string>
+
+ <!-- The text of the switching work/personal profile in PhotoPicker. [CHAR LIMIT=80] -->
+ <string name="picker_work_profile">Switch to work</string>
+ <!-- The text of the switching work/personal profile in PhotoPicker. [CHAR LIMIT=80] -->
+ <string name="picker_personal_profile">Switch to personal</string>
+ <!-- The title for error dialog in PhotoPicker when the admin blocks cross user interaction for
+ the intent. [CHAR LIMIT=100] -->
+ <string name="picker_profile_admin_title">Blocked by your admin</string>
+ <!-- The message for error dialog in PhotoPicker when the admin blocks cross user interaction
+ for the intent. [CHAR LIMIT=NONE] -->
+ <string name="picker_profile_admin_msg_from_personal">Accessing work data from a personal app is not permitted</string>
+ <!-- The message for error dialog in PhotoPicker when the admin blocks cross user interaction
+ for the intent. [CHAR LIMIT=NONE] -->
+ <string name="picker_profile_admin_msg_from_work">Accessing personal data from a work app is not permitted</string>
+ <!-- The title of the error dialog in PhotoPicker when the user tries to switch to work content,
+ but work profile is off. [CHAR LIMIT=80] -->
+ <string name="picker_profile_work_paused_title">Work apps are paused</string>
+ <!-- The message of the error dialog in PhotoPicker when the user tries to switch to work
+ content, but work profile is off. [CHAR LIMIT=NONE] -->
+ <string name="picker_profile_work_paused_msg">To open work photos, turn on your work apps then try again</string>
+
+ <!-- PhotoPicker privacy message. [CHAR LIMIT=80] -->
+ <string name="picker_privacy_message">This app can only access the photos you select</string>
+
+ <!-- Text shown on the album item in PhotoPicker. [CHAR LIMIT=30] -->
+ <string name="picker_album_item_count"> {count, plural,
+ =1 {<xliff:g id="count" example="1">^1</xliff:g> item}
+ other {<xliff:g id="count" example="42">^1</xliff:g> items}
+ }</string>
+
+ <!-- Text shown on the add button for multi-select in PhotoPicker. [CHAR LIMIT=30] -->
+ <string name="picker_add_button_multi_select">Add (<xliff:g id="count" example="42">^1</xliff:g>)</string>
+
+ <!-- Title for the category in the picker that offers items in Camera folder. [CHAR LIMIT=24] -->
+ <string name="picker_category_camera">Camera</string>
+ <!-- Title for the category in the picker that offers downloaded items. [CHAR LIMIT=24] -->
+ <string name="picker_category_downloads">Downloads</string>
+ <!-- Title for the category in the picker that offers favorite items. [CHAR LIMIT=24] -->
+ <string name="picker_category_favorites">Favorites</string>
+ <!-- Title for the category in the picker that offers screenshots. [CHAR LIMIT=24] -->
+ <string name="picker_category_screenshots">Screenshots</string>
+ <!-- Title for the category in the picker that offers videos. [CHAR LIMIT=24] -->
+ <string name="picker_category_videos">@string/root_videos</string>
+
+ <!-- Special format text in preview screen for Motion Photo. [CHAR LIMIT=30] -->
+ <string name="picker_motion_photo_text">Motion Photo</string>
+
+ <!-- Content description of when a photo was taken on [CHAR LIMIT=NONE] -->
+ <string name="picker_item_content_desc"><xliff:g id="item_name" example="Photo">%1$s</xliff:g> taken on <xliff:g id="time" example="Jul 7, 2020, 12:00:00 AM">%2$s</xliff:g></string>
+
+ <!-- Content description of an video item [CHAR LIMIT=NONE] -->
+ <string name="picker_video_item_content_desc">Video taken on <xliff:g id="time" example="Jul 7, 2020, 12:00:00 AM">%1$s</xliff:g> with duration <xliff:g id="duration" example="00:03">%2$s</xliff:g></string>
+
+ <!-- Title of the picker photo item [CHAR LIMIT=40] -->
+ <string name="picker_photo">Photo</string>
+
+ <!-- Title of the picker GIF item [CHAR LIMIT=40] -->
+ <string name="picker_gif">GIF</string>
+
+ <!-- Title of the picker motion photo item [CHAR LIMIT=60] -->
+ <string name="picker_motion_photo">Motion Photo</string>
+
+ <!-- Content description for a button that mutes the current video. [CHAR LIMIT=50] -->
+ <string name="picker_mute_video">Mute video</string>
+
+ <!-- Content description for a button that unmutes the current video. [CHAR LIMIT=50] -->
+ <string name="picker_unmute_video">Unmute video</string>
+
+ <!-- Content description for a button that playes the current video. [CHAR LIMIT=50] -->
+ <string name="picker_play_video">Play video</string>
+
+ <!-- Content description for a button that pauses the current video. [CHAR LIMIT=50] -->
+ <string name="picker_pause_video">Pause video</string>
+
+ <!-- Toast notifying user that cloud media content is now available from an app on their device. [CHAR LIMIT=NONE] -->
+ <string name="picker_cloud_sync">Cloud media now available from <xliff:g id="pkg_name" example="Gmail">%1$s</xliff:g></string>
+
+ <!-- Default not selected text used by accessibility for an element that can be unselected. [CHAR LIMIT=NONE] -->
+ <string name="not_selected">not selected</string>
+
<!-- ========================= BEGIN AUTO-GENERATED BY gen_strings.py ========================= -->
<!-- ========================= WRITE STRINGS ========================= -->
<!-- Dialog title asking if user will allow write permission to the audio item displayed below this string. [CHAR LIMIT=128] -->
- <plurals name="permission_write_audio">
- <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to modify this audio file?</item>
- <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to modify <xliff:g id="count" example="42">^2</xliff:g> audio files?</item>
- </plurals>
+ <string name="permission_write_audio"> {count, plural,
+ =1 {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to modify this audio file?}
+ other {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to modify <xliff:g id="count" example="42">^2</xliff:g> audio files?}
+ }</string>
<!-- Progress dialog message after user allows write permission to the audio item [CHAR LIMIT=128] -->
- <plurals name="permission_progress_write_audio">
- <item quantity="one">Modifying audio file…</item>
- <item quantity="other">Modifying <xliff:g id="count" example="42">^1</xliff:g> audio files…</item>
- </plurals>
+ <string name="permission_progress_write_audio"> {count, plural,
+ =1 {Modifying audio file…}
+ other {Modifying <xliff:g id="count" example="42">^1</xliff:g> audio files…}
+ }</string>
<!-- Dialog title asking if user will allow write permission to the video item displayed below this string. [CHAR LIMIT=128] -->
- <plurals name="permission_write_video">
- <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to modify this video?</item>
- <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to modify <xliff:g id="count" example="42">^2</xliff:g> videos?</item>
- </plurals>
+ <string name="permission_write_video"> {count, plural,
+ =1 {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to modify this video?}
+ other {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to modify <xliff:g id="count" example="42">^2</xliff:g> videos?}
+ }</string>
<!-- Progress dialog message after user allows write permission to the video item [CHAR LIMIT=128] -->
- <plurals name="permission_progress_write_video">
- <item quantity="one">Modifying video…</item>
- <item quantity="other">Modifying <xliff:g id="count" example="42">^1</xliff:g> videos…</item>
- </plurals>
+ <string name="permission_progress_write_video"> {count, plural,
+ =1 {Modifying video…}
+ other {Modifying <xliff:g id="count" example="42">^1</xliff:g> videos…}
+ }</string>
<!-- Dialog title asking if user will allow write permission to the image item displayed below this string. [CHAR LIMIT=128] -->
- <plurals name="permission_write_image">
- <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to modify this photo?</item>
- <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to modify <xliff:g id="count" example="42">^2</xliff:g> photos?</item>
- </plurals>
+ <string name="permission_write_image"> {count, plural,
+ =1 {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to modify this photo?}
+ other {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to modify <xliff:g id="count" example="42">^2</xliff:g> photos?}
+ }</string>
<!-- Progress dialog message after user allows write permission to the image item [CHAR LIMIT=128] -->
- <plurals name="permission_progress_write_image">
- <item quantity="one">Modifying photo…</item>
- <item quantity="other">Modifying <xliff:g id="count" example="42">^1</xliff:g> photos…</item>
- </plurals>
+ <string name="permission_progress_write_image"> {count, plural,
+ =1 {Modifying photo…}
+ other {Modifying <xliff:g id="count" example="42">^1</xliff:g> photos…}
+ }</string>
<!-- Dialog title asking if user will allow write permission to the generic item displayed below this string. [CHAR LIMIT=128] -->
- <plurals name="permission_write_generic">
- <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to modify this item?</item>
- <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to modify <xliff:g id="count" example="42">^2</xliff:g> items?</item>
- </plurals>
+ <string name="permission_write_generic"> {count, plural,
+ =1 {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to modify this item?}
+ other {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to modify <xliff:g id="count" example="42">^2</xliff:g> items?}
+ }</string>
<!-- Progress dialog message after user allows write permission to the generic item [CHAR LIMIT=128] -->
- <plurals name="permission_progress_write_generic">
- <item quantity="one">Modifying item…</item>
- <item quantity="other">Modifying <xliff:g id="count" example="42">^1</xliff:g> items…</item>
- </plurals>
+ <string name="permission_progress_write_generic"> {count, plural,
+ =1 {Modifying item…}
+ other {Modifying <xliff:g id="count" example="42">^1</xliff:g> items…}
+ }</string>
<!-- ========================= TRASH STRINGS ========================= -->
<!-- Dialog title asking if user will allow trash permission to the audio item displayed below this string. [CHAR LIMIT=128] -->
- <plurals name="permission_trash_audio">
- <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this audio file to trash?</item>
- <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> audio files to trash?</item>
- </plurals>
+ <string name="permission_trash_audio"> {count, plural,
+ =1 {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this audio file to trash?}
+ other {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> audio files to trash?}
+ }</string>
<!-- Progress dialog message after user allows trash permission to the audio item [CHAR LIMIT=128] -->
- <plurals name="permission_progress_trash_audio">
- <item quantity="one">Moving audio file to trash…</item>
- <item quantity="other">Moving <xliff:g id="count" example="42">^1</xliff:g> audio files to trash…</item>
- </plurals>
+ <string name="permission_progress_trash_audio"> {count, plural,
+ =1 {Moving audio file to trash…}
+ other {Moving <xliff:g id="count" example="42">^1</xliff:g> audio files to trash…}
+ }</string>
<!-- Dialog title asking if user will allow trash permission to the video item displayed below this string. [CHAR LIMIT=128] -->
- <plurals name="permission_trash_video">
- <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this video to trash?</item>
- <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> videos to trash?</item>
- </plurals>
+ <string name="permission_trash_video"> {count, plural,
+ =1 {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this video to trash?}
+ other {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> videos to trash?}
+ }</string>
<!-- Progress dialog message after user allows trash permission to the video item [CHAR LIMIT=128] -->
- <plurals name="permission_progress_trash_video">
- <item quantity="one">Moving video to trash…</item>
- <item quantity="other">Moving <xliff:g id="count" example="42">^1</xliff:g> videos to trash…</item>
- </plurals>
+ <string name="permission_progress_trash_video"> {count, plural,
+ =1 {Moving video to trash…}
+ other {Moving <xliff:g id="count" example="42">^1</xliff:g> videos to trash…}
+ }</string>
<!-- Dialog title asking if user will allow trash permission to the image item displayed below this string. [CHAR LIMIT=128] -->
- <plurals name="permission_trash_image">
- <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this photo to trash?</item>
- <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> photos to trash?</item>
- </plurals>
+ <string name="permission_trash_image"> {count, plural,
+ =1 {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this photo to trash?}
+ other {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> photos to trash?}
+ }</string>
<!-- Progress dialog message after user allows trash permission to the image item [CHAR LIMIT=128] -->
- <plurals name="permission_progress_trash_image">
- <item quantity="one">Moving photo to trash…</item>
- <item quantity="other">Moving <xliff:g id="count" example="42">^1</xliff:g> photos to trash…</item>
- </plurals>
+ <string name="permission_progress_trash_image"> {count, plural,
+ =1 {Moving photo to trash…}
+ other {Moving <xliff:g id="count" example="42">^1</xliff:g> photos to trash…}
+ }</string>
<!-- Dialog title asking if user will allow trash permission to the generic item displayed below this string. [CHAR LIMIT=128] -->
- <plurals name="permission_trash_generic">
- <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this item to trash?</item>
- <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> items to trash?</item>
- </plurals>
+ <string name="permission_trash_generic"> {count, plural,
+ =1 {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this item to trash?}
+ other {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> items to trash?}
+ }</string>
<!-- Progress dialog message after user allows trash permission to the generic item [CHAR LIMIT=128] -->
- <plurals name="permission_progress_trash_generic">
- <item quantity="one">Moving item to trash…</item>
- <item quantity="other">Moving <xliff:g id="count" example="42">^1</xliff:g> items to trash…</item>
- </plurals>
+ <string name="permission_progress_trash_generic"> {count, plural,
+ =1 {Moving item to trash…}
+ other {Moving <xliff:g id="count" example="42">^1</xliff:g> items to trash…}
+ }</string>
<!-- ========================= UNTRASH STRINGS ========================= -->
<!-- Dialog title asking if user will allow untrash permission to the audio item displayed below this string. [CHAR LIMIT=128] -->
- <plurals name="permission_untrash_audio">
- <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this audio file out of trash?</item>
- <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> audio files out of trash?</item>
- </plurals>
+ <string name="permission_untrash_audio"> {count, plural,
+ =1 {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this audio file out of trash?}
+ other {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> audio files out of trash?}
+ }</string>
<!-- Progress dialog message after user allows untrash permission to the audio item [CHAR LIMIT=128] -->
- <plurals name="permission_progress_untrash_audio">
- <item quantity="one">Moving audio file out of trash…</item>
- <item quantity="other">Moving <xliff:g id="count" example="42">^1</xliff:g> audio files out of trash…</item>
- </plurals>
+ <string name="permission_progress_untrash_audio"> {count, plural,
+ =1 {Moving audio file out of trash…}
+ other {Moving <xliff:g id="count" example="42">^1</xliff:g> audio files out of trash…}
+ }</string>
<!-- Dialog title asking if user will allow untrash permission to the video item displayed below this string. [CHAR LIMIT=128] -->
- <plurals name="permission_untrash_video">
- <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this video out of trash?</item>
- <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> videos out of trash?</item>
- </plurals>
+ <string name="permission_untrash_video"> {count, plural,
+ =1 {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this video out of trash?}
+ other {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> videos out of trash?}
+ }</string>
<!-- Progress dialog message after user allows untrash permission to the video item [CHAR LIMIT=128] -->
- <plurals name="permission_progress_untrash_video">
- <item quantity="one">Moving video out of trash…</item>
- <item quantity="other">Moving <xliff:g id="count" example="42">^1</xliff:g> videos out of trash…</item>
- </plurals>
+ <string name="permission_progress_untrash_video"> {count, plural,
+ =1 {Moving video out of trash…}
+ other {Moving <xliff:g id="count" example="42">^1</xliff:g> videos out of trash…}
+ }</string>
<!-- Dialog title asking if user will allow untrash permission to the image item displayed below this string. [CHAR LIMIT=128] -->
- <plurals name="permission_untrash_image">
- <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this photo out of trash?</item>
- <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> photos out of trash?</item>
- </plurals>
+ <string name="permission_untrash_image"> {count, plural,
+ =1 {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this photo out of trash?}
+ other {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> photos out of trash?}
+ }</string>
<!-- Progress dialog message after user allows untrash permission to the image item [CHAR LIMIT=128] -->
- <plurals name="permission_progress_untrash_image">
- <item quantity="one">Moving photo out of trash…</item>
- <item quantity="other">Moving <xliff:g id="count" example="42">^1</xliff:g> photos out of trash…</item>
- </plurals>
+ <string name="permission_progress_untrash_image"> {count, plural,
+ =1 {Moving photo out of trash…}
+ other {Moving <xliff:g id="count" example="42">^1</xliff:g> photos out of trash…}
+ }</string>
<!-- Dialog title asking if user will allow untrash permission to the generic item displayed below this string. [CHAR LIMIT=128] -->
- <plurals name="permission_untrash_generic">
- <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this item out of trash?</item>
- <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> items out of trash?</item>
- </plurals>
+ <string name="permission_untrash_generic"> {count, plural,
+ =1 {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this item out of trash?}
+ other {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> items out of trash?}
+ }</string>
<!-- Progress dialog message after user allows untrash permission to the generic item [CHAR LIMIT=128] -->
- <plurals name="permission_progress_untrash_generic">
- <item quantity="one">Moving item out of trash…</item>
- <item quantity="other">Moving <xliff:g id="count" example="42">^1</xliff:g> items out of trash…</item>
- </plurals>
+ <string name="permission_progress_untrash_generic"> {count, plural,
+ =1 {Moving item out of trash…}
+ other {Moving <xliff:g id="count" example="42">^1</xliff:g> items out of trash…}
+ }</string>
<!-- ========================= DELETE STRINGS ========================= -->
<!-- Dialog title asking if user will allow delete permission to the audio item displayed below this string. [CHAR LIMIT=128] -->
- <plurals name="permission_delete_audio">
- <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to delete this audio file?</item>
- <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to delete <xliff:g id="count" example="42">^2</xliff:g> audio files?</item>
- </plurals>
+ <string name="permission_delete_audio"> {count, plural,
+ =1 {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to delete this audio file?}
+ other {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to delete <xliff:g id="count" example="42">^2</xliff:g> audio files?}
+ }</string>
<!-- Progress dialog message after user allows delete permission to the audio item [CHAR LIMIT=128] -->
- <plurals name="permission_progress_delete_audio">
- <item quantity="one">Deleting audio file…</item>
- <item quantity="other">Deleting <xliff:g id="count" example="42">^1</xliff:g> audio files…</item>
- </plurals>
+ <string name="permission_progress_delete_audio"> {count, plural,
+ =1 {Deleting audio file…}
+ other {Deleting <xliff:g id="count" example="42">^1</xliff:g> audio files…}
+ }</string>
<!-- Dialog title asking if user will allow delete permission to the video item displayed below this string. [CHAR LIMIT=128] -->
- <plurals name="permission_delete_video">
- <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to delete this video?</item>
- <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to delete <xliff:g id="count" example="42">^2</xliff:g> videos?</item>
- </plurals>
+ <string name="permission_delete_video"> {count, plural,
+ =1 {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to delete this video?}
+ other {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to delete <xliff:g id="count" example="42">^2</xliff:g> videos?}
+ }</string>
<!-- Progress dialog message after user allows delete permission to the video item [CHAR LIMIT=128] -->
- <plurals name="permission_progress_delete_video">
- <item quantity="one">Deleting video…</item>
- <item quantity="other">Deleting <xliff:g id="count" example="42">^1</xliff:g> videos…</item>
- </plurals>
+ <string name="permission_progress_delete_video"> {count, plural,
+ =1 {Deleting video…}
+ other {Deleting <xliff:g id="count" example="42">^1</xliff:g> videos…}
+ }</string>
<!-- Dialog title asking if user will allow delete permission to the image item displayed below this string. [CHAR LIMIT=128] -->
- <plurals name="permission_delete_image">
- <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to delete this photo?</item>
- <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to delete <xliff:g id="count" example="42">^2</xliff:g> photos?</item>
- </plurals>
+ <string name="permission_delete_image"> {count, plural,
+ =1 {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to delete this photo?}
+ other {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to delete <xliff:g id="count" example="42">^2</xliff:g> photos?}
+ }</string>
<!-- Progress dialog message after user allows delete permission to the image item [CHAR LIMIT=128] -->
- <plurals name="permission_progress_delete_image">
- <item quantity="one">Deleting photo…</item>
- <item quantity="other">Deleting <xliff:g id="count" example="42">^1</xliff:g> photos…</item>
- </plurals>
+ <string name="permission_progress_delete_image"> {count, plural,
+ =1 {Deleting photo…}
+ other {Deleting <xliff:g id="count" example="42">^1</xliff:g> photos…}
+ }</string>
<!-- Dialog title asking if user will allow delete permission to the generic item displayed below this string. [CHAR LIMIT=128] -->
- <plurals name="permission_delete_generic">
- <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to delete this item?</item>
- <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to delete <xliff:g id="count" example="42">^2</xliff:g> items?</item>
- </plurals>
+ <string name="permission_delete_generic"> {count, plural,
+ =1 {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to delete this item?}
+ other {Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to delete <xliff:g id="count" example="42">^2</xliff:g> items?}
+ }</string>
<!-- Progress dialog message after user allows delete permission to the generic item [CHAR LIMIT=128] -->
- <plurals name="permission_progress_delete_generic">
- <item quantity="one">Deleting item…</item>
- <item quantity="other">Deleting <xliff:g id="count" example="42">^1</xliff:g> items…</item>
- </plurals>
+ <string name="permission_progress_delete_generic"> {count, plural,
+ =1 {Deleting item…}
+ other {Deleting <xliff:g id="count" example="42">^1</xliff:g> items…}
+ }</string>
<!-- ========================= END AUTO-GENERATED BY gen_strings.py ========================= -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 5f1e662..378bdd2 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -35,18 +35,84 @@
<item name="android:alertDialogTheme">@style/AlertDialogTheme</item>
</style>
- <style name="CacheClearingAlertDialogTitle"
- parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
- <item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">wrap_content</item>
- <item name="android:gravity">center</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
- <item name="android:textSize">16sp</item>
- <item name="android:textStyle">bold</item>
+ <style name="MaterialBorderlessButtonStyle"
+ parent="@style/Widget.MaterialComponents.Button.TextButton">
+ <item name="android:textAppearance">@style/PickerButtonTextAppearance</item>
</style>
- <style name="PermissionAlertDialogTitle"
- parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
- <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <style name="MaterialButtonStyle"
+ parent="@style/Widget.MaterialComponents.Button.UnelevatedButton">
+ <item name="android:textAppearance">@style/PickerButtonTextAppearance</item>
</style>
+
+ <style name="ProfileDialogTheme"
+ parent="@style/ThemeOverlay.MaterialComponents.MaterialAlertDialog.Centered">
+ <item name="shapeAppearanceOverlay">@style/ShapeAppearance</item>
+ <item name="android:backgroundDimAmount">@dimen/picker_profile_dialog_scrim</item>
+ <item name="android:background">?attr/pickerProfileDialogBackgroundColor</item>
+ <item name="materialAlertDialogTitleTextStyle">@style/MaterialAlertDialogTitleStyle</item>
+ <item name="materialAlertDialogBodyTextStyle">@style/MaterialAlertDialogBodyStyle</item>
+ <item name="materialAlertDialogTitleIconStyle">@style/MaterialAlertDialogIconStyle</item>
+ <item name="buttonBarPositiveButtonStyle">@style/PositiveButtonStyle</item>
+ </style>
+
+ <style name="ShapeAppearance">
+ <item name="cornerFamily">rounded</item>
+ <item name="cornerSize">@dimen/picker_profile_dialog_radius</item>
+ </style>
+
+ <style name="PositiveButtonStyle" parent="Widget.MaterialComponents.Button.TextButton.Dialog">
+ <item name="android:textColor">?attr/pickerProfileDialogButtonAndIconColor</item>
+ </style>
+
+ <style name="MaterialAlertDialogTitleStyle"
+ parent="@style/MaterialAlertDialog.MaterialComponents.Title.Text.CenterStacked">
+ <item name="android:textAppearance">@style/PickerProfileDialogTitle</item>
+ </style>
+
+ <style name="MaterialAlertDialogBodyStyle"
+ parent="@style/MaterialAlertDialog.MaterialComponents.Body.Text">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:textAppearance">@style/PickerProfileDialogBody</item>
+ </style>
+
+ <style name="MaterialAlertDialogIconStyle"
+ parent="@style/MaterialAlertDialog.MaterialComponents.Title.Icon.CenterStacked">
+ <item name="android:tint">?attr/pickerProfileDialogButtonAndIconColor</item>
+ <item name="android:importantForAccessibility">no</item>
+ <item name="android:layout_width">@dimen/picker_profile_dialog_icon_width</item>
+ <item name="android:layout_height">@dimen/picker_profile_dialog_icon_height</item>
+ </style>
+
+ <style name="PickerDefaultTheme" parent="@android:style/Theme.DeviceDefault.DayNight">
+ <!-- System | Widget section -->
+ <item name="android:backgroundDimEnabled">true</item>
+ <item name="android:navigationBarColor">@color/picker_background_color</item>
+ <item name="android:statusBarColor">@android:color/transparent</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowNoTitle">true</item>
+ </style>
+
+ <style name="PickerMaterialTheme" parent="@style/Theme.MaterialComponents.DayNight.NoActionBar">
+ <item name="materialAlertDialogTheme">@style/ProfileDialogTheme</item>
+ <item name="pickerDragBarColor">#DADCE0</item>
+ <item name="pickerHighlightColor">?android:attr/colorAccent</item>
+ <item name="pickerHighlightTextColor">@android:color/white</item>
+ <item name="pickerProfileButtonColor">#E8F0FE</item>
+ <item name="pickerDisabledProfileButtonColor">@android:color/white</item>
+ <item name="pickerProfileButtonTextColor">#0B57D0</item>
+ <item name="pickerDisabledProfileButtonTextColor">#42757575</item>
+ <item name="pickerSelectedTabBackgroundColor">#E8F0FE</item>
+ <item name="pickerSelectedTabTextColor">#185ABC</item>
+ <item name="pickerTabBackgroundColor">@color/picker_background_color</item>
+ <item name="pickerTextColor">?android:attr/textColorPrimary</item>
+ <item name="pickerSelectedColor">?android:attr/colorAccent</item>
+ <item name="pickerProfileDialogButtonAndIconColor">#1A73E8</item>
+ <item name="pickerProfileDialogTitleColor">#202124</item>
+ <item name="pickerProfileDialogBodyColor">#5F6368</item>
+ <item name="pickerProfileDialogBackgroundColor">@android:color/white</item>
+ </style>
+
</resources>
diff --git a/res/values/styles_text.xml b/res/values/styles_text.xml
new file mode 100644
index 0000000..f19fe71
--- /dev/null
+++ b/res/values/styles_text.xml
@@ -0,0 +1,66 @@
+<?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.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <style name="CacheClearingAlertDialogTitle"
+ parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:gravity">center</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:textStyle">bold</item>
+ </style>
+
+ <style name="PermissionAlertDialogTitle"
+ parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
+
+ <style name="PickerToolbarTitleTextAppearance"
+ parent="@android:style/TextAppearance.DeviceDefault.Large">
+ <item name="android:textSize">@dimen/picker_toolbar_title_text_size</item>
+ </style>
+
+ <style name="PickerHeaderTextAppearance"
+ parent="@android:style/TextAppearance.DeviceDefault.Medium">
+ <item name="android:textColor">?attr/pickerTextColor</item>
+ <item name="android:textSize">14sp</item>
+ </style>
+
+ <style name="PickerTabTextAppearance"
+ parent="@android:style/TextAppearance.DeviceDefault.Widget.TabWidget">
+ <item name="android:textSize">@dimen/picker_tab_text_size</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+
+ <style name="PickerButtonTextAppearance"
+ parent="@android:style/TextAppearance.DeviceDefault.Widget.Button"/>
+
+ <style name="PickerProfileDialogTitle"
+ parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
+ <item name="android:textAllCaps">false</item>
+ <item name="android:textColor">?attr/pickerProfileDialogTitleColor</item>
+ <item name="android:textSize">@dimen/picker_profile_dialog_title_text_size</item>
+ </style>
+
+ <style name="PickerProfileDialogBody" parent="@android:style/TextAppearance.Material.Body2">
+ <item name="android:textAllCaps">false</item>
+ <item name="android:textColor">?attr/pickerProfileDialogBodyColor</item>
+ </style>
+
+</resources>
diff --git a/src/com/android/providers/media/DatabaseHelper.java b/src/com/android/providers/media/DatabaseHelper.java
index 7f863bd..e4700b0 100644
--- a/src/com/android/providers/media/DatabaseHelper.java
+++ b/src/com/android/providers/media/DatabaseHelper.java
@@ -20,6 +20,7 @@
import static com.android.providers.media.util.Logging.LOGV;
import static com.android.providers.media.util.Logging.TAG;
+import android.annotation.SuppressLint;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentUris;
@@ -34,10 +35,13 @@
import android.database.sqlite.SQLiteOpenHelper;
import android.mtp.MtpConstants;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.MediaStore;
@@ -61,8 +65,9 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.modules.utils.BackgroundThread;
+import com.android.providers.media.dao.FileRow;
import com.android.providers.media.playlist.Playlist;
-import com.android.providers.media.util.BackgroundThread;
import com.android.providers.media.util.DatabaseUtils;
import com.android.providers.media.util.FileUtils;
import com.android.providers.media.util.ForegroundThread;
@@ -78,11 +83,16 @@
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
+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;
import java.util.UUID;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function;
import java.util.function.UnaryOperator;
@@ -94,6 +104,54 @@
* on demand, create and upgrade the schema, etc.
*/
public class DatabaseHelper extends SQLiteOpenHelper implements AutoCloseable {
+ @VisibleForTesting
+ static final String TEST_RECOMPUTE_DB = "test_recompute";
+ @VisibleForTesting
+ static final String TEST_UPGRADE_DB = "test_upgrade";
+ @VisibleForTesting
+ static final String TEST_DOWNGRADE_DB = "test_downgrade";
+ @VisibleForTesting
+ public static final String TEST_CLEAN_DB = "test_clean";
+
+ /**
+ * Key name of xattr used to set next row id for internal DB.
+ */
+ private static final String INTERNAL_DB_NEXT_ROW_ID_XATTR_KEY = "user.intdbnextrowid".concat(
+ String.valueOf(UserHandle.myUserId()));
+
+ /**
+ * Key name of xattr used to set next row id for external DB.
+ */
+ private static final String EXTERNAL_DB_NEXT_ROW_ID_XATTR_KEY = "user.extdbnextrowid".concat(
+ String.valueOf(UserHandle.myUserId()));
+
+ /**
+ * Key name of xattr used to set session id for internal DB.
+ */
+ private static final String INTERNAL_DB_SESSION_ID_XATTR_KEY = "user.intdbsessionid".concat(
+ String.valueOf(UserHandle.myUserId()));
+
+ /**
+ * Key name of xattr used to set session id for external DB.
+ */
+ private static final String EXTERNAL_DB_SESSION_ID_XATTR_KEY = "user.extdbsessionid".concat(
+ String.valueOf(UserHandle.myUserId()));
+
+ /** Indicates a billion value used when next row id is not present in respective xattr. */
+ private static final Long NEXT_ROW_ID_DEFAULT_BILLION_VALUE = Double.valueOf(
+ Math.pow(10, 9)).longValue();
+
+ private static final Long INVALID_ROW_ID = -1L;
+
+ /**
+ * Path used for setting next row id and database session id for each user profile. Storing here
+ * because media provider does not have required permission on path /data/media/<user-id> for
+ * work profiles.
+ * For devices with adoptable storage support, opting for adoptable storage will not delete
+ * /data/media/0 directory.
+ */
+ public static final String DATA_MEDIA_XATTR_DIRECTORY_PATH = "/data/media/0";
+
static final String INTERNAL_DATABASE_NAME = "internal.db";
static final String EXTERNAL_DATABASE_NAME = "external.db";
@@ -110,10 +168,10 @@
final String mName;
final int mVersion;
final String mVolumeName;
- final boolean mInternal; // True if this is the internal database
final boolean mEarlyUpgrade;
final boolean mLegacyProvider;
final @Nullable Class<? extends Annotation> mColumnAnnotation;
+ final @Nullable Class<? extends Annotation> mExportedSinceAnnotation;
final @Nullable OnSchemaChangeListener mSchemaListener;
final @Nullable OnFilesChangeListener mFilesListener;
final @Nullable OnLegacyMigrationListener mMigrationListener;
@@ -122,8 +180,17 @@
private final String mMigrationFileName;
long mScanStartTime;
long mScanStopTime;
+ private boolean mEnableNextRowIdRecovery;
/**
+ * Unfortunately we can have multiple instances of DatabaseHelper, causing
+ * onUpgrade() to be called multiple times if those instances happen to run in
+ * parallel. To prevent that, keep track of which databases we've already upgraded.
+ *
+ */
+ static final Set<String> sDatabaseUpgraded = new HashSet<>();
+ static final Object sLock = new Object();
+ /**
* Lock used to guard against deadlocks in SQLite; the write lock is used to
* guard any schema changes, and the read lock is used for all other
* database operations.
@@ -139,66 +206,85 @@
private static Object sMigrationLockInternal = new Object();
private static Object sMigrationLockExternal = new Object();
+ /**
+ * Object used to synchronise sequence of next row id in database.
+ */
+ private static final Object sRecoveryLock = new Object();
+
+ /** Stores cached value of next row id of the database which optimises new id inserts. */
+ private AtomicLong mNextRowIdBackup = new AtomicLong(INVALID_ROW_ID);
+
public interface OnSchemaChangeListener {
- public void onSchemaChange(@NonNull String volumeName, int versionFrom, int versionTo,
+ void onSchemaChange(@NonNull String volumeName, int versionFrom, int versionTo,
long itemCount, long durationMillis, String databaseUuid);
}
public interface OnFilesChangeListener {
- public void onInsert(@NonNull DatabaseHelper helper, @NonNull String volumeName, long id,
- int mediaType, boolean isDownload);
- public void onUpdate(@NonNull DatabaseHelper helper, @NonNull String volumeName,
- long oldId, int oldMediaType, boolean oldIsDownload,
- long newId, int newMediaType, boolean newIsDownload,
- String oldOwnerPackage, String newOwnerPackage, String oldPath);
- public void onDelete(@NonNull DatabaseHelper helper, @NonNull String volumeName, long id,
- int mediaType, boolean isDownload, String ownerPackage, String path);
+ void onInsert(@NonNull DatabaseHelper helper, @NonNull FileRow insertedRow);
+
+ void onUpdate(@NonNull DatabaseHelper helper, @NonNull FileRow oldRow,
+ @NonNull FileRow newRow);
+
+ /** Method invoked on database row delete. */
+ void onDelete(@NonNull DatabaseHelper helper, @NonNull FileRow deletedRow);
}
public interface OnLegacyMigrationListener {
- public void onStarted(ContentProviderClient client, String volumeName);
- public void onProgress(ContentProviderClient client, String volumeName,
+ void onStarted(ContentProviderClient client, String volumeName);
+
+ void onProgress(ContentProviderClient client, String volumeName,
long progress, long total);
- public void onFinished(ContentProviderClient client, String volumeName);
+
+ void onFinished(ContentProviderClient client, String volumeName);
}
public DatabaseHelper(Context context, String name,
- boolean internal, boolean earlyUpgrade, boolean legacyProvider,
+ boolean earlyUpgrade, boolean legacyProvider,
@Nullable Class<? extends Annotation> columnAnnotation,
+ @Nullable Class<? extends Annotation> exportedSinceAnnotation,
@Nullable OnSchemaChangeListener schemaListener,
@Nullable OnFilesChangeListener filesListener,
@NonNull OnLegacyMigrationListener migrationListener,
- @Nullable UnaryOperator<String> idGenerator) {
- this(context, name, getDatabaseVersion(context), internal, earlyUpgrade, legacyProvider,
- columnAnnotation, schemaListener, filesListener, migrationListener, idGenerator);
+ @Nullable UnaryOperator<String> idGenerator, boolean enableNextRowIdRecovery) {
+ this(context, name, getDatabaseVersion(context), earlyUpgrade, legacyProvider,
+ columnAnnotation, exportedSinceAnnotation, schemaListener, filesListener,
+ migrationListener, idGenerator, enableNextRowIdRecovery);
}
public DatabaseHelper(Context context, String name, int version,
- boolean internal, boolean earlyUpgrade, boolean legacyProvider,
+ boolean earlyUpgrade, boolean legacyProvider,
@Nullable Class<? extends Annotation> columnAnnotation,
+ @Nullable Class<? extends Annotation> exportedSinceAnnotation,
@Nullable OnSchemaChangeListener schemaListener,
@Nullable OnFilesChangeListener filesListener,
@NonNull OnLegacyMigrationListener migrationListener,
- @Nullable UnaryOperator<String> idGenerator) {
+ @Nullable UnaryOperator<String> idGenerator, boolean enableNextRowIdRecovery) {
super(context, name, null, version);
mContext = context;
mName = name;
mVersion = version;
- mVolumeName = internal ? MediaStore.VOLUME_INTERNAL : MediaStore.VOLUME_EXTERNAL;
- mInternal = internal;
+ if (isInternal()) {
+ mVolumeName = MediaStore.VOLUME_INTERNAL;
+ } else if (isExternal()) {
+ mVolumeName = MediaStore.VOLUME_EXTERNAL;
+ } else {
+ throw new IllegalStateException("Db must be internal/external");
+ }
mEarlyUpgrade = earlyUpgrade;
mLegacyProvider = legacyProvider;
mColumnAnnotation = columnAnnotation;
+ mExportedSinceAnnotation = exportedSinceAnnotation;
mSchemaListener = schemaListener;
mFilesListener = filesListener;
mMigrationListener = migrationListener;
mIdGenerator = idGenerator;
mMigrationFileName = "." + mVolumeName;
+ this.mEnableNextRowIdRecovery = enableNextRowIdRecovery;
// Configure default filters until we hear differently
- if (mInternal) {
+ if (isInternal()) {
mFilterVolumeNames.add(MediaStore.VOLUME_INTERNAL);
- } else {
+ } else if (isExternal()) {
mFilterVolumeNames.add(MediaStore.VOLUME_EXTERNAL_PRIMARY);
}
@@ -229,7 +315,7 @@
mSchemaLock.writeLock().lock();
try {
db.beginTransaction();
- createLatestViews(db, mInternal);
+ createLatestViews(db);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
@@ -251,6 +337,11 @@
@VisibleForTesting
SQLiteDatabase getWritableDatabaseForTest() {
+ // Tests rely on creating multiple instances of DatabaseHelper to test upgrade
+ // scenarios; so clear this state before returning databases to test.
+ synchronized (sLock) {
+ sDatabaseUpgraded.clear();
+ }
return super.getWritableDatabase();
}
@@ -260,16 +351,22 @@
db.setCustomScalarFunction("_INSERT", (arg) -> {
if (arg != null && mFilesListener != null
&& !mSchemaLock.isWriteLockedByCurrentThread()) {
- final String[] split = arg.split(":", 4);
+ final String[] split = arg.split(":", 5);
final String volumeName = split[0];
final long id = Long.parseLong(split[1]);
final int mediaType = Integer.parseInt(split[2]);
final boolean isDownload = Integer.parseInt(split[3]) != 0;
+ final boolean isPending = Integer.parseInt(split[4]) != 0;
+ FileRow insertedRow = FileRow.newBuilder(id)
+ .setVolumeName(volumeName)
+ .setMediaType(mediaType)
+ .setIsDownload(isDownload)
+ .setIsPending(isPending)
+ .build();
Trace.beginSection("_INSERT");
try {
- mFilesListener.onInsert(DatabaseHelper.this, volumeName, id,
- mediaType, isDownload);
+ mFilesListener.onInsert(DatabaseHelper.this, insertedRow);
} finally {
Trace.endSection();
}
@@ -279,7 +376,7 @@
db.setCustomScalarFunction("_UPDATE", (arg) -> {
if (arg != null && mFilesListener != null
&& !mSchemaLock.isWriteLockedByCurrentThread()) {
- final String[] split = arg.split(":", 10);
+ final String[] split = arg.split(":", 18);
final String volumeName = split[0];
final long oldId = Long.parseLong(split[1]);
final int oldMediaType = Integer.parseInt(split[2]);
@@ -287,15 +384,43 @@
final long newId = Long.parseLong(split[4]);
final int newMediaType = Integer.parseInt(split[5]);
final boolean newIsDownload = Integer.parseInt(split[6]) != 0;
- final String oldOwnerPackage = split[7];
- final String newOwnerPackage = split[8];
- final String oldPath = split[9];
+ final boolean oldIsTrashed = Integer.parseInt(split[7]) != 0;
+ final boolean newIsTrashed = Integer.parseInt(split[8]) != 0;
+ final boolean oldIsPending = Integer.parseInt(split[9]) != 0;
+ final boolean newIsPending = Integer.parseInt(split[10]) != 0;
+ final boolean oldIsFavorite = Integer.parseInt(split[11]) != 0;
+ final boolean newIsFavorite = Integer.parseInt(split[12]) != 0;
+ final int oldSpecialFormat = Integer.parseInt(split[13]);
+ final int newSpecialFormat = Integer.parseInt(split[14]);
+ final String oldOwnerPackage = split[15];
+ final String newOwnerPackage = split[16];
+ final String oldPath = split[17];
+
+ FileRow oldRow = FileRow.newBuilder(oldId)
+ .setVolumeName(volumeName)
+ .setMediaType(oldMediaType)
+ .setIsDownload(oldIsDownload)
+ .setIsTrashed(oldIsTrashed)
+ .setIsPending(oldIsPending)
+ .setIsFavorite(oldIsFavorite)
+ .setSpecialFormat(oldSpecialFormat)
+ .setOwnerPackageName(oldOwnerPackage)
+ .setPath(oldPath)
+ .build();
+ FileRow newRow = FileRow.newBuilder(newId)
+ .setVolumeName(volumeName)
+ .setMediaType(newMediaType)
+ .setIsDownload(newIsDownload)
+ .setIsTrashed(newIsTrashed)
+ .setIsPending(newIsPending)
+ .setIsFavorite(newIsFavorite)
+ .setSpecialFormat(newSpecialFormat)
+ .setOwnerPackageName(newOwnerPackage)
+ .build();
Trace.beginSection("_UPDATE");
try {
- mFilesListener.onUpdate(DatabaseHelper.this, volumeName, oldId,
- oldMediaType, oldIsDownload, newId, newMediaType, newIsDownload,
- oldOwnerPackage, newOwnerPackage, oldPath);
+ mFilesListener.onUpdate(DatabaseHelper.this, oldRow, newRow);
} finally {
Trace.endSection();
}
@@ -313,10 +438,16 @@
final String ownerPackage = split[4];
final String path = split[5];
+ FileRow deletedRow = FileRow.newBuilder(id)
+ .setVolumeName(volumeName)
+ .setMediaType(mediaType)
+ .setIsDownload(isDownload)
+ .setOwnerPackageName(ownerPackage)
+ .setPath(path)
+ .build();
Trace.beginSection("_DELETE");
try {
- mFilesListener.onDelete(DatabaseHelper.this, volumeName, id,
- mediaType, isDownload, ownerPackage, path);
+ mFilesListener.onDelete(DatabaseHelper.this, deletedRow);
} finally {
Trace.endSection();
}
@@ -352,6 +483,15 @@
Log.v(TAG, "onUpgrade() for " + mName + " from " + oldV + " to " + newV);
mSchemaLock.writeLock().lock();
try {
+ synchronized (sLock) {
+ if (sDatabaseUpgraded.contains(mName)) {
+ Log.v(TAG, "Skipping onUpgrade() for " + mName +
+ " because it was already upgraded.");
+ return;
+ } else {
+ sDatabaseUpgraded.add(mName);
+ }
+ }
updateDatabase(db, oldV, newV);
} finally {
mSchemaLock.writeLock().unlock();
@@ -360,23 +500,106 @@
@Override
public void onDowngrade(final SQLiteDatabase db, final int oldV, final int newV) {
- Log.v(TAG, "onDowngrade() for " + mName + " from " + oldV + " to " + newV);
- mSchemaLock.writeLock().lock();
- try {
- downgradeDatabase(db, oldV, newV);
- } finally {
- mSchemaLock.writeLock().unlock();
+ 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(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(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(Locale.ROOT, "Database file:%s deleted.",
+ file.getAbsolutePath()));
+ }
}
}
+
@Override
public void onOpen(final SQLiteDatabase db) {
Log.v(TAG, "onOpen() for " + mName);
-
- tryMigrateFromLegacy(db, mInternal ? sMigrationLockInternal : sMigrationLockExternal);
+ // Recovering before migration from legacy because recovery process will clear up data to
+ // read from xattrs once ids are persisted in xattrs.
+ tryRecoverRowIdSequence(db);
+ tryMigrateFromLegacy(db);
}
- private void tryMigrateFromLegacy(SQLiteDatabase db, Object migrationLock) {
+ private void tryRecoverRowIdSequence(SQLiteDatabase db) {
+ if (!isNextRowIdBackupEnabled()) {
+ Log.d(TAG, "Skipping row id recovery as backup is not enabled.");
+ return;
+ }
+
+ synchronized (sRecoveryLock) {
+ boolean isLastUsedDatabaseSession = isLastUsedDatabaseSession(db);
+ Optional<Long> nextRowIdFromXattrOptional = getNextRowIdFromXattr();
+ if (isLastUsedDatabaseSession && nextRowIdFromXattrOptional.isPresent()) {
+ 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(Locale.ROOT,
+ "%s database inconsistent: isLastUsedDatabaseSession:%b, "
+ + "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);
+ updateSessionIdInDatabaseAndExternalStorage(db);
+ }
+ }
+
+ @GuardedBy("sRecoveryLock")
+ private boolean isLastUsedDatabaseSession(SQLiteDatabase db) {
+ Optional<String> lastUsedSessionIdFromDatabasePathXattr = getXattr(db.getPath(),
+ getSessionIdXattrKeyForDatabase());
+ Optional<String> lastUsedSessionIdFromExternalStoragePathXattr = getXattr(
+ DATA_MEDIA_XATTR_DIRECTORY_PATH, getSessionIdXattrKeyForDatabase());
+
+ return lastUsedSessionIdFromDatabasePathXattr.isPresent()
+ && lastUsedSessionIdFromExternalStoragePathXattr.isPresent()
+ && lastUsedSessionIdFromDatabasePathXattr.get().equals(
+ lastUsedSessionIdFromExternalStoragePathXattr.get());
+ }
+
+ @GuardedBy("sRecoveryLock")
+ private void updateSessionIdInDatabaseAndExternalStorage(SQLiteDatabase db) {
+ final String uuid = UUID.randomUUID().toString();
+ boolean setOnDatabase = setXattr(db.getPath(), getSessionIdXattrKeyForDatabase(), uuid);
+ boolean setOnExternalStorage = setXattr(DATA_MEDIA_XATTR_DIRECTORY_PATH,
+ getSessionIdXattrKeyForDatabase(), uuid);
+ if (setOnDatabase && setOnExternalStorage) {
+ Log.i(TAG, String.format(Locale.ROOT, "SessionId set to %s on paths %s and %s.", uuid,
+ db.getPath(), DATA_MEDIA_XATTR_DIRECTORY_PATH));
+ }
+ }
+
+ private void tryMigrateFromLegacy(SQLiteDatabase db) {
+ final Object migrationLock;
+ if (isInternal()) {
+ migrationLock = sMigrationLockInternal;
+ } else if (isExternal()) {
+ migrationLock = sMigrationLockExternal;
+ } else {
+ throw new IllegalStateException("Db migration only supported for internal/external db");
+ }
+
final File migration = new File(mContext.getFilesDir(), mMigrationFileName);
// Another thread entering migration block will be blocked until the
// migration is complete from current thread.
@@ -391,7 +614,7 @@
// Temporarily drop indexes to improve migration performance
makePristineIndexes(db);
migrateFromLegacy(db);
- createLatestIndexes(db, mInternal);
+ createLatestIndexes(db);
} finally {
mSchemaLock.writeLock().unlock();
// Clear flag, since we should only attempt once
@@ -420,10 +643,13 @@
map = new ArrayMap<>();
try {
for (Field field : clazz.getFields()) {
- if (Objects.equals(field.getName(), "_ID")
- || field.isAnnotationPresent(mColumnAnnotation)) {
- final String column = (String) field.get(null);
- map.put(column, column);
+ if (Objects.equals(field.getName(), "_ID") || (mColumnAnnotation != null
+ && field.isAnnotationPresent(mColumnAnnotation))) {
+ boolean shouldIgnoreByOsVersion = shouldBeIgnoredByOsVersion(field);
+ if (!shouldIgnoreByOsVersion) {
+ final String column = (String) field.get(null);
+ map.put(column, column);
+ }
}
}
} catch (ReflectiveOperationException e) {
@@ -821,18 +1047,20 @@
+ "_transcode_status INTEGER DEFAULT 0, _video_codec_type TEXT DEFAULT NULL,"
+ "_modifier INTEGER DEFAULT 0, is_recording INTEGER DEFAULT 0,"
+ "redacted_uri_id TEXT DEFAULT NULL, _user_id INTEGER DEFAULT "
- + UserHandle.myUserId() + ")");
-
+ + UserHandle.myUserId() + ", _special_format INTEGER DEFAULT NULL)");
db.execSQL("CREATE TABLE log (time DATETIME, message TEXT)");
- if (!mInternal) {
+ db.execSQL("CREATE TABLE deleted_media (_id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + "old_id INTEGER UNIQUE, generation_modified INTEGER NOT NULL)");
+
+ if (isExternal()) {
db.execSQL("CREATE TABLE audio_playlists_map (_id INTEGER PRIMARY KEY,"
+ "audio_id INTEGER NOT NULL,playlist_id INTEGER NOT NULL,"
+ "play_order INTEGER NOT NULL)");
}
- createLatestViews(db, mInternal);
- createLatestTriggers(db, mInternal);
- createLatestIndexes(db, mInternal);
+ createLatestViews(db);
+ createLatestTriggers(db);
+ createLatestIndexes(db);
// Since this code is used by both the legacy and modern providers, we
// only want to migrate when we're running as the modern provider
@@ -903,7 +1131,7 @@
// Handle playlist files which may need special handling if
// there are no "real" playlist files.
final int mediaType = c.getInt(c.getColumnIndex(FileColumns.MEDIA_TYPE));
- if (!mInternal && volumePath != null &&
+ if (isExternal() && volumePath != null &&
mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) {
File playlistFile = new File(data);
@@ -1168,7 +1396,7 @@
c.close();
}
- private void createLatestViews(SQLiteDatabase db, boolean internal) {
+ private void createLatestViews(SQLiteDatabase db) {
makePristineViews(db);
if (mColumnAnnotation == null) {
@@ -1181,7 +1409,7 @@
filterVolumeNames = bindList(mFilterVolumeNames.toArray());
}
- if (!internal) {
+ if (isExternal()) {
db.execSQL("CREATE VIEW audio_playlists AS SELECT "
+ getColumnsForCollection(Audio.Playlists.class)
+ " FROM files WHERE media_type=4");
@@ -1290,14 +1518,20 @@
c.close();
}
- private static void createLatestTriggers(SQLiteDatabase db, boolean internal) {
+ private static void createLatestTriggers(SQLiteDatabase db) {
makePristineTriggers(db);
final String insertArg =
- "new.volume_name||':'||new._id||':'||new.media_type||':'||new.is_download";
+ "new.volume_name||':'||new._id||':'||new.media_type||':'||new.is_download"
+ + "||':'||new.is_pending";
final String updateArg =
"old.volume_name||':'||old._id||':'||old.media_type||':'||old.is_download"
+ "||':'||new._id||':'||new.media_type||':'||new.is_download"
+ + "||':'||old.is_trashed||':'||new.is_trashed"
+ + "||':'||old.is_pending||':'||new.is_pending"
+ + "||':'||old.is_favorite||':'||new.is_favorite"
+ + "||':'||ifnull(old._special_format,0)"
+ + "||':'||ifnull(new._special_format,0)"
+ "||':'||ifnull(old.owner_package_name,'null')"
+ "||':'||ifnull(new.owner_package_name,'null')||':'||old._data";
final String deleteArg =
@@ -1323,7 +1557,7 @@
c.close();
}
- private static void createLatestIndexes(SQLiteDatabase db, boolean internal) {
+ private static void createLatestIndexes(SQLiteDatabase db) {
makePristineIndexes(db);
db.execSQL("CREATE INDEX image_id_index on thumbnails(image_id)");
@@ -1361,7 +1595,7 @@
+ " WHERE (is_alarm IS 1) OR (is_ringtone IS 1) OR (is_notification IS 1)");
}
- private static void updateAddOwnerPackageName(SQLiteDatabase db, boolean internal) {
+ private static void updateAddOwnerPackageName(SQLiteDatabase db) {
db.execSQL("ALTER TABLE files ADD COLUMN owner_package_name TEXT DEFAULT NULL");
// Derive new column value based on well-known paths
@@ -1394,22 +1628,22 @@
db.execSQL("ALTER TABLE files ADD COLUMN color_range INTEGER;");
}
- private static void updateAddHashAndPending(SQLiteDatabase db, boolean internal) {
+ private static void updateAddHashAndPending(SQLiteDatabase db) {
db.execSQL("ALTER TABLE files ADD COLUMN _hash BLOB DEFAULT NULL;");
db.execSQL("ALTER TABLE files ADD COLUMN is_pending INTEGER DEFAULT 0;");
}
- private static void updateAddDownloadInfo(SQLiteDatabase db, boolean internal) {
+ private static void updateAddDownloadInfo(SQLiteDatabase db) {
db.execSQL("ALTER TABLE files ADD COLUMN is_download INTEGER DEFAULT 0;");
db.execSQL("ALTER TABLE files ADD COLUMN download_uri TEXT DEFAULT NULL;");
db.execSQL("ALTER TABLE files ADD COLUMN referer_uri TEXT DEFAULT NULL;");
}
- private static void updateAddAudiobook(SQLiteDatabase db, boolean internal) {
+ private static void updateAddAudiobook(SQLiteDatabase db) {
db.execSQL("ALTER TABLE files ADD COLUMN is_audiobook INTEGER DEFAULT 0;");
}
- private static void updateAddRecording(SQLiteDatabase db, boolean internal) {
+ private static void updateAddRecording(SQLiteDatabase db) {
db.execSQL("ALTER TABLE files ADD COLUMN is_recording INTEGER DEFAULT 0;");
// We add the column is_recording, rescan all music files
db.execSQL("UPDATE files SET date_modified=0 WHERE is_music=1;");
@@ -1419,69 +1653,76 @@
db.execSQL("ALTER TABLE files ADD COLUMN redacted_uri_id TEXT DEFAULT NULL;");
}
- private static void updateClearLocation(SQLiteDatabase db, boolean internal) {
+ private static void updateClearLocation(SQLiteDatabase db) {
db.execSQL("UPDATE files SET latitude=NULL, longitude=NULL;");
}
- private static void updateSetIsDownload(SQLiteDatabase db, boolean internal) {
+ private static void updateSetIsDownload(SQLiteDatabase db) {
db.execSQL("UPDATE files SET is_download=1 WHERE _data REGEXP '"
+ FileUtils.PATTERN_DOWNLOADS_FILE + "'");
}
- private static void updateAddExpiresAndTrashed(SQLiteDatabase db, boolean internal) {
+ private static void updateAddExpiresAndTrashed(SQLiteDatabase db) {
db.execSQL("ALTER TABLE files ADD COLUMN date_expires INTEGER DEFAULT NULL;");
db.execSQL("ALTER TABLE files ADD COLUMN is_trashed INTEGER DEFAULT 0;");
}
- private static void updateAddGroupId(SQLiteDatabase db, boolean internal) {
+ private static void updateAddGroupId(SQLiteDatabase db) {
db.execSQL("ALTER TABLE files ADD COLUMN group_id INTEGER DEFAULT NULL;");
}
- private static void updateAddDirectories(SQLiteDatabase db, boolean internal) {
+ private static void updateAddDirectories(SQLiteDatabase db) {
db.execSQL("ALTER TABLE files ADD COLUMN primary_directory TEXT DEFAULT NULL;");
db.execSQL("ALTER TABLE files ADD COLUMN secondary_directory TEXT DEFAULT NULL;");
}
- private static void updateAddXmpMm(SQLiteDatabase db, boolean internal) {
+ private static void updateAddXmpMm(SQLiteDatabase db) {
db.execSQL("ALTER TABLE files ADD COLUMN document_id TEXT DEFAULT NULL;");
db.execSQL("ALTER TABLE files ADD COLUMN instance_id TEXT DEFAULT NULL;");
db.execSQL("ALTER TABLE files ADD COLUMN original_document_id TEXT DEFAULT NULL;");
}
- private static void updateAddPath(SQLiteDatabase db, boolean internal) {
+ private static void updateAddPath(SQLiteDatabase db) {
db.execSQL("ALTER TABLE files ADD COLUMN relative_path TEXT DEFAULT NULL;");
}
- private static void updateAddVolumeName(SQLiteDatabase db, boolean internal) {
+ private static void updateAddVolumeName(SQLiteDatabase db) {
db.execSQL("ALTER TABLE files ADD COLUMN volume_name TEXT DEFAULT NULL;");
}
- private static void updateDirsMimeType(SQLiteDatabase db, boolean internal) {
+ private static void updateDirsMimeType(SQLiteDatabase db) {
db.execSQL("UPDATE files SET mime_type=NULL WHERE format="
+ MtpConstants.FORMAT_ASSOCIATION);
}
- private static void updateRelativePath(SQLiteDatabase db, boolean internal) {
+ private static void updateRelativePath(SQLiteDatabase db) {
db.execSQL("UPDATE files"
+ " SET " + MediaColumns.RELATIVE_PATH + "=" + MediaColumns.RELATIVE_PATH + "||'/'"
+ " WHERE " + MediaColumns.RELATIVE_PATH + " IS NOT NULL"
+ " AND " + MediaColumns.RELATIVE_PATH + " NOT LIKE '%/';");
}
- private static void updateAddTranscodeSatus(SQLiteDatabase db, boolean internal) {
+ private static void updateAddTranscodeSatus(SQLiteDatabase db) {
db.execSQL("ALTER TABLE files ADD COLUMN _transcode_status INTEGER DEFAULT 0;");
}
+ private static void updateAddSpecialFormat(SQLiteDatabase db) {
+ db.execSQL("ALTER TABLE files ADD COLUMN _special_format INTEGER DEFAULT NULL;");
+ }
- private static void updateAddVideoCodecType(SQLiteDatabase db, boolean internal) {
+ private static void updateSpecialFormatToNotDetected(SQLiteDatabase db) {
+ db.execSQL("UPDATE files SET _special_format=NULL WHERE _special_format=0");
+ }
+
+ private static void updateAddVideoCodecType(SQLiteDatabase db) {
db.execSQL("ALTER TABLE files ADD COLUMN _video_codec_type TEXT DEFAULT NULL;");
}
- private static void updateClearDirectories(SQLiteDatabase db, boolean internal) {
+ private static void updateClearDirectories(SQLiteDatabase db) {
db.execSQL("UPDATE files SET primary_directory=NULL, secondary_directory=NULL;");
}
- private static void updateRestructureAudio(SQLiteDatabase db, boolean internal) {
+ private static void updateRestructureAudio(SQLiteDatabase db) {
db.execSQL("ALTER TABLE files ADD COLUMN artist_key TEXT DEFAULT NULL;");
db.execSQL("ALTER TABLE files ADD COLUMN album_key TEXT DEFAULT NULL;");
db.execSQL("ALTER TABLE files ADD COLUMN genre TEXT DEFAULT NULL;");
@@ -1505,7 +1746,7 @@
db.execSQL("UPDATE files SET date_modified=0 WHERE media_type=2;");
}
- private static void updateAddMetadata(SQLiteDatabase db, boolean internal) {
+ private static void updateAddMetadata(SQLiteDatabase db) {
db.execSQL("ALTER TABLE files ADD COLUMN author TEXT DEFAULT NULL;");
db.execSQL("ALTER TABLE files ADD COLUMN bitrate INTEGER DEFAULT NULL;");
db.execSQL("ALTER TABLE files ADD COLUMN capture_framerate REAL DEFAULT NULL;");
@@ -1520,11 +1761,11 @@
db.execSQL("ALTER TABLE files ADD COLUMN iso INTEGER DEFAULT NULL;");
}
- private static void updateAddSceneCaptureType(SQLiteDatabase db, boolean internal) {
+ private static void updateAddSceneCaptureType(SQLiteDatabase db) {
db.execSQL("ALTER TABLE files ADD COLUMN scene_capture_type INTEGER DEFAULT NULL;");
}
- private static void updateMigrateLogs(SQLiteDatabase db, boolean internal) {
+ private static void updateMigrateLogs(SQLiteDatabase db) {
// Migrate any existing logs to new system
try (Cursor c = db.query("log", new String[] { "time", "message" },
null, null, null, null, null)) {
@@ -1537,37 +1778,43 @@
db.execSQL("DELETE FROM log;");
}
- private static void updateAddLocalMetadata(SQLiteDatabase db, boolean internal) {
+ private static void updateAddLocalMetadata(SQLiteDatabase db) {
db.execSQL("CREATE TABLE local_metadata (generation INTEGER DEFAULT 0)");
db.execSQL("INSERT INTO local_metadata VALUES (0)");
}
- private static void updateAddGeneration(SQLiteDatabase db, boolean internal) {
+ private static void updateAddGeneration(SQLiteDatabase db) {
db.execSQL("ALTER TABLE files ADD COLUMN generation_added INTEGER DEFAULT 0;");
db.execSQL("ALTER TABLE files ADD COLUMN generation_modified INTEGER DEFAULT 0;");
}
- private static void updateAddXmp(SQLiteDatabase db, boolean internal) {
+ private static void updateAddXmp(SQLiteDatabase db) {
db.execSQL("ALTER TABLE files ADD COLUMN xmp BLOB DEFAULT NULL;");
}
- private static void updateAudioAlbumId(SQLiteDatabase db, boolean internal) {
+ private static void updateAudioAlbumId(SQLiteDatabase db) {
// We change the logic for generating album id, rescan all audio files
db.execSQL("UPDATE files SET date_modified=0 WHERE media_type=2;");
}
- private static void updateAddModifier(SQLiteDatabase db, boolean internal) {
+ private static void updateAddModifier(SQLiteDatabase db) {
db.execSQL("ALTER TABLE files ADD COLUMN _modifier INTEGER DEFAULT 0;");
// For existing files, set default value as _MODIFIER_MEDIA_SCAN
db.execSQL("UPDATE files SET _modifier=3;");
}
+ private static void updateAddDeletedMediaTable(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE deleted_media (_id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + "old_id INTEGER UNIQUE, generation_modified INTEGER NOT NULL)");
+ }
+
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()));
}
- private static void recomputeDataValues(SQLiteDatabase db, boolean internal) {
+ private static void recomputeDataValues(SQLiteDatabase db) {
try (Cursor c = db.query("files", new String[] { FileColumns._ID, FileColumns.DATA },
null, null, null, null, null, null)) {
Log.d(TAG, "Recomputing " + c.getCount() + " data values");
@@ -1627,10 +1874,11 @@
static final int VERSION_P = 900;
static final int VERSION_Q = 1023;
static final int VERSION_R = 1115;
- // Leave some gaps in database version tagging to allow R schema changes
- // to go independent of S schema changes.
static final int VERSION_S = 1209;
- static final int VERSION_LATEST = VERSION_S;
+ // Leave some gaps in database version tagging to allow S schema changes
+ // to go independent of T schema changes.
+ static final int VERSION_T = 1307;
+ public static final int VERSION_LATEST = VERSION_T;
/**
* This method takes care of updating all the tables in the database to the
@@ -1641,7 +1889,6 @@
*/
private void updateDatabase(SQLiteDatabase db, int fromVersion, int toVersion) {
final long startTime = SystemClock.elapsedRealtime();
- final boolean internal = mInternal;
if (fromVersion < 700) {
// Anything older than KK is recreated from scratch
@@ -1655,25 +1902,25 @@
updateAddTitleResource(db);
}
if (fromVersion < 1000) {
- updateAddOwnerPackageName(db, internal);
+ updateAddOwnerPackageName(db);
}
if (fromVersion < 1003) {
updateAddColorSpaces(db);
}
if (fromVersion < 1004) {
- updateAddHashAndPending(db, internal);
+ updateAddHashAndPending(db);
}
if (fromVersion < 1005) {
- updateAddDownloadInfo(db, internal);
+ updateAddDownloadInfo(db);
}
if (fromVersion < 1006) {
- updateAddAudiobook(db, internal);
+ updateAddAudiobook(db);
}
if (fromVersion < 1007) {
- updateClearLocation(db, internal);
+ updateClearLocation(db);
}
if (fromVersion < 1008) {
- updateSetIsDownload(db, internal);
+ updateSetIsDownload(db);
}
if (fromVersion < 1009) {
// This database version added "secondary_bucket_id", but that
@@ -1681,18 +1928,18 @@
// update step is no longer needed.
}
if (fromVersion < 1010) {
- updateAddExpiresAndTrashed(db, internal);
+ updateAddExpiresAndTrashed(db);
}
if (fromVersion < 1012) {
recomputeDataValues = true;
}
if (fromVersion < 1013) {
- updateAddGroupId(db, internal);
- updateAddDirectories(db, internal);
+ updateAddGroupId(db);
+ updateAddDirectories(db);
recomputeDataValues = true;
}
if (fromVersion < 1014) {
- updateAddXmpMm(db, internal);
+ updateAddXmpMm(db);
}
if (fromVersion < 1015) {
// Empty version bump to ensure views are recreated
@@ -1701,43 +1948,43 @@
// Empty version bump to ensure views are recreated
}
if (fromVersion < 1017) {
- updateSetIsDownload(db, internal);
+ updateSetIsDownload(db);
recomputeDataValues = true;
}
if (fromVersion < 1018) {
- updateAddPath(db, internal);
+ updateAddPath(db);
recomputeDataValues = true;
}
if (fromVersion < 1019) {
// Only trigger during "external", so that it runs only once.
- if (!internal) {
+ if (isExternal()) {
deleteLegacyThumbnailData();
}
}
if (fromVersion < 1020) {
- updateAddVolumeName(db, internal);
+ updateAddVolumeName(db);
recomputeDataValues = true;
}
if (fromVersion < 1021) {
// Empty version bump to ensure views are recreated
}
if (fromVersion < 1022) {
- updateDirsMimeType(db, internal);
+ updateDirsMimeType(db);
}
if (fromVersion < 1023) {
- updateRelativePath(db, internal);
+ updateRelativePath(db);
}
if (fromVersion < 1100) {
// Empty version bump to ensure triggers are recreated
}
if (fromVersion < 1101) {
- updateClearDirectories(db, internal);
+ updateClearDirectories(db);
}
if (fromVersion < 1102) {
- updateRestructureAudio(db, internal);
+ updateRestructureAudio(db);
}
if (fromVersion < 1103) {
- updateAddMetadata(db, internal);
+ updateAddMetadata(db);
}
if (fromVersion < 1104) {
// Empty version bump to ensure views are recreated
@@ -1746,16 +1993,16 @@
recomputeDataValues = true;
}
if (fromVersion < 1106) {
- updateMigrateLogs(db, internal);
+ updateMigrateLogs(db);
}
if (fromVersion < 1107) {
- updateAddSceneCaptureType(db, internal);
+ updateAddSceneCaptureType(db);
}
if (fromVersion < 1108) {
- updateAddLocalMetadata(db, internal);
+ updateAddLocalMetadata(db);
}
if (fromVersion < 1109) {
- updateAddGeneration(db, internal);
+ updateAddGeneration(db);
}
if (fromVersion < 1110) {
// Empty version bump to ensure triggers are recreated
@@ -1764,7 +2011,7 @@
recomputeMediaTypeValues(db);
}
if (fromVersion < 1112) {
- updateAddXmp(db, internal);
+ updateAddXmp(db);
}
if (fromVersion < 1113) {
// Empty version bump to ensure triggers are recreated
@@ -1773,16 +2020,16 @@
// Empty version bump to ensure triggers are recreated
}
if (fromVersion < 1115) {
- updateAudioAlbumId(db, internal);
+ updateAudioAlbumId(db);
}
if (fromVersion < 1200) {
- updateAddTranscodeSatus(db, internal);
+ updateAddTranscodeSatus(db);
}
if (fromVersion < 1201) {
- updateAddVideoCodecType(db, internal);
+ updateAddVideoCodecType(db);
}
if (fromVersion < 1202) {
- updateAddModifier(db, internal);
+ updateAddModifier(db);
}
if (fromVersion < 1203) {
// Empty version bump to ensure views are recreated
@@ -1791,7 +2038,7 @@
// Empty version bump to ensure views are recreated
}
if (fromVersion < 1205) {
- updateAddRecording(db, internal);
+ updateAddRecording(db);
}
if (fromVersion < 1206) {
// Empty version bump to ensure views are recreated
@@ -1805,6 +2052,28 @@
if (fromVersion < 1209) {
// Empty version bump to ensure views are recreated
}
+ if (fromVersion < 1301) {
+ updateAddDeletedMediaTable(db);
+ }
+ if (fromVersion < 1302) {
+ updateAddSpecialFormat(db);
+ }
+ if (fromVersion < 1303) {
+ // Empty version bump to ensure views are recreated
+ }
+ if (fromVersion < 1304) {
+ updateSpecialFormatToNotDetected(db);
+ }
+ if (fromVersion < 1305) {
+ // Empty version bump to ensure views are recreated
+ }
+ if (fromVersion < 1306) {
+ // Empty version bump to ensure views are recreated
+ }
+ if (fromVersion < 1307) {
+ // This is to ensure Animated Webp files are tagged
+ updateSpecialFormatToNotDetected(db);
+ }
// If this is the legacy database, it's not worth recomputing data
// values locally, since they'll be recomputed after the migration
@@ -1813,14 +2082,14 @@
}
if (recomputeDataValues) {
- recomputeDataValues(db, internal);
+ recomputeDataValues(db);
}
}
// Always recreate latest views and triggers during upgrade; they're
// cheap and it's an easy way to ensure they're defined consistently
- createLatestViews(db, internal);
- createLatestTriggers(db, internal);
+ createLatestViews(db);
+ createLatestTriggers(db);
getOrCreateUuid(db);
@@ -1927,6 +2196,31 @@
}
}
+ private boolean shouldBeIgnoredByOsVersion(@NonNull Field field) {
+ if (mExportedSinceAnnotation == null) {
+ return false;
+ }
+
+ if (!field.isAnnotationPresent(mExportedSinceAnnotation)) {
+ return false;
+ }
+
+ try {
+ final Annotation annotation = field.getAnnotation(mExportedSinceAnnotation);
+ final int exportedSinceOSVersion = (int) annotation.annotationType().getMethod(
+ "osVersion").invoke(annotation);
+ final boolean shouldIgnore = exportedSinceOSVersion > Build.VERSION.SDK_INT;
+ if (shouldIgnore) {
+ Log.d(TAG, "Ignoring column " + field.get(null) + " with version "
+ + exportedSinceOSVersion + " in OS version " + Build.VERSION.SDK_INT);
+ }
+ return shouldIgnore;
+ } catch (Exception e) {
+ Log.e(TAG, "Can't parse the OS version in ExportedSince annotation", e);
+ return false;
+ }
+ }
+
/**
* Return the current generation that will be populated into
* {@link MediaColumns#GENERATION_ADDED} or
@@ -1947,7 +2241,158 @@
null);
}
+ public boolean isInternal() {
+ return mName.equals(INTERNAL_DATABASE_NAME);
+ }
+
public boolean isExternal() {
- return !mInternal;
+ // Matches test dbs as external
+ switch (mName) {
+ case EXTERNAL_DATABASE_NAME:
+ return true;
+ case TEST_RECOMPUTE_DB:
+ return true;
+ case TEST_UPGRADE_DB:
+ return true;
+ case TEST_DOWNGRADE_DB:
+ return true;
+ case TEST_CLEAN_DB:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ @SuppressLint("DefaultLocale")
+ @GuardedBy("sRecoveryLock")
+ private void updateNextRowIdInDatabaseAndExternalStorage(SQLiteDatabase db) {
+ Optional<Long> nextRowIdOptional = getNextRowIdFromXattr();
+ // Use a billion as the next row id if not found on external storage.
+ long nextRowId = nextRowIdOptional.orElse(NEXT_ROW_ID_DEFAULT_BILLION_VALUE);
+
+ backupNextRowId(nextRowId);
+ // Insert and delete a row to update sqlite_sequence counter
+ 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));
+ }
+
+ /**
+ * Backs up next row id value in xattr to {@code nextRowId} + BackupFrequency. Also updates
+ * respective in-memory next row id cached value.
+ */
+ protected void backupNextRowId(long nextRowId) {
+ long backupId = nextRowId + getNextRowIdBackupFrequency();
+ boolean setOnExternalStorage = setXattr(DATA_MEDIA_XATTR_DIRECTORY_PATH,
+ getNextRowIdXattrKeyForDatabase(),
+ String.valueOf(backupId));
+ if (setOnExternalStorage) {
+ mNextRowIdBackup.set(backupId);
+ 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));
+ }
+ }
+
+ protected Optional<Long> getNextRowIdFromXattr() {
+ try {
+ return Optional.of(Long.parseLong(new String(
+ Os.getxattr(DATA_MEDIA_XATTR_DIRECTORY_PATH,
+ getNextRowIdXattrKeyForDatabase()))));
+ } catch (Exception e) {
+ Log.e(TAG, String.format(Locale.ROOT, "Xattr:%s not found on external storage.",
+ getNextRowIdXattrKeyForDatabase()), e);
+ return Optional.empty();
+ }
+ }
+
+ protected String getNextRowIdXattrKeyForDatabase() {
+ if (isInternal()) {
+ return INTERNAL_DB_NEXT_ROW_ID_XATTR_KEY;
+ } else if (isExternal()) {
+ return EXTERNAL_DB_NEXT_ROW_ID_XATTR_KEY;
+ }
+ throw new RuntimeException(
+ String.format(Locale.ROOT, "Next row id xattr key not defined for database:%s.",
+ mName));
+ }
+
+ protected String getSessionIdXattrKeyForDatabase() {
+ if (isInternal()) {
+ return INTERNAL_DB_SESSION_ID_XATTR_KEY;
+ } else if (isExternal()) {
+ return EXTERNAL_DB_SESSION_ID_XATTR_KEY;
+ }
+ throw new RuntimeException(
+ String.format(Locale.ROOT, "Session id xattr key not defined for database:%s.",
+ mName));
+ }
+
+ protected static boolean setXattr(String path, String key, String value) {
+ try (ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(path),
+ ParcelFileDescriptor.MODE_READ_ONLY)) {
+ // Map id value to xattr key
+ Os.setxattr(path, key, value.getBytes(), 0);
+ Os.fsync(pfd.getFileDescriptor());
+ 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(Locale.ROOT, "Failed to set xattr:%s to %s for path: %s.", key,
+ value, path), e);
+ return false;
+ }
+ }
+
+ protected static Optional<String> getXattr(String path, String key) {
+ try {
+ return Optional.of(Arrays.toString(Os.getxattr(path, key)));
+ } catch (Exception e) {
+ Log.w(TAG, String.format(Locale.ROOT,
+ "Exception encountered while reading xattr:%s from path:%s.", key, path));
+ return Optional.empty();
+ }
+ }
+
+ protected Optional<Long> getNextRowId() {
+ if (mNextRowIdBackup.get() == INVALID_ROW_ID) {
+ return getNextRowIdFromXattr();
+ }
+
+ return Optional.of(mNextRowIdBackup.get());
+ }
+
+ boolean isNextRowIdBackupEnabled() {
+ if (!mEnableNextRowIdRecovery) {
+ return false;
+ }
+
+ if (mVersion < VERSION_R) {
+ // Do not back up next row id if DB version is less than R. This is unlikely to hit
+ // as we will backport row id backup changes till Android R.
+ Log.v(TAG, "Skipping next row id backup for android versions less than R.");
+ return false;
+ }
+
+ if (isInternal()) {
+ // Skip id reuse fix for internal db as it can lead to ids starting from a billion
+ // and can cause aberrant behaviour in Ringtones Manager. Reference: b/229153534.
+ Log.v(TAG, "Skipping next row id backup for internal database.");
+ return false;
+ }
+
+ if (!(new File(DATA_MEDIA_XATTR_DIRECTORY_PATH)).exists()) {
+ Log.w(TAG, String.format(Locale.ROOT,
+ "Skipping row id recovery as path:%s does not exist.",
+ DATA_MEDIA_XATTR_DIRECTORY_PATH));
+ return false;
+ }
+
+ return SystemProperties.getBoolean("persist.sys.fuse.backup.nextrowid_enabled",
+ true);
+ }
+
+ public static int getNextRowIdBackupFrequency() {
+ return SystemProperties.getInt("persist.sys.fuse.backup.nextrowid_backup_frequency",
+ 1000);
}
}
diff --git a/src/com/android/providers/media/FdAccessResult.java b/src/com/android/providers/media/FdAccessResult.java
new file mode 100644
index 0000000..583e6fd
--- /dev/null
+++ b/src/com/android/providers/media/FdAccessResult.java
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+import android.text.TextUtils;
+
+/**
+ * Wrapper class which contains information about how a file descriptor can be opened
+ * after a {@link FuseDaemon#checkFdAccess} call.
+ */
+public final class FdAccessResult {
+ public final String filePath;
+ public final boolean shouldRedact;
+
+ public FdAccessResult(String filePath, boolean shouldRedact) {
+ this.filePath = filePath;
+ this.shouldRedact = shouldRedact;
+ }
+
+ public boolean isSuccess() {
+ return !TextUtils.isEmpty(filePath);
+ }
+}
diff --git a/src/com/android/providers/media/FileAccessAttributes.java b/src/com/android/providers/media/FileAccessAttributes.java
new file mode 100644
index 0000000..e9dc556
--- /dev/null
+++ b/src/com/android/providers/media/FileAccessAttributes.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+package com.android.providers.media;
+
+import android.database.Cursor;
+
+/**
+ * Class to represent the file metadata stored in the database (SQLite/xAttr)
+ */
+public final class FileAccessAttributes {
+ private final long mId;
+ private final int mMediaType;
+ private final boolean mIsPending;
+ private final boolean mIsTrashed;
+ // TODO(b/227348809): Remove ownerId field when we add the logic to check ownerId from xattr
+ private final int mOwnerId;
+ private final String mOwnerPackageName;
+
+ public FileAccessAttributes(long id, int mediaType, boolean isPending,
+ boolean isTrashed, int ownerId, String ownerPackageName) {
+ this.mId = id;
+ this.mMediaType = mediaType;
+ this.mIsPending = isPending;
+ this.mIsTrashed = isTrashed;
+ this.mOwnerId = ownerId;
+ this.mOwnerPackageName = ownerPackageName;
+ }
+
+ public static FileAccessAttributes fromCursor(Cursor c) {
+ final long id = c.getLong(0);
+ String ownerPackageName = c.getString(1);
+ final boolean isPending = c.getInt(2) != 0;
+ final int mediaType = c.getInt(3);
+ final boolean isTrashed = c.getInt(4) != 0;
+ return new FileAccessAttributes(id, mediaType, isPending, isTrashed, -1,
+ ownerPackageName);
+ }
+
+ public String toString() {
+ return String.format("Id: %s, Mediatype: %s, isPending: %s, "
+ + "isTrashed: %s, ownerpackageName: %s", this.mId, this.mMediaType,
+ mIsPending, mIsTrashed, mOwnerId);
+ }
+
+ public long getId() {
+ return this.mId;
+ }
+
+ public int getMediaType() {
+ return this.mMediaType;
+ }
+
+ public int getOwnerId() {
+ return this.mOwnerId;
+ }
+
+ public boolean isTrashed() {
+ return this.mIsTrashed;
+ }
+
+ public boolean isPending() {
+ return this.mIsPending;
+ }
+
+ public String getOwnerPackageName() {
+ return this.mOwnerPackageName;
+ }
+}
diff --git a/src/com/android/providers/media/FileLookupResult.java b/src/com/android/providers/media/FileLookupResult.java
index f0e6bfb..4ac0c0e 100644
--- a/src/com/android/providers/media/FileLookupResult.java
+++ b/src/com/android/providers/media/FileLookupResult.java
@@ -28,6 +28,11 @@
public final boolean transformsSupported;
public final String ioPath;
+ public FileLookupResult(int transforms, int uid, String ioPath) {
+ this (transforms, /* transformsReason */ 0, uid, /* transformsComplete */ true,
+ /* transformsSupported */ transforms == 0 ? false : true, ioPath);
+ }
+
public FileLookupResult(int transforms, int transformsReason, int uid,
boolean transformsComplete, boolean transformsSupported, String ioPath) {
this.transforms = transforms;
diff --git a/src/com/android/providers/media/FileOpenResult.java b/src/com/android/providers/media/FileOpenResult.java
index 1052f98..f52fa50 100644
--- a/src/com/android/providers/media/FileOpenResult.java
+++ b/src/com/android/providers/media/FileOpenResult.java
@@ -23,12 +23,23 @@
public final int status;
public final int uid;
public final int transformsUid;
+ public final int nativeFd;
public final long[] redactionRanges;
public FileOpenResult(int status, int uid, int transformsUid, long[] redactionRanges) {
+ this(status, uid, transformsUid, /* nativeFd */ -1, redactionRanges);
+ }
+
+ public FileOpenResult(int status, int uid, int transformsUid, int nativeFd,
+ long[] redactionRanges) {
this.status = status;
this.uid = uid;
this.transformsUid = transformsUid;
+ this.nativeFd = nativeFd;
this.redactionRanges = redactionRanges;
}
+
+ public static FileOpenResult createError(int errorCode, int uid) {
+ return new FileOpenResult(errorCode, uid, /* transformsUid */ 0, new long[0]);
+ }
}
diff --git a/src/com/android/providers/media/LocalCallingIdentity.java b/src/com/android/providers/media/LocalCallingIdentity.java
index 6191457..7387b44 100644
--- a/src/com/android/providers/media/LocalCallingIdentity.java
+++ b/src/com/android/providers/media/LocalCallingIdentity.java
@@ -17,7 +17,6 @@
package com.android.providers.media;
import static android.Manifest.permission.ACCESS_MEDIA_LOCATION;
-import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.permissionToOp;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
@@ -306,6 +305,7 @@
}
private boolean hasPermissionInternal(int permission) {
+ boolean targetSdkIsAtLeastT = getTargetSdkVersion() > Build.VERSION_CODES.S_V2;
// While we're here, enforce any broad user-level restrictions
if ((uid == Process.SHELL_UID) && context.getSystemService(UserManager.class)
.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER)) {
@@ -338,13 +338,13 @@
case PERMISSION_READ_AUDIO:
return checkPermissionReadAudio(
- context, pid, uid, getPackageName(), attributionTag);
+ context, pid, uid, getPackageName(), attributionTag, targetSdkIsAtLeastT);
case PERMISSION_READ_VIDEO:
return checkPermissionReadVideo(
- context, pid, uid, getPackageName(), attributionTag);
+ context, pid, uid, getPackageName(), attributionTag, targetSdkIsAtLeastT);
case PERMISSION_READ_IMAGES:
return checkPermissionReadImages(
- context, pid, uid, getPackageName(), attributionTag);
+ context, pid, uid, getPackageName(), attributionTag, targetSdkIsAtLeastT);
case PERMISSION_WRITE_AUDIO:
return checkPermissionWriteAudio(
context, pid, uid, getPackageName(), attributionTag);
diff --git a/src/com/android/providers/media/MediaDocumentsProvider.java b/src/com/android/providers/media/MediaDocumentsProvider.java
index c958721..62a1aa9 100644
--- a/src/com/android/providers/media/MediaDocumentsProvider.java
+++ b/src/com/android/providers/media/MediaDocumentsProvider.java
@@ -202,7 +202,7 @@
* When underlying provider is ready, we kick off a notification of roots
* changed so they can be refreshed.
*/
- static void onMediaStoreReady(Context context, String volumeName) {
+ static void onMediaStoreReady(Context context) {
sMediaStoreReady = true;
notifyRootsChanged(context);
}
@@ -1044,24 +1044,21 @@
final Uri target = getUriForDocumentId(docId);
final int callingUid = Binder.getCallingUid();
- if (!"r".equals(mode)) {
- throw new IllegalArgumentException("Media is read-only");
- }
-
// Delegate to real provider
final long token = Binder.clearCallingIdentity();
try {
- return openFileForRead(target, callingUid);
- } finally {
+ return openFile(target, callingUid, mode);
+ }
+ finally {
Binder.restoreCallingIdentity(token);
}
}
- public ParcelFileDescriptor openFileForRead(final Uri target, final int callingUid)
+ public ParcelFileDescriptor openFile(final Uri target, final int callingUid, String mode)
throws FileNotFoundException {
final Bundle opts = new Bundle();
opts.putInt(MediaStore.EXTRA_MEDIA_CAPABILITIES_UID, callingUid);
-
+ opts.putString(MediaStore.EXTRA_MODE, mode);
AssetFileDescriptor afd =
getContext().getContentResolver().openTypedAssetFileDescriptor(target, "*/*",
opts);
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index 705d7ec..9ae9c6f 100644
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -25,15 +25,25 @@
import static android.content.ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.database.Cursor.FIELD_TYPE_BLOB;
+import static android.provider.CloudMediaProviderContract.EXTRA_ASYNC_CONTENT_PROVIDER;
+import static android.provider.CloudMediaProviderContract.METHOD_GET_ASYNC_CONTENT_PROVIDER;
+import static android.provider.MediaStore.Files.FileColumns.MEDIA_TYPE;
+import static android.provider.MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE;
+import static android.provider.MediaStore.Files.FileColumns._SPECIAL_FORMAT;
+import static android.provider.MediaStore.Files.FileColumns._SPECIAL_FORMAT_NONE;
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.MY_UID;
+import static android.provider.MediaStore.PER_USER_RANGE;
import static android.provider.MediaStore.QUERY_ARG_DEFER_SCAN;
import static android.provider.MediaStore.QUERY_ARG_MATCH_FAVORITE;
import static android.provider.MediaStore.QUERY_ARG_MATCH_PENDING;
import static android.provider.MediaStore.QUERY_ARG_MATCH_TRASHED;
+import static android.provider.MediaStore.QUERY_ARG_REDACTED_URI;
import static android.provider.MediaStore.QUERY_ARG_RELATED_URI;
+import static android.provider.MediaStore.VOLUME_EXTERNAL;
import static android.provider.MediaStore.getVolumeName;
import static android.system.OsConstants.F_GETFL;
@@ -58,11 +68,13 @@
import static com.android.providers.media.LocalCallingIdentity.PERMISSION_WRITE_EXTERNAL_STORAGE;
import static com.android.providers.media.LocalCallingIdentity.PERMISSION_WRITE_IMAGES;
import static com.android.providers.media.LocalCallingIdentity.PERMISSION_WRITE_VIDEO;
+import static com.android.providers.media.PickerUriResolver.getMediaUri;
import static com.android.providers.media.scan.MediaScanner.REASON_DEMAND;
import static com.android.providers.media.scan.MediaScanner.REASON_IDLE;
import static com.android.providers.media.util.DatabaseUtils.bindList;
import static com.android.providers.media.util.FileUtils.DEFAULT_FOLDER_NAMES;
import static com.android.providers.media.util.FileUtils.PATTERN_PENDING_FILEPATH_FOR_SQL;
+import static com.android.providers.media.util.FileUtils.buildPrimaryVolumeFile;
import static com.android.providers.media.util.FileUtils.extractDisplayName;
import static com.android.providers.media.util.FileUtils.extractFileExtension;
import static com.android.providers.media.util.FileUtils.extractFileName;
@@ -73,6 +85,7 @@
import static com.android.providers.media.util.FileUtils.extractTopLevelDir;
import static com.android.providers.media.util.FileUtils.extractVolumeName;
import static com.android.providers.media.util.FileUtils.extractVolumePath;
+import static com.android.providers.media.util.FileUtils.fromFuseFile;
import static com.android.providers.media.util.FileUtils.getAbsoluteSanitizedPath;
import static com.android.providers.media.util.FileUtils.isCrossUserEnabled;
import static com.android.providers.media.util.FileUtils.isDataOrObbPath;
@@ -81,9 +94,19 @@
import static com.android.providers.media.util.FileUtils.isExternalMediaDirectory;
import static com.android.providers.media.util.FileUtils.isObbOrChildRelativePath;
import static com.android.providers.media.util.FileUtils.sanitizePath;
+import static com.android.providers.media.util.FileUtils.toFuseFile;
import static com.android.providers.media.util.Logging.LOGV;
import static com.android.providers.media.util.Logging.TAG;
+import static com.android.providers.media.util.SyntheticPathUtils.REDACTED_URI_ID_PREFIX;
+import static com.android.providers.media.util.SyntheticPathUtils.REDACTED_URI_ID_SIZE;
+import static com.android.providers.media.util.SyntheticPathUtils.createSparseFile;
+import static com.android.providers.media.util.SyntheticPathUtils.extractSyntheticRelativePathSegements;
+import static com.android.providers.media.util.SyntheticPathUtils.getRedactedRelativePath;
+import static com.android.providers.media.util.SyntheticPathUtils.isPickerPath;
+import static com.android.providers.media.util.SyntheticPathUtils.isRedactedPath;
+import static com.android.providers.media.util.SyntheticPathUtils.isSyntheticPath;
+import android.annotation.IntDef;
import android.app.AppOpsManager;
import android.app.AppOpsManager.OnOpActiveChangedListener;
import android.app.AppOpsManager.OnOpChangedListener;
@@ -145,7 +168,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
@@ -153,11 +175,14 @@
import android.os.storage.StorageManager.StorageVolumeCallback;
import android.os.storage.StorageVolume;
import android.preference.PreferenceManager;
+import android.provider.AsyncContentProvider;
import android.provider.BaseColumns;
import android.provider.Column;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.OnPropertiesChangedListener;
import android.provider.DocumentsContract;
+import android.provider.ExportedSince;
+import android.provider.IAsyncContentProvider;
import android.provider.MediaStore;
import android.provider.MediaStore.Audio;
import android.provider.MediaStore.Audio.AudioColumns;
@@ -180,6 +205,7 @@
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.LongSparseArray;
+import android.util.Pair;
import android.util.Size;
import android.util.SparseArray;
import android.webkit.MimeTypeMap;
@@ -191,16 +217,21 @@
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
+import com.android.modules.utils.BackgroundThread;
import com.android.modules.utils.build.SdkLevel;
import com.android.providers.media.DatabaseHelper.OnFilesChangeListener;
import com.android.providers.media.DatabaseHelper.OnLegacyMigrationListener;
+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.PickerDataLayer;
+import com.android.providers.media.photopicker.PickerSyncController;
+import com.android.providers.media.photopicker.data.ExternalDbFacade;
+import com.android.providers.media.photopicker.data.PickerDbFacade;
import com.android.providers.media.playlist.Playlist;
import com.android.providers.media.scan.MediaScanner;
import com.android.providers.media.scan.ModernMediaScanner;
-import com.android.providers.media.util.BackgroundThread;
import com.android.providers.media.util.CachedSupplier;
import com.android.providers.media.util.DatabaseUtils;
import com.android.providers.media.util.FileUtils;
@@ -211,8 +242,12 @@
import com.android.providers.media.util.Metrics;
import com.android.providers.media.util.MimeUtils;
import com.android.providers.media.util.PermissionUtils;
+import com.android.providers.media.util.Preconditions;
import com.android.providers.media.util.SQLiteQueryBuilder;
+import com.android.providers.media.util.SpecialFormatDetector;
+import com.android.providers.media.util.StringUtils;
import com.android.providers.media.util.UserCache;
+import com.android.providers.media.util.XAttrUtils;
import com.android.providers.media.util.XmpInterface;
import com.google.common.hash.Hashing;
@@ -225,6 +260,8 @@
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
@@ -241,11 +278,16 @@
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
/**
* Media content provider. See {@link android.provider.MediaStore} for details.
@@ -273,6 +315,9 @@
/** File access by uid is a synthetic path corresponding to a redacted URI */
private static final int FLAG_TRANSFORM_REDACTION = 1 << 1;
+ /** File access by uid is a synthetic path corresponding to a picker URI */
+ private static final int FLAG_TRANSFORM_PICKER = 1 << 2;
+
/**
* These directory names aren't declared in Environment as final variables, and so we need to
* have the same values in separate final variables in order to have them considered constant
@@ -296,11 +341,6 @@
private static final String DIRECTORY_MEDIA = "media";
private static final String DIRECTORY_THUMBNAILS = ".thumbnails";
- private static final String REDACTED_URI_ID_PREFIX = "RUID";
- private static final String TRANSFORMS_SYNTHETIC_DIR = ".transforms/synthetic";
- private static final String REDACTED_URI_DIR = TRANSFORMS_SYNTHETIC_DIR + "/redacted";
- public static final int REDACTED_URI_ID_SIZE = 36;
- private static final String QUERY_ARG_REDACTED_URI = "android:query-arg-redacted-uri";
/**
* Hard-coded filename where the current value of
@@ -331,6 +371,13 @@
private static final int NON_HIDDEN_CACHE_SIZE = 50;
/**
+ * This is required as idle maintenance maybe stopped anytime; we do not want to query
+ * and accumulate values to update for a long time, instead we want to batch query and update
+ * by a limited number.
+ */
+ private static final int IDLE_MAINTENANCE_ROWS_LIMIT = 1000;
+
+ /**
* Where clause to match pending files from FUSE. Pending files from FUSE will not have
* PATTERN_PENDING_FILEPATH_FOR_SQL pattern.
*/
@@ -357,10 +404,6 @@
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
static final long ENABLE_INCLUDE_ALL_VOLUMES = 182734110L;
- // Stolen from: UserHandle#getUserId
- private static final int PER_USER_RANGE = 100000;
- private static final int MY_UID = android.os.Process.myUid();
-
/**
* Set of {@link Cursor} columns that refer to raw filesystem paths.
*/
@@ -451,6 +494,7 @@
private PackageManager mPackageManager;
private DevicePolicyManager mDevicePolicyManager;
private UserManager mUserManager;
+ private PickerUriResolver mPickerUriResolver;
private UserCache mUserCache;
private VolumeCache mVolumeCache;
@@ -502,7 +546,7 @@
LocalCallingIdentity identity = mCachedCallingIdentityForFuse.get(uid);
if (identity == null) {
identity = LocalCallingIdentity.fromExternal(getContext(), mUserCache, uid);
- if (uid / PER_USER_RANGE == sUserId) {
+ if (uidToUserId(uid) == sUserId) {
mCachedCallingIdentityForFuse.put(uid, identity);
} else {
// In some app cloning designs, MediaProvider user 0 may
@@ -576,6 +620,10 @@
String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
if (pkg != null) {
invalidateLocalCallingIdentityCache(pkg, "package " + intent.getAction());
+ if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
+ mUserCache.invalidateWorkProfileOwnerApps(pkg);
+ mPickerSyncController.notifyPackageRemoval(pkg);
+ }
} else {
Log.w(TAG, "Failed to retrieve package from intent: " + intent.getAction());
}
@@ -584,6 +632,32 @@
}
};
+ private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case Intent.ACTION_USER_REMOVED:
+ /**
+ * Removing media files for user being deleted. This would impact if the deleted
+ * user have been using same MediaProvider as the current user i.e. when
+ * isMediaSharedWithParent is true.On removal of such user profile,
+ * the owner's MediaProvider would need to clean any media files stored
+ * by the removed user profile.
+ */
+ UserHandle userToBeRemoved = intent.getParcelableExtra(Intent.EXTRA_USER);
+ if(userToBeRemoved.getIdentifier() != sUserId){
+ mExternalDatabase.runWithTransaction((db) -> {
+ db.execSQL("delete from files where _user_id=?",
+ new String[]{String.valueOf(userToBeRemoved.getIdentifier())});
+ return null ;
+ });
+ }
+ break;
+ }
+ }
+ };
+
+
private void invalidateLocalCallingIdentityCache(String packageName, String reason) {
synchronized (mCachedCallingIdentityForFuse) {
try {
@@ -656,44 +730,62 @@
*/
private final OnFilesChangeListener mFilesListener = new OnFilesChangeListener() {
@Override
- public void onInsert(@NonNull DatabaseHelper helper, @NonNull String volumeName, long id,
- int mediaType, boolean isDownload) {
- handleInsertedRowForFuse(id);
- acceptWithExpansion(helper::notifyInsert, volumeName, id, mediaType, isDownload);
-
+ public void onInsert(@NonNull DatabaseHelper helper, @NonNull FileRow insertedRow) {
+ handleInsertedRowForFuse(insertedRow.getId());
+ acceptWithExpansion(helper::notifyInsert, insertedRow.getVolumeName(),
+ insertedRow.getId(), insertedRow.getMediaType(), insertedRow.isDownload());
+ updateNextRowIdXattr(helper, insertedRow.getId());
helper.postBackground(() -> {
if (helper.isExternal()) {
// Update the quota type on the filesystem
- Uri fileUri = MediaStore.Files.getContentUri(volumeName, id);
- updateQuotaTypeForUri(fileUri, mediaType);
+ Uri fileUri = MediaStore.Files.getContentUri(insertedRow.getVolumeName(),
+ insertedRow.getId());
+ updateQuotaTypeForUri(fileUri, insertedRow.getMediaType());
}
// Tell our SAF provider so it knows when views are no longer empty
- MediaDocumentsProvider.onMediaStoreInsert(getContext(), volumeName, mediaType, id);
+ MediaDocumentsProvider.onMediaStoreInsert(getContext(), insertedRow.getVolumeName(),
+ insertedRow.getMediaType(), insertedRow.getId());
+
+ if (mExternalDbFacade.onFileInserted(insertedRow.getMediaType(),
+ insertedRow.isPending())) {
+ mPickerSyncController.notifyMediaEvent();
+ }
});
}
@Override
- public void onUpdate(@NonNull DatabaseHelper helper, @NonNull String volumeName,
- long oldId, int oldMediaType, boolean oldIsDownload,
- long newId, int newMediaType, boolean newIsDownload,
- String oldOwnerPackage, String newOwnerPackage, String oldPath) {
- final boolean isDownload = oldIsDownload || newIsDownload;
- final Uri fileUri = MediaStore.Files.getContentUri(volumeName, oldId);
- handleUpdatedRowForFuse(oldPath, oldOwnerPackage, oldId, newId);
- handleOwnerPackageNameChange(oldPath, oldOwnerPackage, newOwnerPackage);
- acceptWithExpansion(helper::notifyUpdate, volumeName, oldId, oldMediaType, isDownload);
-
+ public void onUpdate(@NonNull DatabaseHelper helper, @NonNull FileRow oldRow,
+ @NonNull FileRow newRow) {
+ final boolean isDownload = oldRow.isDownload() || newRow.isDownload();
+ final Uri fileUri = MediaStore.Files.getContentUri(oldRow.getVolumeName(),
+ oldRow.getId());
+ handleUpdatedRowForFuse(oldRow.getPath(), oldRow.getOwnerPackageName(), oldRow.getId(),
+ newRow.getId());
+ handleOwnerPackageNameChange(oldRow.getPath(), oldRow.getOwnerPackageName(),
+ newRow.getOwnerPackageName());
+ acceptWithExpansion(helper::notifyUpdate, oldRow.getVolumeName(), oldRow.getId(),
+ oldRow.getMediaType(), isDownload);
+ updateNextRowIdXattr(helper, newRow.getId());
helper.postBackground(() -> {
if (helper.isExternal()) {
// Update the quota type on the filesystem
- updateQuotaTypeForUri(fileUri, newMediaType);
+ updateQuotaTypeForUri(fileUri, newRow.getMediaType());
+ }
+
+ if (mExternalDbFacade.onFileUpdated(oldRow.getId(),
+ oldRow.getMediaType(), newRow.getMediaType(),
+ oldRow.isTrashed(), newRow.isTrashed(),
+ oldRow.isPending(), newRow.isPending(),
+ oldRow.isFavorite(), newRow.isFavorite(),
+ oldRow.getSpecialFormat(), newRow.getSpecialFormat())) {
+ mPickerSyncController.notifyMediaEvent();
}
});
- if (newMediaType != oldMediaType) {
- acceptWithExpansion(helper::notifyUpdate, volumeName, oldId, newMediaType,
- isDownload);
+ if (newRow.getMediaType() != oldRow.getMediaType()) {
+ acceptWithExpansion(helper::notifyUpdate, oldRow.getVolumeName(), oldRow.getId(),
+ newRow.getMediaType(), isDownload);
helper.postBackground(() -> {
// Invalidate any thumbnails when the media type changes
@@ -703,13 +795,13 @@
}
@Override
- public void onDelete(@NonNull DatabaseHelper helper, @NonNull String volumeName, long id,
- int mediaType, boolean isDownload, String ownerPackageName, String path) {
- handleDeletedRowForFuse(path, ownerPackageName, id);
- acceptWithExpansion(helper::notifyDelete, volumeName, id, mediaType, isDownload);
+ public void onDelete(@NonNull DatabaseHelper helper, @NonNull FileRow deletedRow) {
+ handleDeletedRowForFuse(deletedRow.getPath(), deletedRow.getOwnerPackageName(),
+ deletedRow.getId());
+ acceptWithExpansion(helper::notifyDelete, deletedRow.getVolumeName(),
+ deletedRow.getId(), deletedRow.getMediaType(), deletedRow.isDownload());
// Remove cached transcoded file if any
- mTranscodeHelper.deleteCachedTranscodeFile(id);
-
+ mTranscodeHelper.deleteCachedTranscodeFile(deletedRow.getId());
helper.postBackground(() -> {
// Item no longer exists, so revoke all access to it
@@ -717,28 +809,58 @@
try {
acceptWithExpansion((uri) -> {
getContext().revokeUriPermission(uri, ~0);
- }, volumeName, id, mediaType, isDownload);
+ },
+ deletedRow.getVolumeName(), deletedRow.getId(),
+ deletedRow.getMediaType(), deletedRow.isDownload());
} finally {
Trace.endSection();
}
- switch (mediaType) {
+ switch (deletedRow.getMediaType()) {
case FileColumns.MEDIA_TYPE_PLAYLIST:
case FileColumns.MEDIA_TYPE_AUDIO:
if (helper.isExternal()) {
- removePlaylistMembers(mediaType, id);
+ removePlaylistMembers(deletedRow.getMediaType(), deletedRow.getId());
}
}
// Invalidate any thumbnails now that media is gone
- invalidateThumbnails(MediaStore.Files.getContentUri(volumeName, id));
+ invalidateThumbnails(MediaStore.Files.getContentUri(deletedRow.getVolumeName(),
+ deletedRow.getId()));
// Tell our SAF provider so it can revoke too
- MediaDocumentsProvider.onMediaStoreDelete(getContext(), volumeName, mediaType, id);
+ MediaDocumentsProvider.onMediaStoreDelete(getContext(), deletedRow.getVolumeName(),
+ deletedRow.getMediaType(), deletedRow.getId());
+
+ if (mExternalDbFacade.onFileDeleted(deletedRow.getId(),
+ deletedRow.getMediaType())) {
+ mPickerSyncController.notifyMediaEvent();
+ }
});
}
};
+ protected void updateNextRowIdXattr(DatabaseHelper helper, long id) {
+ if (!helper.isNextRowIdBackupEnabled()) {
+ Log.v(TAG, "Skipping next row id backup.");
+ return;
+ }
+
+ Optional<Long> nextRowIdBackupOptional = helper.getNextRowId();
+ if (!nextRowIdBackupOptional.isPresent()) {
+ 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(Locale.ROOT, "Inserted id:%d less than next row id backup:%d.",
+ id, nextRowIdBackupOptional.get()));
+ }
+ }
+
private final UnaryOperator<String> mIdGenerator = path -> {
final long rowId = mCallingIdentity.get().getDeletedRowId(path);
if (rowId != -1 && isFuseThread()) {
@@ -822,12 +944,27 @@
}
/**
- * Ensure that default folders are created on mounted primary storage
- * devices. We only do this once per volume so we don't annoy the user if
- * deleted manually.
+ * Ensure that default folders are created on mounted storage devices.
+ * We only do this once per volume so we don't annoy the user if deleted
+ * manually.
*/
private void ensureDefaultFolders(@NonNull MediaVolume volume, @NonNull SQLiteDatabase db) {
- final String key = "created_default_folders_" + volume.getId();
+ if (volume.isExternallyManaged()) {
+ // Default folders should not be automatically created inside volumes managed from
+ // outside Android.
+ return;
+ }
+ final String volumeName = volume.getName();
+ String key;
+ if (volumeName.equals(MediaStore.VOLUME_EXTERNAL_PRIMARY)) {
+ // For the primary volume, we use the ID, because we may be handling
+ // the primary volume for multiple users
+ key = "created_default_folders_" + volume.getId();
+ } else {
+ // For others, like public volumes, just use the name, because the id
+ // might not change when re-formatted
+ key = "created_default_folders_" + volumeName;
+ }
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
if (prefs.getInt(key, 0) == 0) {
@@ -852,6 +989,12 @@
* disk, then all thumbnails will be considered stable and will be deleted.
*/
private void ensureThumbnailsValid(@NonNull MediaVolume volume, @NonNull SQLiteDatabase db) {
+ if (volume.isExternallyManaged()) {
+ // Default folders and thumbnail directories should not be automatically created inside
+ // volumes managed from outside Android, and there is no need to ensure the validity of
+ // their thumbnails here.
+ return;
+ }
final String uuidFromDatabase = DatabaseHelper.getOrCreateUuid(db);
try {
for (File dir : getThumbnailDirectories(volume)) {
@@ -918,12 +1061,26 @@
mMediaScanner = new ModernMediaScanner(context);
- mInternalDatabase = new DatabaseHelper(context, INTERNAL_DATABASE_NAME,
- true, false, false, Column.class,
- Metrics::logSchemaChange, mFilesListener, MIGRATION_LISTENER, mIdGenerator);
- mExternalDatabase = new DatabaseHelper(context, EXTERNAL_DATABASE_NAME,
- false, false, false, Column.class,
- Metrics::logSchemaChange, mFilesListener, MIGRATION_LISTENER, mIdGenerator);
+ mInternalDatabase = new DatabaseHelper(context, INTERNAL_DATABASE_NAME, false, false,
+ Column.class, ExportedSince.class, Metrics::logSchemaChange, mFilesListener,
+ MIGRATION_LISTENER, mIdGenerator, true);
+ mExternalDatabase = new DatabaseHelper(context, EXTERNAL_DATABASE_NAME, false, false,
+ Column.class, ExportedSince.class, Metrics::logSchemaChange, mFilesListener,
+ MIGRATION_LISTENER, mIdGenerator, true);
+ mExternalDbFacade = new ExternalDbFacade(getContext(), mExternalDatabase, mVolumeCache);
+ mPickerDbFacade = new PickerDbFacade(context);
+
+ final String localPickerProvider = PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY;
+ final String allowedCloudProviders =
+ getStringDeviceConfig(PickerSyncController.ALLOWED_CLOUD_PROVIDERS_KEY,
+ /* default */ "");
+ final int pickerSyncDelayMs = getIntDeviceConfig(PickerSyncController.SYNC_DELAY_MS,
+ /* default */ 5000);
+
+ mPickerSyncController = new PickerSyncController(context, mPickerDbFacade,
+ localPickerProvider, allowedCloudProviders, pickerSyncDelayMs);
+ mPickerDataLayer = new PickerDataLayer(context, mPickerDbFacade, mPickerSyncController);
+ mPickerUriResolver = new PickerUriResolver(context, mPickerDbFacade);
if (SdkLevel.isAtLeastS()) {
mTranscodeHelper = new TranscodeHelperImpl(context, this);
@@ -931,8 +1088,8 @@
mTranscodeHelper = new TranscodeHelperNoOp();
}
- // Create dir for redacted URI's path.
- new File("/storage/emulated/" + UserHandle.myUserId(), REDACTED_URI_DIR).mkdirs();
+ // Create dir for redacted and picker URI paths.
+ buildPrimaryVolumeFile(uidToUserId(MY_UID), getRedactedRelativePath()).mkdirs();
final IntentFilter packageFilter = new IntentFilter();
packageFilter.setPriority(10);
@@ -941,15 +1098,30 @@
packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
context.registerReceiver(mPackageReceiver, packageFilter);
+ // Creating intent broadcast receiver for user actions like Intent.ACTION_USER_REMOVED,
+ // where we would need to remove files stored by removed user.
+ final IntentFilter userIntentFilter = new IntentFilter();
+ userIntentFilter.addAction(Intent.ACTION_USER_REMOVED);
+ context.registerReceiver(mUserIntentReceiver, userIntentFilter);
+
// Watch for invalidation of cached volumes
mStorageManager.registerStorageVolumeCallback(context.getMainExecutor(),
new StorageVolumeCallback() {
@Override
public void onStateChanged(@NonNull StorageVolume volume) {
updateVolumes();
- }
+ }
});
+ if (SdkLevel.isAtLeastT()) {
+ try {
+ mStorageManager.setCloudMediaProvider(mPickerSyncController.getCloudProvider());
+ } catch (SecurityException e) {
+ // This can happen in unit tests
+ Log.w(TAG, "Failed to update the system_server with the latest cloud provider", e);
+ }
+ }
+
updateVolumes();
attachVolume(MediaVolume.fromInternal(), /* validate */ false);
for (MediaVolume volume : mVolumeCache.getExternalVolumes()) {
@@ -963,6 +1135,12 @@
mAppOpsManager.startWatchingMode(AppOpsManager.OPSTR_READ_EXTERNAL_STORAGE,
null /* all packages */, mModeListener);
+ mAppOpsManager.startWatchingMode(AppOpsManager.OPSTR_READ_MEDIA_AUDIO,
+ null /* all packages */, mModeListener);
+ mAppOpsManager.startWatchingMode(AppOpsManager.OPSTR_READ_MEDIA_IMAGES,
+ null /* all packages */, mModeListener);
+ mAppOpsManager.startWatchingMode(AppOpsManager.OPSTR_READ_MEDIA_VIDEO,
+ null /* all packages */, mModeListener);
mAppOpsManager.startWatchingMode(AppOpsManager.OPSTR_WRITE_EXTERNAL_STORAGE,
null /* all packages */, mModeListener);
mAppOpsManager.startWatchingMode(permissionToOp(ACCESS_MEDIA_LOCATION),
@@ -1007,6 +1185,16 @@
return true;
}
+ Optional<DatabaseHelper> getDatabaseHelper(String dbName) {
+ if (dbName.equalsIgnoreCase(INTERNAL_DATABASE_NAME)) {
+ return Optional.of(mInternalDatabase);
+ } else if (dbName.equalsIgnoreCase(EXTERNAL_DATABASE_NAME)) {
+ return Optional.of(mExternalDatabase);
+ }
+
+ return Optional.empty();
+ }
+
@Override
public void onCallingPackageChanged() {
// Identity of the current thread has changed, so invalidate caches
@@ -1031,8 +1219,9 @@
mCallingIdentity.set(token);
}
- private boolean isPackageKnown(@NonNull String packageName) {
- final PackageManager pm = getContext().getPackageManager();
+ private boolean isPackageKnown(@NonNull String packageName, int userId) {
+ final Context context = mUserCache.getContextForUser(UserHandle.of(userId));
+ final PackageManager pm = context.getPackageManager();
// First, is the app actually installed?
try {
@@ -1054,7 +1243,6 @@
public void onIdleMaintenance(@NonNull CancellationSignal signal) {
final long startTime = SystemClock.elapsedRealtime();
-
// Trim any stale log files before we emit new events below
Logging.trimPersistent();
@@ -1083,25 +1271,7 @@
Log.d(TAG, "Pruned " + staleThumbnails + " unknown thumbnails");
// Finished orphaning any content whose package no longer exists
- final int stalePackages = mExternalDatabase.runWithTransaction((db) -> {
- final ArraySet<String> unknownPackages = new ArraySet<>();
- try (Cursor c = db.query(true, "files", new String[] { "owner_package_name" },
- null, null, null, null, null, null, signal)) {
- while (c.moveToNext()) {
- final String packageName = c.getString(0);
- if (TextUtils.isEmpty(packageName)) continue;
-
- if (!isPackageKnown(packageName)) {
- unknownPackages.add(packageName);
- }
- }
- }
- for (String packageName : unknownPackages) {
- onPackageOrphaned(db, packageName);
- }
- return unknownPackages.size();
- });
- Log.d(TAG, "Pruned " + stalePackages + " unknown packages");
+ pruneStalePackages(signal);
// Delete the expired items or extend them on mounted volumes
final int[] result = deleteOrExtendExpiredItems(signal);
@@ -1110,6 +1280,86 @@
Log.d(TAG, "Extended " + result[1] + " expired items");
// Forget any stale volumes
+ deleteStaleVolumes(signal);
+
+ final long itemCount = mExternalDatabase.runWithTransaction((db) -> {
+ return DatabaseHelper.getItemCount(db);
+ });
+
+ // Cleaning media files for users that have been removed
+ cleanMediaFilesForRemovedUser(signal);
+
+ // Populate _SPECIAL_FORMAT column for files which have column value as NULL
+ detectSpecialFormat(signal);
+
+ final long durationMillis = (SystemClock.elapsedRealtime() - startTime);
+ Metrics.logIdleMaintenance(MediaStore.VOLUME_EXTERNAL, itemCount,
+ durationMillis, staleThumbnails, deletedExpiredMedia);
+ }
+
+ /**
+ * This function find and clean the files related to user who have been removed
+ */
+ private void cleanMediaFilesForRemovedUser(CancellationSignal signal) {
+ //Finding userIds that are available in database
+ final List<String> userIds = mExternalDatabase.runWithTransaction((db) -> {
+ final List<String> userIdsPresent = new ArrayList<>();
+ try (Cursor c = db.query(true, "files", new String[] { "_user_id" },
+ null, null, null, null, null,
+ null, signal)) {
+ while (c.moveToNext()) {
+ final String userId = c.getString(0);
+ userIdsPresent.add(userId);
+ }
+ }
+ return userIdsPresent;
+ });
+
+ //removing calling userId
+ userIds.remove(String.valueOf(sUserId));
+ //removing all the valid/existing user, remaining userIds would be users who would have been
+ //removed
+ userIds.removeAll(mUserManager.getEnabledProfiles().stream()
+ .map(userHandle -> String.valueOf(userHandle.getIdentifier())).collect(
+ Collectors.toList()));
+
+ // Cleaning media files of users who have been removed
+ mExternalDatabase.runWithTransaction((db) -> {
+ userIds.stream().forEach(userId ->{
+ Log.d(TAG, "Removing media files associated with user : " + userId);
+ db.execSQL("delete from files where _user_id=?",
+ new String[]{String.valueOf(userId)});
+ });
+ return null ;
+ });
+ }
+
+ private void pruneStalePackages(CancellationSignal signal) {
+ final int stalePackages = mExternalDatabase.runWithTransaction((db) -> {
+ final ArraySet<Pair<String, Integer>> unknownPackages = new ArraySet<>();
+ try (Cursor c = db.query(true, "files",
+ new String[] { "owner_package_name", "_user_id" },
+ null, null, null, null, null, null, signal)) {
+ while (c.moveToNext()) {
+ final String packageName = c.getString(0);
+ if (TextUtils.isEmpty(packageName)) continue;
+
+ final int userId = c.getInt(1);
+
+ if (!isPackageKnown(packageName, userId)) {
+ unknownPackages.add(Pair.create(packageName, userId));
+ }
+ }
+ }
+ for (Pair<String, Integer> pair : unknownPackages) {
+ onPackageOrphaned(db, pair.first, pair.second);
+ }
+ return unknownPackages.size();
+ });
+ Log.d(TAG, "Pruned " + stalePackages + " unknown packages");
+ }
+
+ private void deleteStaleVolumes(CancellationSignal signal) {
mExternalDatabase.runWithTransaction((db) -> {
final Set<String> recentVolumeNames = MediaStore
.getRecentExternalVolumeNames(getContext());
@@ -1134,14 +1384,103 @@
synchronized (mDirectoryCache) {
mDirectoryCache.clear();
}
+ }
- final long itemCount = mExternalDatabase.runWithTransaction((db) -> {
- return DatabaseHelper.getItemCount(db);
+ @VisibleForTesting
+ public void setUriResolver(PickerUriResolver resolver) {
+ Log.w(TAG, "Changing the PickerUriResolver!!! Should only be called during test");
+ mPickerUriResolver = resolver;
+ }
+
+ @VisibleForTesting
+ void detectSpecialFormat(@NonNull CancellationSignal signal) {
+ mExternalDatabase.runWithTransaction((db) -> {
+ updateSpecialFormatColumn(db, signal);
+ return null;
});
+ }
- final long durationMillis = (SystemClock.elapsedRealtime() - startTime);
- Metrics.logIdleMaintenance(MediaStore.VOLUME_EXTERNAL, itemCount,
- durationMillis, staleThumbnails, deletedExpiredMedia);
+ private void updateSpecialFormatColumn(SQLiteDatabase db, @NonNull CancellationSignal signal) {
+ // This is to ensure we only do a bounded iteration over the rows as updates can fail, and
+ // we don't want to keep running the query/update indefinitely.
+ final int totalRowsToUpdate = getPendingSpecialFormatRowsCount(db,signal);
+ for (int i = 0 ; i < totalRowsToUpdate ; i += IDLE_MAINTENANCE_ROWS_LIMIT) {
+ updateSpecialFormatForLimitedRows(db, signal);
+ }
+ }
+
+ private int getPendingSpecialFormatRowsCount(SQLiteDatabase db,
+ @NonNull CancellationSignal signal) {
+ try (Cursor c = queryForPendingSpecialFormatColumns(db, /* limit */ null, signal)) {
+ if (c == null) {
+ return 0;
+ }
+ return c.getCount();
+ }
+ }
+
+ private void updateSpecialFormatForLimitedRows(SQLiteDatabase db,
+ @NonNull CancellationSignal signal) {
+ final SQLiteQueryBuilder qbForUpdate = getQueryBuilder(TYPE_UPDATE, FILES,
+ Files.getContentUri(VOLUME_EXTERNAL), Bundle.EMPTY, null);
+ // Accumulate all the new SPECIAL_FORMAT updates with their ids
+ ArrayMap<Long, Integer> newSpecialFormatValues = new ArrayMap<>();
+ final String limit = String.valueOf(IDLE_MAINTENANCE_ROWS_LIMIT);
+ try (Cursor c = queryForPendingSpecialFormatColumns(db, limit, signal)) {
+ while (c.moveToNext() && !signal.isCanceled()) {
+ final long id = c.getLong(0);
+ final String path = c.getString(1);
+ newSpecialFormatValues.put(id, getSpecialFormatValue(path));
+ }
+ }
+
+ // Now, update all the new SPECIAL_FORMAT values.
+ final ContentValues values = new ContentValues();
+ int count = 0;
+ for (long id: newSpecialFormatValues.keySet()) {
+ if (signal.isCanceled()) {
+ return;
+ }
+
+ values.clear();
+ values.put(_SPECIAL_FORMAT, newSpecialFormatValues.get(id));
+ final String selection = MediaColumns._ID + "=?";
+ final String[] selectionArgs = new String[]{String.valueOf(id)};
+ if (qbForUpdate.update(db, values, selection, selectionArgs) == 1) {
+ count++;
+ } else {
+ Log.e(TAG, "Unable to update _SPECIAL_FORMAT for id = " + id);
+ }
+ }
+ Log.d(TAG, "Updated _SPECIAL_FORMAT for " + count + " items");
+ }
+
+ private int getSpecialFormatValue(String path) {
+ final File file = new File(path);
+ if (!file.exists()) {
+ // We always update special format to none if the file is not found or there is an
+ // error, this is so that we do not repeat over the same column again and again.
+ return _SPECIAL_FORMAT_NONE;
+ }
+
+ try {
+ return SpecialFormatDetector.detect(file);
+ } catch (Exception e) {
+ // we tried our best, no need to run special detection again and again if it
+ // throws exception once, it is likely to do so everytime.
+ Log.d(TAG, "Failed to detect special format for file: " + file, e);
+ return _SPECIAL_FORMAT_NONE;
+ }
+ }
+
+ private Cursor queryForPendingSpecialFormatColumns(SQLiteDatabase db, String limit,
+ @NonNull CancellationSignal signal) {
+ // Run special detection for images only
+ final String selection = _SPECIAL_FORMAT + " IS NULL AND "
+ + MEDIA_TYPE + "=" + MEDIA_TYPE_IMAGE;
+ final String[] projection = new String[] { MediaColumns._ID, MediaColumns.DATA };
+ return db.query(/* distinct */ true, "files", projection, selection, null, null, null,
+ null, limit, signal);
}
/**
@@ -1160,7 +1499,7 @@
final long expiredOneWeek =
((System.currentTimeMillis() - DateUtils.WEEK_IN_MILLIS) / 1000);
final long now = (System.currentTimeMillis() / 1000);
- final Long extendedTime = now + (FileUtils.DEFAULT_DURATION_EXTENDED / 1000);
+ final Long expiredTime = now + (FileUtils.DEFAULT_DURATION_EXTENDED / 1000);
final int result[] = mExternalDatabase.runWithTransaction((db) -> {
String selection = FileColumns.DATE_EXPIRES + " < " + now;
selection += " AND volume_name in " + bindList(MediaStore.getExternalVolumeNames(
@@ -1171,6 +1510,7 @@
null, signal)) {
int totalDeleteCount = 0;
int totalExtendedCount = 0;
+ int index = 0;
while (c.moveToNext()) {
final String volumeName = c.getString(0);
final long id = c.getLong(1);
@@ -1180,10 +1520,13 @@
totalDeleteCount += delete(Files.getContentUri(volumeName, id), null, null);
} else {
final String oriPath = c.getString(3);
- final boolean success = extendExpiredItem(db, oriPath, id, extendedTime);
+
+ final boolean success = extendExpiredItem(db, oriPath, id, expiredTime,
+ expiredTime + index);
if (success) {
totalExtendedCount++;
}
+ index++;
}
}
return new int[]{totalDeleteCount, totalExtendedCount};
@@ -1193,32 +1536,84 @@
}
/**
- * Extend the expired items by renaming the file to new path with new
- * timestamp and updating the database for {@link FileColumns#DATA} and
- * {@link FileColumns#DATE_EXPIRES}
+ * Extend the expired items by renaming the file to new path with new timestamp and updating the
+ * database for {@link FileColumns#DATA} and {@link FileColumns#DATE_EXPIRES}. If there is
+ * UNIQUE constraint error for FileColumns.DATA, use adjustedExpiredTime and generate the new
+ * path by adjustedExpiredTime.
*/
private boolean extendExpiredItem(@NonNull SQLiteDatabase db, @NonNull String originalPath,
- Long id, Long extendedTime) {
- final String newPath = FileUtils.getAbsoluteExtendedPath(originalPath, extendedTime);
+ long id, long newExpiredTime, long adjustedExpiredTime) {
+ String newPath = FileUtils.getAbsoluteExtendedPath(originalPath, newExpiredTime);
if (newPath == null) {
+ Log.e(TAG, "Couldn't compute path for " + originalPath + " and expired time "
+ + newExpiredTime);
return false;
}
try {
+ if (updateDatabaseForExpiredItem(db, newPath, id, newExpiredTime)) {
+ return renameInLowerFsAndInvalidateFuseDentry(originalPath, newPath);
+ }
+ return false;
+ } catch (SQLiteConstraintException e) {
+ final String errorMessage =
+ "Update database _data from " + originalPath + " to " + newPath + " failed.";
+ Log.d(TAG, errorMessage, e);
+ }
+
+ // When we update the database for newPath with newExpiredTime, if the new path already
+ // exists in the database, it may raise SQLiteConstraintException.
+ // If there are two expired items that have the same display name in the same directory,
+ // but they have different expired time. E.g. .trashed-123-A.jpg and .trashed-456-A.jpg.
+ // After we rename .trashed-123-A.jpg to .trashed-newExpiredTime-A.jpg, then we rename
+ // .trashed-456-A.jpg to .trashed-newExpiredTime-A.jpg, it raises the exception. For
+ // this case, we will retry it with the adjustedExpiredTime again.
+ newPath = FileUtils.getAbsoluteExtendedPath(originalPath, adjustedExpiredTime);
+ Log.i(TAG, "Retrying to extend expired item with the new path = " + newPath);
+ try {
+ if (updateDatabaseForExpiredItem(db, newPath, id, adjustedExpiredTime)) {
+ return renameInLowerFsAndInvalidateFuseDentry(originalPath, newPath);
+ }
+ } catch (SQLiteConstraintException e) {
+ // If we want to rename one expired item E.g. .trashed-123-A.jpg., and there is another
+ // non-expired trashed/pending item has the same name. E.g.
+ // .trashed-adjustedExpiredTime-A.jpg. When we rename .trashed-123-A.jpg to
+ // .trashed-adjustedExpiredTime-A.jpg, it raises the SQLiteConstraintException.
+ // The smallest unit of the expired time we use is second. It is a very rare case.
+ // When this case is happened, we can handle it in next idle maintenance.
+ final String errorMessage =
+ "Update database _data from " + originalPath + " to " + newPath + " failed.";
+ Log.d(TAG, errorMessage, e);
+ }
+
+ return false;
+ }
+
+ private boolean updateDatabaseForExpiredItem(@NonNull SQLiteDatabase db,
+ @NonNull String path, long id, long expiredTime) {
+ final String table = "files";
+ final String whereClause = MediaColumns._ID + "=?";
+ final String[] whereArgs = new String[]{String.valueOf(id)};
+ final ContentValues values = new ContentValues();
+ values.put(FileColumns.DATA, path);
+ values.put(FileColumns.DATE_EXPIRES, expiredTime);
+ final int count = db.update(table, values, whereClause, whereArgs);
+ return count == 1;
+ }
+
+ private boolean renameInLowerFsAndInvalidateFuseDentry(@NonNull String originalPath,
+ @NonNull String newPath) {
+ try {
Os.rename(originalPath, newPath);
invalidateFuseDentry(originalPath);
invalidateFuseDentry(newPath);
+ return true;
} catch (ErrnoException e) {
- final String errorMessage = "Rename " + originalPath + " to " + newPath + " failed.";
+ final String errorMessage = "Rename " + originalPath + " to " + newPath
+ + " in lower file system for extending item failed.";
Log.e(TAG, errorMessage, e);
- return false;
}
-
- final ContentValues values = new ContentValues();
- values.put(FileColumns.DATA, newPath);
- values.put(FileColumns.DATE_EXPIRES, extendedTime);
- final int count = db.update("files", values, "_id=?", new String[]{String.valueOf(id)});
- return count == 1;
+ return false;
}
public void onIdleMaintenanceStopped() {
@@ -1229,33 +1624,62 @@
* Orphan any content of the given package. This will delete Android/media orphaned files from
* the database.
*/
- public void onPackageOrphaned(String packageName) {
+ public void onPackageOrphaned(String packageName, int uid) {
mExternalDatabase.runWithTransaction((db) -> {
- onPackageOrphaned(db, packageName);
+ final int userId = uid / PER_USER_RANGE;
+ onPackageOrphaned(db, packageName, userId);
return null;
});
}
/**
* Orphan any content of the given package from the given database. This will delete
- * Android/media orphaned files from the database.
+ * Android/media files from the database if the underlying file no longe exists.
*/
- public void onPackageOrphaned(@NonNull SQLiteDatabase db, @NonNull String packageName) {
- // Delete files from Android/media.
- String relativePath = "Android/media/" + DatabaseUtils.escapeForLike(packageName) + "/%";
- final int countDeleted = db.delete(
- "files",
- "relative_path LIKE ? ESCAPE '\\' AND owner_package_name=?",
- new String[] {relativePath, packageName});
- Log.d(TAG, "Deleted " + countDeleted + " Android/media items belonging to "
- + packageName + " on " + db.getPath());
+ public void onPackageOrphaned(@NonNull SQLiteDatabase db,
+ @NonNull String packageName, int userId) {
+ // Delete Android/media entries.
+ deleteAndroidMediaEntries(db, packageName, userId);
+ // Orphan rest of entries.
+ orphanEntries(db, packageName, userId);
+ }
- // Orphan rest of files.
+ private void deleteAndroidMediaEntries(SQLiteDatabase db, String packageName, int userId) {
+ String relativePath = "Android/media/" + DatabaseUtils.escapeForLike(packageName) + "/%";
+ try (Cursor cursor = db.query(
+ "files",
+ new String[] { MediaColumns._ID, MediaColumns.DATA },
+ "relative_path LIKE ? ESCAPE '\\' AND owner_package_name=? AND _user_id=?",
+ new String[] { relativePath, packageName, "" + userId },
+ /* groupBy= */ null,
+ /* having= */ null,
+ /* orderBy= */null,
+ /* limit= */ null)) {
+ int countDeleted = 0;
+ if (cursor != null) {
+ while (cursor.moveToNext()) {
+ File file = new File(cursor.getString(1));
+ // We check for existence to be sure we don't delete files that still exist.
+ // This can happen even if the pair (package, userid) is unknown,
+ // since some framework implementations may rely on special userids.
+ if (!file.exists()) {
+ countDeleted +=
+ db.delete("files", "_id=?", new String[]{cursor.getString(0)});
+ }
+ }
+ }
+ Log.d(TAG, "Deleted " + countDeleted + " Android/media items belonging to "
+ + packageName + " on " + db.getPath());
+ }
+ }
+
+ private void orphanEntries(
+ @NonNull SQLiteDatabase db, @NonNull String packageName, int userId) {
final ContentValues values = new ContentValues();
values.putNull(FileColumns.OWNER_PACKAGE_NAME);
final int countOrphaned = db.update("files", values,
- "owner_package_name=?", new String[] { packageName });
+ "owner_package_name=? AND _user_id=?", new String[] { packageName, "" + userId });
if (countOrphaned > 0) {
Log.d(TAG, "Orphaned " + countOrphaned + " items belonging to "
+ packageName + " on " + db.getPath());
@@ -1355,7 +1779,7 @@
*/
@Keep
public boolean shouldAllowLookupForFuse(int uid, int pathUserId) {
- int callingUserId = uid / PER_USER_RANGE;
+ int callingUserId = uidToUserId(uid);
if (!isCrossUserEnabled()) {
Log.d(TAG, "CrossUser not enabled. Users: " + callingUserId + " and " + pathUserId);
return false;
@@ -1458,58 +1882,52 @@
@Keep
public FileLookupResult onFileLookupForFuse(String path, int uid, int tid) {
uid = getBinderUidForFuse(uid, tid);
- if (isSyntheticFilePathForRedactedUri(path, uid)) {
- return getFileLookupResultsForRedactedUriPath(uid, path);
+ final int userId = uidToUserId(uid);
+
+ if (isSyntheticPath(path, userId)) {
+ if (isRedactedPath(path, userId)) {
+ return handleRedactedFileLookup(uid, path);
+ } else if (isPickerPath(path, userId)) {
+ return handlePickerFileLookup(userId, uid, path);
+ }
+
+ throw new IllegalStateException("Unexpected synthetic path: " + path);
}
- String ioPath = "";
- boolean transformsComplete = true;
- boolean transformsSupported = mTranscodeHelper.supportsTranscode(path);
- int transforms = 0;
- int transformsReason = 0;
-
- if (transformsSupported) {
- PendingOpenInfo info = null;
- synchronized (mPendingOpenInfo) {
- info = mPendingOpenInfo.get(tid);
- }
-
- if (info != null && info.uid == uid) {
- transformsReason = info.transcodeReason;
- } else {
- transformsReason = mTranscodeHelper.shouldTranscode(path, uid, null /* bundle */);
- }
-
- if (transformsReason > 0) {
- ioPath = mTranscodeHelper.getIoPath(path, uid);
- transformsComplete = mTranscodeHelper.isTranscodeFileCached(path, ioPath);
- transforms = FLAG_TRANSFORM_TRANSCODING;
- }
+ if (mTranscodeHelper.supportsTranscode(path)) {
+ return handleTranscodedFileLookup(path, uid, tid);
}
- return new FileLookupResult(transforms, transformsReason, uid, transformsComplete,
- transformsSupported, ioPath);
+ return new FileLookupResult(/* transforms */ 0, uid, /* ioPath */ "");
}
- private boolean isSyntheticFilePathForRedactedUri(String path, int uid) {
- if (path == null) return false;
+ private FileLookupResult handleTranscodedFileLookup(String path, int uid, int tid) {
+ final int transformsReason;
+ final PendingOpenInfo info;
- final String transformsSyntheticDir = getStorageRootPathForUid(uid) + "/"
- + REDACTED_URI_DIR;
- final String fileName = extractFileName(path);
- return fileName != null && path.toLowerCase(Locale.ROOT).startsWith(
- transformsSyntheticDir.toLowerCase(Locale.ROOT)) && fileName.startsWith(
- REDACTED_URI_ID_PREFIX) && fileName.length() == REDACTED_URI_ID_SIZE;
+ synchronized (mPendingOpenInfo) {
+ info = mPendingOpenInfo.get(tid);
+ }
+
+ if (info != null && info.uid == uid) {
+ transformsReason = info.transcodeReason;
+ } else {
+ transformsReason = mTranscodeHelper.shouldTranscode(path, uid, null /* bundle */);
+ }
+
+ if (transformsReason > 0) {
+ final String ioPath = mTranscodeHelper.prepareIoPath(path, uid);
+ final boolean transformsComplete = mTranscodeHelper.isTranscodeFileCached(path, ioPath);
+
+ return new FileLookupResult(FLAG_TRANSFORM_TRANSCODING, transformsReason, uid,
+ transformsComplete, /* transformsSupported */ true, ioPath);
+ }
+
+ return new FileLookupResult(/* transforms */ 0, transformsReason, uid,
+ /* transformsComplete */ true, /* transformsSupported */ true, "");
}
- private boolean isSyntheticDirPath(String path, int uid) {
- final String transformsSyntheticDir = getStorageRootPathForUid(uid) + "/"
- + TRANSFORMS_SYNTHETIC_DIR;
- return path != null && path.toLowerCase(Locale.ROOT).startsWith(
- transformsSyntheticDir.toLowerCase(Locale.ROOT));
- }
-
- private FileLookupResult getFileLookupResultsForRedactedUriPath(int uid, @NonNull String path) {
+ private FileLookupResult handleRedactedFileLookup(int uid, @NonNull String path) {
final LocalCallingIdentity token = clearLocalCallingIdentity();
final String fileName = extractFileName(path);
@@ -1524,17 +1942,153 @@
(db) -> db.query("files", new String[]{MediaColumns.DATA},
FileColumns.REDACTED_URI_ID + "=?", new String[]{fileName}, null, null,
null))) {
- if (!c.moveToFirst()) {
- return new FileLookupResult(FLAG_TRANSFORM_REDACTION, 0, uid, false, true, null);
+ if (c.moveToFirst()) {
+ return new FileLookupResult(FLAG_TRANSFORM_REDACTION, uid, c.getString(0));
}
- return new FileLookupResult(FLAG_TRANSFORM_REDACTION, 0, uid, true, true,
- c.getString(0));
+ throw new IllegalStateException("Failed to fetch synthetic redacted path: " + path);
} finally {
restoreLocalCallingIdentity(token);
}
}
+ private FileLookupResult handlePickerFileLookup(int userId, int uid, @NonNull String path) {
+ final File file = new File(path);
+ final List<String> syntheticRelativePathSegments =
+ extractSyntheticRelativePathSegements(path, userId);
+ final int segmentCount = syntheticRelativePathSegments.size();
+
+ if (segmentCount < 1 || segmentCount > 5) {
+ throw new IllegalStateException("Unexpected synthetic picker path: " + file);
+ }
+
+ final String lastSegment = syntheticRelativePathSegments.get(segmentCount - 1);
+
+ boolean result = false;
+ switch (segmentCount) {
+ case 1:
+ // .../picker
+ if (lastSegment.equals("picker")) {
+ result = file.exists() || file.mkdir();
+ }
+ break;
+ case 2:
+ // .../picker/<user-id>
+ try {
+ Integer.parseInt(lastSegment);
+ result = file.exists() || file.mkdir();
+ } catch (NumberFormatException e) {
+ Log.w(TAG, "Invalid user id for picker file lookup: " + lastSegment
+ + ". File: " + file);
+ }
+ break;
+ case 3:
+ // .../picker/<user-id>/<authority>
+ result = preparePickerAuthorityPathSegment(file, lastSegment, uid);
+ break;
+ case 4:
+ // .../picker/<user-id>/<authority>/media
+ if (lastSegment.equals("media")) {
+ result = file.exists() || file.mkdir();
+ }
+ break;
+ case 5:
+ // .../picker/<user-id>/<authority>/media/<media-id.extension>
+ final String fileUserId = syntheticRelativePathSegments.get(1);
+ final String authority = syntheticRelativePathSegments.get(2);
+ result = preparePickerMediaIdPathSegment(file, authority, lastSegment, fileUserId);
+ break;
+ }
+
+ if (result) {
+ return new FileLookupResult(FLAG_TRANSFORM_PICKER, uid, path);
+ }
+ throw new IllegalStateException("Failed to prepare synthetic picker path: " + file);
+ }
+
+ private FileOpenResult handlePickerFileOpen(String path, int uid) {
+ final String[] segments = path.split("/");
+ if (segments.length != 11) {
+ Log.e(TAG, "Picker file open failed. Unexpected segments: " + path);
+ return new FileOpenResult(OsConstants.ENOENT /* status */, uid, /* transformsUid */ 0,
+ new long[0]);
+ }
+
+ // ['', 'storage', 'emulated', '0', 'transforms', 'synthetic', 'picker', '<user-id>',
+ // '<host>', 'media', '<fileName>']
+ final String userId = segments[7];
+ final String fileName = segments[10];
+ final String host = segments[8];
+ final String authority = userId + "@" + host;
+ final int lastDotIndex = fileName.lastIndexOf('.');
+
+ if (lastDotIndex == -1) {
+ Log.e(TAG, "Picker file open failed. No file extension: " + path);
+ return FileOpenResult.createError(OsConstants.ENOENT, uid);
+ }
+
+ final String mediaId = fileName.substring(0, lastDotIndex);
+ final Uri uri = getMediaUri(authority).buildUpon().appendPath(mediaId).build();
+
+ IBinder binder = getContext().getContentResolver()
+ .call(uri, METHOD_GET_ASYNC_CONTENT_PROVIDER, null, null)
+ .getBinder(EXTRA_ASYNC_CONTENT_PROVIDER);
+ if (binder == null) {
+ Log.e(TAG, "Picker file open failed. No cloud media provider found.");
+ return FileOpenResult.createError(OsConstants.ENOENT, uid);
+ }
+ IAsyncContentProvider iAsyncContentProvider = IAsyncContentProvider.Stub.asInterface(
+ binder);
+ AsyncContentProvider asyncContentProvider = new AsyncContentProvider(iAsyncContentProvider);
+ final ParcelFileDescriptor pfd;
+ try {
+ pfd = asyncContentProvider.openMedia(uri, "r");
+ } catch (FileNotFoundException | ExecutionException | InterruptedException
+ | TimeoutException | RemoteException e) {
+ Log.e(TAG, "Picker file open failed. Failed to open URI: " + uri, e);
+ return FileOpenResult.createError(OsConstants.ENOENT, uid);
+ }
+
+ try (FileInputStream fis = new FileInputStream(pfd.getFileDescriptor())) {
+ final String mimeType = MimeUtils.resolveMimeType(new File(path));
+ final long[] redactionRanges = getRedactionRanges(fis, mimeType).redactionRanges;
+ return new FileOpenResult(0 /* status */, uid, /* transformsUid */ 0,
+ /* nativeFd */ pfd.detachFd(), redactionRanges);
+ } catch (IOException e) {
+ Log.e(TAG, "Picker file open failed. No file extension: " + path, e);
+ return FileOpenResult.createError(OsConstants.ENOENT, uid);
+ }
+ }
+
+ private boolean preparePickerAuthorityPathSegment(File file, String authority, int uid) {
+ if (mPickerSyncController.isProviderEnabled(authority)) {
+ return file.exists() || file.mkdir();
+ }
+
+ return false;
+ }
+
+ private boolean preparePickerMediaIdPathSegment(File file, String authority, String fileName,
+ String userId) {
+ final String mediaId = extractFileName(fileName);
+ final String[] projection = new String[] { MediaStore.PickerMediaColumns.SIZE };
+
+ final Uri uri = Uri.parse("content://media/picker/" + userId + "/" + authority + "/media/"
+ + mediaId);
+ try (Cursor cursor = mPickerUriResolver.query(uri, projection, /* callingUid */0,
+ android.os.Process.myUid())) {
+ if (cursor != null && cursor.moveToFirst()) {
+ final int sizeBytesIdx = cursor.getColumnIndex(MediaStore.PickerMediaColumns.SIZE);
+
+ if (sizeBytesIdx != -1) {
+ return createSparseFile(file, cursor.getLong(sizeBytesIdx));
+ }
+ }
+ }
+
+ return false;
+ }
+
public int getBinderUidForFuse(int uid, int tid) {
if (uid != MY_UID) {
return uid;
@@ -1549,6 +2103,10 @@
}
}
+ private static int uidToUserId(int uid) {
+ return uid / PER_USER_RANGE;
+ }
+
/**
* Returns true if the app denoted by the given {@code uid} and {@code packageName} is allowed
* to clear other apps' cache directories.
@@ -1993,7 +2551,7 @@
supportedPrimaryMimeType = ClipDescription.MIMETYPE_UNKNOWN;
}
return (supportedPrimaryMimeType.equalsIgnoreCase(ClipDescription.MIMETYPE_UNKNOWN) ||
- MimeUtils.startsWithIgnoreCase(mimeType, supportedPrimaryMimeType));
+ StringUtils.startsWithIgnoreCase(mimeType, supportedPrimaryMimeType));
}
/**
@@ -2084,12 +2642,7 @@
}
if (retryUpdateWithReplace) {
- // We are replacing file in newPath with file in oldPath. If calling package has
- // write permission for newPath, delete existing database entry and retry update.
- final Uri uriNewPath = FileUtils.getContentUriForPath(oldPath);
- final SQLiteQueryBuilder qbForDelete = getQueryBuilder(TYPE_DELETE,
- matchUri(uriNewPath, allowHidden), uriNewPath, qbExtras, null);
- if (qbForDelete.delete(helper, selection, new String[] {newPath}) == 1) {
+ if (deleteForFuseRename(helper, oldPath, newPath, qbExtras, selection, allowHidden)) {
Log.i(TAG, "Retrying database update after deleting conflicting entry");
count = qbForUpdate.update(helper, values, selection, new String[]{oldPath});
} else {
@@ -2099,6 +2652,30 @@
return count == 1;
}
+ private boolean deleteForFuseRename(DatabaseHelper helper, String oldPath,
+ String newPath, Bundle qbExtras, String selection, boolean allowHidden) {
+ // We are replacing file in newPath with file in oldPath. If calling package has
+ // write permission for newPath, delete existing database entry and retry update.
+ final Uri uriNewPath = FileUtils.getContentUriForPath(oldPath);
+ final SQLiteQueryBuilder qbForDelete = getQueryBuilder(TYPE_DELETE,
+ matchUri(uriNewPath, allowHidden), uriNewPath, qbExtras, null);
+ if (qbForDelete.delete(helper, selection, new String[] {newPath}) == 1) {
+ return true;
+ }
+ // Check if delete can be done using other URI grants
+ final String[] projection = new String[] {
+ FileColumns.MEDIA_TYPE,
+ FileColumns.DATA,
+ FileColumns._ID,
+ FileColumns.IS_DOWNLOAD,
+ FileColumns.MIME_TYPE,
+ };
+ return
+ deleteWithOtherUriGrants(
+ FileUtils.getContentUriForPath(newPath),
+ helper, projection, selection, new String[] {newPath}, qbExtras) == 1;
+ }
+
/**
* Gets {@link ContentValues} for updating database entry to {@code path}.
*/
@@ -2305,8 +2882,8 @@
final Bundle qbExtras = new Bundle();
qbExtras.putStringArrayList(INCLUDED_DEFAULT_DIRECTORIES,
getIncludedDefaultDirectories());
- final boolean wasHidden = FileUtils.isDirectoryHidden(new File(oldPath));
- final boolean isHidden = FileUtils.isDirectoryHidden(new File(newPath));
+ final boolean wasHidden = FileUtils.shouldDirBeHidden(new File(oldPath));
+ final boolean isHidden = FileUtils.shouldDirBeHidden(new File(newPath));
for (String filePath : fileList) {
final String newFilePath = newPath + "/" + filePath;
final String mimeType = MimeUtils.resolveMimeType(new File(newFilePath));
@@ -2364,21 +2941,6 @@
return renameFileForFuse(oldPath, newPath, /* bypassRestrictions */ true) ;
}
- private static boolean shouldFileBeHidden(@NonNull File file) {
- if (FileUtils.isFileHidden(file)) {
- return true;
- }
- File parent = file.getParentFile();
- while (parent != null) {
- if (FileUtils.isDirectoryHidden(parent)) {
- return true;
- }
- parent = parent.getParentFile();
- }
-
- return false;
- }
-
private int renameFileForFuse(String oldPath, String newPath, boolean bypassRestrictions) {
final DatabaseHelper helper;
try {
@@ -2387,16 +2949,15 @@
throw new IllegalStateException("Failed to update database row with " + oldPath, e);
}
- final boolean wasHidden = shouldFileBeHidden(new File(oldPath));
- final boolean isHidden = shouldFileBeHidden(new File(newPath));
+ final boolean wasHidden = FileUtils.shouldFileBeHidden(new File(oldPath));
+ final boolean isHidden = FileUtils.shouldFileBeHidden(new File(newPath));
helper.beginTransaction();
try {
final String newMimeType = MimeUtils.resolveMimeType(new File(newPath));
final String oldMimeType = MimeUtils.resolveMimeType(new File(oldPath));
final boolean isSameMimeType = newMimeType.equalsIgnoreCase(oldMimeType);
- final ContentValues contentValues = getContentValuesForFuseRename(newPath, newMimeType,
+ ContentValues contentValues = getContentValuesForFuseRename(newPath, newMimeType,
wasHidden, isHidden, isSameMimeType);
-
if (!updateDatabaseForFuseRename(helper, oldPath, newPath, contentValues)) {
if (!bypassRestrictions) {
// Check for other URI format grants for oldPath only. Check right before
@@ -2505,7 +3066,8 @@
return OsConstants.EPERM;
}
- if (shouldBypassDatabaseAndSetDirtyForFuse(uid, newPath)) {
+ if (shouldBypassDatabaseAndSetDirtyForFuse(uid, oldPath)
+ && shouldBypassDatabaseAndSetDirtyForFuse(uid, newPath)) {
return renameInLowerFs(oldPath, newPath);
}
@@ -2589,8 +3151,8 @@
final LocalCallingIdentity token = clearLocalCallingIdentity(
LocalCallingIdentity.fromExternal(getContext(), mUserCache, uid));
- if(isRedactedUri(uri)) {
- if((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+ if (isRedactedUri(uri)) {
+ if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
// we don't allow write grants on redacted uris.
return PackageManager.PERMISSION_DENIED;
}
@@ -2598,6 +3160,15 @@
uri = getUriForRedactedUri(uri);
}
+ if (isPickerUri(uri)) {
+ // Do not allow implicit access (by the virtue of ownership/permission) to picker uris.
+ // Picker uris should have explicit permission grants.
+ // If the calling app A has an explicit grant on picker uri, UriGrantsManagerService
+ // will check the grant status and allow app A to grant the uri to app B (without
+ // calling into MediaProvider)
+ return PackageManager.PERMISSION_DENIED;
+ }
+
try {
final boolean allowHidden = isCallingPackageAllowedHidden();
final int table = matchUri(uri, allowHidden);
@@ -2610,13 +3181,10 @@
}
final int type;
- final boolean forWrite;
if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
type = TYPE_UPDATE;
- forWrite = true;
} else {
type = TYPE_QUERY;
- forWrite = false;
}
final SQLiteQueryBuilder qb = getQueryBuilder(type, table, uri, Bundle.EMPTY, null);
@@ -2699,6 +3267,11 @@
private Cursor queryInternal(Uri uri, String[] projection, Bundle queryArgs,
CancellationSignal signal, boolean forSelf) throws FallbackException {
+ if (isPickerUri(uri)) {
+ return mPickerUriResolver.query(uri, projection, mCallingIdentity.get().pid,
+ mCallingIdentity.get().uid);
+ }
+
final String volumeName = getVolumeName(uri);
PulledMetrics.logVolumeAccessViaMediaProvider(getCallingUidOrSelf(), volumeName);
queryArgs = (queryArgs != null) ? queryArgs : new Bundle();
@@ -2748,6 +3321,13 @@
return c;
}
+ // TODO(b/195008831): Add test to verify that apps can't access
+ if (table == PICKER_INTERNAL_MEDIA) {
+ return mPickerDataLayer.fetchMedia(queryArgs);
+ } else if (table == PICKER_INTERNAL_ALBUMS) {
+ return mPickerDataLayer.fetchAlbums(queryArgs);
+ }
+
final DatabaseHelper helper = getDatabaseForUri(uri);
final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_QUERY, table, uri, queryArgs,
honoredArgs::add);
@@ -2810,7 +3390,7 @@
}
// Update locale if necessary.
- if (helper == mInternalDatabase && !Locale.getDefault().equals(mLastLocale)) {
+ if (helper.isInternal() && !Locale.getDefault().equals(mLastLocale)) {
Log.i(TAG, "Updating locale within queryInternal");
onLocaleChanged(false);
}
@@ -2876,13 +3456,13 @@
String ext = getFileExtensionFromCursor(c, columnNames);
ext = ext == null ? "" : "." + ext;
final String displayName = redactedUriId + ext;
- final String data = getPathForRedactedUriId(displayName);
-
+ final String data = buildPrimaryVolumeFile(uidToUserId(Binder.getCallingUid()),
+ getRedactedRelativePath(), displayName).getAbsolutePath();
updateRow(columnNames, MediaColumns._ID, row, redactedUriId);
updateRow(columnNames, MediaColumns.DISPLAY_NAME, row, displayName);
- updateRow(columnNames, MediaColumns.RELATIVE_PATH, row, REDACTED_URI_DIR);
- updateRow(columnNames, MediaColumns.BUCKET_DISPLAY_NAME, row, REDACTED_URI_DIR);
+ updateRow(columnNames, MediaColumns.RELATIVE_PATH, row, getRedactedRelativePath());
+ updateRow(columnNames, MediaColumns.BUCKET_DISPLAY_NAME, row, getRedactedRelativePath());
updateRow(columnNames, MediaColumns.DATA, row, data);
updateRow(columnNames, MediaColumns.DOCUMENT_ID, row, null);
updateRow(columnNames, MediaColumns.INSTANCE_ID, row, null);
@@ -2903,15 +3483,6 @@
return null;
}
- static private String getPathForRedactedUriId(@NonNull String displayName) {
- return getStorageRootPathForUid(Binder.getCallingUid()) + "/" + REDACTED_URI_DIR + "/"
- + displayName;
- }
-
- static private String getStorageRootPathForUid(int uid) {
- return "/storage/emulated/" + (uid / PER_USER_RANGE);
- }
-
private void updateRow(HashSet<String> columnNames, String columnName,
MatrixCursor.RowBuilder row, Object val) {
if (columnNames.contains(columnName)) {
@@ -3003,6 +3574,9 @@
return Video.Media.CONTENT_TYPE;
case DOWNLOADS:
return Downloads.CONTENT_TYPE;
+
+ case PICKER_ID:
+ return mPickerUriResolver.getType(url);
}
throw new IllegalStateException("Unknown URL : " + url);
}
@@ -3266,9 +3840,23 @@
// Generate path when undefined
if (TextUtils.isEmpty(values.getAsString(MediaColumns.DATA))) {
+ // Note that just the volume name isn't enough to determine the path,
+ // since we can manage different volumes with the same name for
+ // different users. Instead, if we have a current path (which implies
+ // an already existing file to be renamed), use that to derive the
+ // user-id of the file, and in turn use that to derive the correct
+ // volume. Cross-user renames are not supported without a specified
+ // DATA column.
File volumePath;
+ UserHandle userHandle = mCallingIdentity.get().getUser();
+ if (currentPath != null) {
+ int userId = FileUtils.extractUserId(currentPath);
+ if (userId != -1) {
+ userHandle = UserHandle.of(userId);
+ }
+ }
try {
- volumePath = getVolumePath(resolvedVolumeName);
+ volumePath = mVolumeCache.getVolumePath(resolvedVolumeName, userHandle);
} catch (FileNotFoundException e) {
throw new IllegalArgumentException(e);
}
@@ -3360,8 +3948,8 @@
// gallery is not allowed to create non-default top level directory.
final boolean createNonDefaultTopLevelDir = primary != null &&
!FileUtils.buildPath(volumePath, primary).exists();
- validPath = !createNonDefaultTopLevelDir &&
- canAccessMediaFile(res.getAbsolutePath(), /*allowLegacy*/ false);
+ validPath = !createNonDefaultTopLevelDir && canAccessMediaFile(
+ res.getAbsolutePath(), /*excludeNonSystemGallery*/ true);
}
// Nothing left to check; caller can't use this path
@@ -3482,7 +4070,7 @@
if (path != null) {
// Touch root of volume to update mTime on FUSE filesystem
// This allows FileManagers that may be relying on mTime changes to update their UI
- File fusePath = getFuseFile(path);
+ File fusePath = toFuseFile(path);
if (fusePath != null) {
Log.i(TAG, "Touching FUSE path " + fusePath);
fusePath.setLastModified(System.currentTimeMillis());
@@ -3829,7 +4417,8 @@
if (isCallingPackageSelf() && values.containsKey(FileColumns.MEDIA_TYPE)) {
// Leave FileColumns.MEDIA_TYPE untouched if the caller is ModernMediaScanner and
// FileColumns.MEDIA_TYPE is already populated.
- } else if (isFuseThread() && path != null && shouldFileBeHidden(new File(path))) {
+ } else if (isFuseThread() && path != null
+ && FileUtils.shouldFileBeHidden(new File(path))) {
// We should only mark MEDIA_TYPE as MEDIA_TYPE_NONE for Fuse Thread.
// MediaProvider#insert() returns the uri by appending the "rowId" to the given
// uri, hence to ensure the correct working of the returned uri, we shouldn't
@@ -3856,7 +4445,7 @@
// So we only set the user_id field in the database for external storage.
qb.allowColumn(FileColumns._USER_ID);
int ownerUserId = FileUtils.extractUserId(path);
- if (!helper.mInternal) {
+ if (helper.isExternal()) {
if (isAppCloneUserForFuse(ownerUserId)) {
values.put(FileColumns._USER_ID, ownerUserId);
} else {
@@ -3895,7 +4484,8 @@
// Checking if the file/directory is hidden can be expensive based on the depth of
// the directory tree. Call shouldFileBeHidden() only when the caller of insert()
// cares about returned uri.
- if (!isCallingPackageSelf() && !isFuseThread() && shouldFileBeHidden(file)) {
+ if (!isCallingPackageSelf() && !isFuseThread()
+ && FileUtils.shouldFileBeHidden(file)) {
newUri = MediaStore.Files.getContentUri(MediaStore.getVolumeName(uri));
}
}
@@ -4241,7 +4831,7 @@
}
case IMAGES_THUMBNAILS: {
- if (helper.mInternal) {
+ if (helper.isInternal()) {
throw new UnsupportedOperationException(
"Writing to internal storage is not supported.");
}
@@ -4263,7 +4853,7 @@
}
case VIDEO_THUMBNAILS: {
- if (helper.mInternal) {
+ if (helper.isInternal()) {
throw new UnsupportedOperationException(
"Writing to internal storage is not supported.");
}
@@ -4342,7 +4932,7 @@
}
case AUDIO_ALBUMART: {
- if (helper.mInternal) {
+ if (helper.isInternal()) {
throw new UnsupportedOperationException("no internal album art allowed");
}
@@ -4772,6 +5362,17 @@
// owner, so callers need to hold READ_MEDIA_AUDIO
appendWhereStandalone(qb, "0");
}
+ // In order to be consistent with other audio views like audio_artist, audio_albums,
+ // and audio_genres, exclude pending and trashed item
+ appendWhereStandaloneMatch(qb, FileColumns.IS_PENDING, MATCH_EXCLUDE, uri);
+ appendWhereStandaloneMatch(qb, FileColumns.IS_TRASHED, MATCH_EXCLUDE, uri);
+ appendWhereStandaloneMatch(qb, FileColumns.IS_FAVORITE, matchFavorite, uri);
+ if (honored != null) {
+ honored.accept(QUERY_ARG_MATCH_FAVORITE);
+ }
+ if (!includeAllVolumes) {
+ appendWhereStandalone(qb, FileColumns.VOLUME_NAME + " IN " + includeVolumes);
+ }
break;
}
case AUDIO_PLAYLISTS_ID:
@@ -5378,9 +5979,8 @@
private int deleteWithOtherUriGrants(@NonNull Uri uri, DatabaseHelper helper,
String[] projection, String userWhere, String[] userWhereArgs,
@Nullable Bundle extras) {
- try {
- Cursor c = queryForSingleItemAsMediaProvider(uri, projection, userWhere, userWhereArgs,
- null);
+ try (Cursor c = queryForSingleItemAsMediaProvider(uri, projection, userWhere, userWhereArgs,
+ null)) {
final int mediaType = c.getInt(0);
final String data = c.getString(1);
final long id = c.getLong(2);
@@ -5545,13 +6145,26 @@
return null;
}
case MediaStore.WAIT_FOR_IDLE_CALL: {
+ // TODO(b/195009139): Remove after overriding wait for idle in test to sync picker
+ // Syncing the picker while waiting for idle fixes tests with the picker db
+ // flag enabled because the picker db is in a consistent state with the external
+ // db after the sync
+ syncAllMedia();
ForegroundThread.waitForIdle();
- BackgroundThread.waitForIdle();
+ final CountDownLatch latch = new CountDownLatch(1);
+ BackgroundThread.getExecutor().execute(() -> {
+ latch.countDown();
+ });
+ try {
+ latch.await(30, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new IllegalStateException(e);
+ }
return null;
}
case MediaStore.SCAN_FILE_CALL:
case MediaStore.SCAN_VOLUME_CALL: {
- final int userId = Binder.getCallingUid() / PER_USER_RANGE;
+ final int userId = uidToUserId(Binder.getCallingUid());
final LocalCallingIdentity token = clearLocalCallingIdentity();
final CallingIdentity providerToken = clearCallingIdentity();
try {
@@ -5727,16 +6340,86 @@
} finally {
restoreLocalCallingIdentity(token);
}
+ case MediaStore.SET_CLOUD_PROVIDER_CALL: {
+ // TODO(b/190713331): Remove after initial development
+ final String cloudProvider = extras.getString(MediaStore.EXTRA_CLOUD_PROVIDER);
+ Log.i(TAG, "Test initiated cloud provider switch: " + cloudProvider);
+ mPickerSyncController.forceSetCloudProvider(cloudProvider);
+ // fall-through
+ }
+ case MediaStore.SYNC_PROVIDERS_CALL: {
+ syncAllMedia();
+ return new Bundle();
+ }
+ case MediaStore.IS_SUPPORTED_CLOUD_PROVIDER_CALL: {
+ final boolean isSupported = mPickerSyncController.isProviderSupported(arg,
+ Binder.getCallingUid());
+
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(MediaStore.EXTRA_CLOUD_PROVIDER_RESULT, isSupported);
+ return bundle;
+ }
+ case MediaStore.IS_CURRENT_CLOUD_PROVIDER_CALL: {
+ final boolean isEnabled = mPickerSyncController.isProviderEnabled(arg,
+ Binder.getCallingUid());
+
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(MediaStore.EXTRA_CLOUD_PROVIDER_RESULT, isEnabled);
+ return bundle;
+ }
+ case MediaStore.NOTIFY_CLOUD_MEDIA_CHANGED_EVENT_CALL: {
+ final boolean notifyCloudEventResult;
+ if (mPickerSyncController.isProviderEnabled(arg, Binder.getCallingUid())) {
+ mPickerSyncController.notifyMediaEvent();
+ notifyCloudEventResult = true;
+ } else {
+ notifyCloudEventResult = false;
+ }
+
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(MediaStore.EXTRA_CLOUD_PROVIDER_RESULT,
+ notifyCloudEventResult);
+ return bundle;
+ }
+ case MediaStore.USES_FUSE_PASSTHROUGH: {
+ boolean isEnabled = false;
+ try {
+ FuseDaemon daemon = getFuseDaemonForFile(new File(arg));
+ if (daemon != null) {
+ isEnabled = daemon.usesFusePassthrough();
+ }
+ } catch (FileNotFoundException e) {
+ }
+
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(MediaStore.USES_FUSE_PASSTHROUGH_RESULT, isEnabled);
+ return bundle;
+ }
default:
throw new UnsupportedOperationException("Unsupported call: " + method);
}
}
+ private void syncAllMedia() {
+ // Clear the binder calling identity so that we can sync the unexported
+ // local_provider while running as MediaProvider
+ final long t = Binder.clearCallingIdentity();
+ try {
+ Log.v(TAG, "Test initiated cloud provider sync");
+ mPickerSyncController.syncAllMedia();
+ } finally {
+ Binder.restoreCallingIdentity(t);
+ }
+ }
+
private AssetFileDescriptor getOriginalMediaFormatFileDescriptor(Bundle extras)
throws FileNotFoundException {
try (ParcelFileDescriptor inputPfd =
extras.getParcelable(MediaStore.EXTRA_FILE_DESCRIPTOR)) {
- final File file = getFileFromFileDescriptor(inputPfd);
+ File file = getFileFromFileDescriptor(inputPfd);
+ // Convert from FUSE file to lower fs file because the supportsTranscode() check below
+ // expects a lower fs file format
+ file = fromFuseFile(file);
if (!mTranscodeHelper.supportsTranscode(file.getPath())) {
// Note that we should be checking if a file is a modern format and not just
// that it supports transcoding, unfortunately, checking modern format
@@ -5747,22 +6430,25 @@
}
FuseDaemon fuseDaemon = getFuseDaemonForFile(file);
- String outputPath = fuseDaemon.getOriginalMediaFormatFilePath(inputPfd);
- if (TextUtils.isEmpty(outputPath)) {
+ int uid = Binder.getCallingUid();
+
+ FdAccessResult result = fuseDaemon.checkFdAccess(inputPfd, uid);
+ if (!result.isSuccess()) {
throw new FileNotFoundException("Invalid path for original media format file");
}
+ String outputPath = result.filePath;
+ boolean shouldRedact = result.shouldRedact;
+
int posixMode = Os.fcntlInt(inputPfd.getFileDescriptor(), F_GETFL,
0 /* args */);
int modeBits = FileUtils.translateModePosixToPfd(posixMode);
- int uid = Binder.getCallingUid();
ParcelFileDescriptor pfd = openWithFuse(outputPath, uid, 0 /* mediaCapabilitiesUid */,
- modeBits, true /* shouldRedact */, false /* shouldTranscode */,
+ modeBits, shouldRedact, false /* shouldTranscode */,
0 /* transcodeReason */);
return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH);
} catch (IOException e) {
- Log.w(TAG, "Failed to fetch original file descriptor", e);
throw new FileNotFoundException("Failed to fetch original file descriptor");
} catch (ErrnoException e) {
Log.w(TAG, "Failed to fetch access mode for file descriptor", e);
@@ -5807,6 +6493,9 @@
* Return the filesystem path of the real file on disk that is represented
* by the given {@link ParcelFileDescriptor}.
*
+ * Note that the file may be a FUSE or lower fs file and depending on the purpose might need
+ * to be converted with {@link FileUtils#toFuseFile} or {@link FileUtils#fromFuseFile}.
+ *
* Copied from {@link ParcelFileDescriptor#getFile}
*/
private static File getFileFromFileDescriptor(ParcelFileDescriptor fileDescriptor)
@@ -6488,7 +7177,8 @@
} else if (!Objects.equals(beforeVolume, probeVolume)) {
throw new IllegalArgumentException("Changing volume from " + beforePath + " to "
+ probePath + " not allowed");
- } else if (!Objects.equals(beforeOwner, probeOwner)) {
+ } else if (!isUpdateAllowedForOwnedPath(beforeOwner, probeOwner, beforePath,
+ probePath)) {
throw new IllegalArgumentException("Changing ownership from " + beforePath + " to "
+ probePath + " not allowed");
} else {
@@ -6674,6 +7364,43 @@
return count;
}
+ private boolean isUpdateAllowedForOwnedPath(@Nullable String srcOwner,
+ @Nullable String destOwner, @NonNull String srcPath, @NonNull String destPath) {
+ // 1. Allow if the update is within owned path
+ // update() from /sdcard/Android/media/com.foo/ABC/image.jpeg to
+ // /sdcard/Android/media/com.foo/XYZ/image.jpeg - Allowed
+ if(Objects.equals(srcOwner, destOwner)) {
+ return true;
+ }
+
+ // 2. Check if the calling package is a special app which has global access
+ if (isCallingPackageManager() ||
+ (canAccessMediaFile(srcPath, /* excludeNonSystemGallery */ true) &&
+ (canAccessMediaFile(destPath, /* excludeNonSystemGallery */ true)))) {
+ return true;
+ }
+
+ // 3. Allow update from srcPath if the source is not a owned path or calling package is the
+ // owner of the source path or calling package shares the UID with the owner of the source
+ // path
+ // update() from /sdcard/DCIM/Foo.jpeg - Allowed
+ // update() from /sdcard/Android/media/com.foo/image.jpeg - Allowed for
+ // callingPackage=com.foo, not allowed for callingPackage=com.bar
+ final boolean isSrcUpdateAllowed = srcOwner == null
+ || isCallingIdentitySharedPackageName(srcOwner);
+
+ // 4. Allow update to dstPath if the destination is not a owned path or calling package is
+ // the owner of the destination path or calling package shares the UID with the owner of the
+ // destination path
+ // update() to /sdcard/Pictures/image.jpeg - Allowed
+ // update() to /sdcard/Android/media/com.foo/image.jpeg - Allowed for
+ // callingPackage=com.foo, not allowed for callingPackage=com.bar
+ final boolean isDestUpdateAllowed = destOwner == null
+ || isCallingIdentitySharedPackageName(destOwner);
+
+ return isSrcUpdateAllowed && isDestUpdateAllowed;
+ }
+
private void notifyTranscodeHelperOnUriPublished(Uri uri) {
BackgroundThread.getExecutor().execute(() -> {
final LocalCallingIdentity token = clearLocalCallingIdentity();
@@ -7067,6 +7794,18 @@
return -1;
}
+ 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;
+ }
+
+ public boolean isPickerUnreliableVolumeUri(Uri uri, boolean allowHidden) {
+ final int match = matchUri(uri, allowHidden);
+ return match == PICKER_UNRELIABLE_VOLUME;
+ }
+
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
return openFileCommon(uri, mode, /*signal*/ null, /*opts*/ null);
@@ -7090,6 +7829,12 @@
}
uri = safeUncanonicalize(uri);
+ if (isPickerUri(uri)) {
+ final int callingPid = mCallingIdentity.get().pid;
+ final int callingUid = mCallingIdentity.get().uid;
+ return mPickerUriResolver.openFile(uri, mode, signal, callingPid, callingUid);
+ }
+
final boolean allowHidden = isCallingPackageAllowedHidden();
final int match = matchUri(uri, allowHidden);
final String volumeName = getVolumeName(uri);
@@ -7145,7 +7890,16 @@
private AssetFileDescriptor openTypedAssetFileCommon(Uri uri, String mimeTypeFilter,
Bundle opts, CancellationSignal signal) throws FileNotFoundException {
- uri = safeUncanonicalize(uri);
+ final boolean wantsThumb = (opts != null) && opts.containsKey(ContentResolver.EXTRA_SIZE)
+ && StringUtils.startsWithIgnoreCase(mimeTypeFilter, "image/");
+ String mode = "r";
+
+ // If request is not for thumbnail and arising from MediaProvider, then check for EXTRA_MODE
+ if (opts != null && !wantsThumb && isCallingPackageSelf()) {
+ mode = opts.getString(MediaStore.EXTRA_MODE, "r");
+ } else if (opts != null) {
+ opts.remove(MediaStore.EXTRA_MODE);
+ }
if (opts != null && opts.containsKey(MediaStore.EXTRA_FILE_DESCRIPTOR)) {
// This is called as part of MediaStore#getOriginalMediaFormatFileDescriptor
@@ -7159,21 +7913,35 @@
// level from #3
// 5. Return the fd from #4 to the app or throw an exception if any of the conditions
// are not met
- return getOriginalMediaFormatFileDescriptor(opts);
+ try {
+ return getOriginalMediaFormatFileDescriptor(opts);
+ } finally {
+ // Clearing the Bundle closes the underlying Parcel, ensuring that the input fd
+ // owned by the Parcel is closed immediately and not at the next GC.
+ // This works around a change in behavior introduced by:
+ // aosp/Icfe8880cad00c3cd2afcbe4b92400ad4579e680e
+ opts.clear();
+ }
+ }
+
+ // This is needed for thumbnail resolution as it doesn't go through openFileCommon
+ if (isPickerUri(uri)) {
+ final int callingPid = mCallingIdentity.get().pid;
+ final int callingUid = mCallingIdentity.get().uid;
+ return mPickerUriResolver.openTypedAssetFile(uri, mimeTypeFilter, opts, signal,
+ callingPid, callingUid);
}
// TODO: enforce that caller has access to this uri
// Offer thumbnail of media, when requested
- final boolean wantsThumb = (opts != null) && opts.containsKey(ContentResolver.EXTRA_SIZE)
- && MimeUtils.startsWithIgnoreCase(mimeTypeFilter, "image/");
if (wantsThumb) {
final ParcelFileDescriptor pfd = ensureThumbnail(uri, signal);
return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH);
}
// Worst case, return the underlying file
- return new AssetFileDescriptor(openFileCommon(uri, "r", signal, opts), 0,
+ return new AssetFileDescriptor(openFileCommon(uri, mode, signal, opts), 0,
AssetFileDescriptor.UNKNOWN_LENGTH);
}
@@ -7354,8 +8122,14 @@
*/
Cursor queryForSingleItem(Uri uri, String[] projection, String selection,
String[] selectionArgs, CancellationSignal signal) throws FileNotFoundException {
- final Cursor c = query(uri, projection,
- DatabaseUtils.createSqlQueryBundle(selection, selectionArgs, null), signal, true);
+ Cursor c = null;
+ try {
+ c = query(uri, projection,
+ DatabaseUtils.createSqlQueryBundle(selection, selectionArgs, null),
+ signal, true);
+ } catch (IllegalArgumentException e) {
+ throw new FileNotFoundException("Volume not found for " + uri);
+ }
if (c == null) {
throw new FileNotFoundException("Missing cursor for " + uri);
} else if (c.getCount() < 1) {
@@ -7384,16 +8158,10 @@
final boolean callerIsOwner = Objects.equals(getCallingPackageOrSelf(), itemOwner);
if (hasOwner && !callerIsOwner) {
throw new IllegalStateException(
- "Only owner is able to interact with pending item " + item);
+ "Only owner is able to interact with pending/trashed item " + item);
}
}
- public File getFuseFile(File file) {
- String filePath = file.getPath().replaceFirst(
- "/storage/", "/mnt/user/" + UserHandle.myUserId() + "/");
- return new File(filePath);
- }
-
private ParcelFileDescriptor openWithFuse(String filePath, int uid, int mediaCapabilitiesUid,
int modeBits, boolean shouldRedact, boolean shouldTranscode, int transcodeReason)
throws FileNotFoundException {
@@ -7410,7 +8178,7 @@
}
try {
- return FileUtils.openSafely(getFuseFile(new File(filePath)), modeBits);
+ return FileUtils.openSafely(toFuseFile(new File(filePath)), modeBits);
} finally {
synchronized (mPendingOpenInfo) {
mPendingOpenInfo.remove(tid);
@@ -7706,8 +8474,8 @@
return MimeUtils.resolveMediaType(mimeType);
}
- private boolean canAccessMediaFile(String filePath, boolean allowLegacy) {
- if (!allowLegacy && isCallingPackageRequestingLegacy()) {
+ private boolean canAccessMediaFile(String filePath, boolean excludeNonSystemGallery) {
+ if (excludeNonSystemGallery && !isCallingPackageSystemGallery()) {
return false;
}
switch (getFileMediaType(filePath)) {
@@ -7749,7 +8517,7 @@
// Apps with write access to images and/or videos can bypass our restrictions if all of the
// the files they're accessing are of the compatible media type.
- if (canAccessMediaFile(filePath, /*allowLegacy*/ true)) {
+ if (canAccessMediaFile(filePath, /*excludeNonSystemGallery*/ false)) {
return true;
}
@@ -7851,6 +8619,12 @@
private static final class RedactionInfo {
public final long[] redactionRanges;
public final long[] freeOffsets;
+
+ public RedactionInfo() {
+ this.redactionRanges = new long[0];
+ this.freeOffsets = new long[0];
+ }
+
public RedactionInfo(long[] redactionRanges, long[] freeOffsets) {
this.redactionRanges = redactionRanges;
this.freeOffsets = freeOffsets;
@@ -7888,6 +8662,8 @@
/**
* Calculates the ranges that need to be redacted for the given file and user that wants to
* access the file.
+ * Note: This method assumes that the caller of this function has already done permission checks
+ * for the uid to access this path.
*
* @param uid UID of the package wanting to access the file
* @param path File path
@@ -7937,16 +8713,20 @@
final Uri contentUri = FileUtils.getContentUriForPath(path);
final String[] projection = new String[]{
- MediaColumns.OWNER_PACKAGE_NAME, MediaColumns._ID };
+ MediaColumns.OWNER_PACKAGE_NAME, MediaColumns._ID , FileColumns.MEDIA_TYPE};
final String selection = MediaColumns.DATA + "=?";
final String[] selectionArgs = new String[]{path};
final String ownerPackageName;
- final Uri item;
- try (final Cursor c = queryForSingleItem(contentUri, projection, selection,
- selectionArgs, null)) {
+ final int id;
+ final int mediaType;
+ // Query as MediaProvider as non-RES apps will result in FileNotFoundException.
+ // Note: The caller uid already has passed permission checks to access this file.
+ try (final Cursor c = queryForSingleItemAsMediaProvider(contentUri, projection,
+ selection, selectionArgs, null)) {
c.moveToFirst();
ownerPackageName = c.getString(0);
- item = ContentUris.withAppendedId(contentUri, /*item id*/ c.getInt(1));
+ id = c.getInt(1);
+ mediaType = c.getInt(2);
} catch (FileNotFoundException e) {
// Ideally, this shouldn't happen unless the file was deleted after we checked its
// existence and before we get to the redaction logic here. In this case we throw
@@ -7959,14 +8739,23 @@
final boolean callerIsOwner = Objects.equals(getCallingPackageOrSelf(),
ownerPackageName);
+ // Do not redact if the caller is the owner
if (callerIsOwner) {
return new long[0];
}
- final boolean callerHasUriPermission = getContext().checkUriPermission(
- item, mCallingIdentity.get().pid, mCallingIdentity.get().uid,
+ // Do not redact if the caller has write uri permission granted on the file.
+ final Uri fileUri = ContentUris.withAppendedId(contentUri, id);
+ boolean callerHasWriteUriPermission = getContext().checkUriPermission(
+ fileUri, mCallingIdentity.get().pid, mCallingIdentity.get().uid,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == PERMISSION_GRANTED;
- if (callerHasUriPermission) {
+ if (callerHasWriteUriPermission) {
+ return new long[0];
+ }
+ // Check if the caller has write access to other uri formats for the same file.
+ callerHasWriteUriPermission = getOtherUriGrantsForPath(path, mediaType,
+ Long.toString(id), /* forWrite */ true) != null;
+ if (callerHasWriteUriPermission) {
return new long[0];
}
@@ -7987,13 +8776,35 @@
*/
@VisibleForTesting
public static RedactionInfo getRedactionRanges(File file) throws IOException {
- Trace.beginSection("getRedactionRanges");
+ try (FileInputStream is = new FileInputStream(file)) {
+ return getRedactionRanges(is, MimeUtils.resolveMimeType(file));
+ } catch (FileNotFoundException ignored) {
+ // If file not found, then there's nothing to redact
+ return new RedactionInfo();
+ } catch (IOException e) {
+ throw new IOException("Failed to redact " + file, e);
+ }
+ }
+
+ /**
+ * Calculates the ranges containing sensitive metadata that should be redacted if the caller
+ * doesn't have the required permissions.
+ *
+ * @param fis {@link FileInputStream} to be redacted
+ * @return the ranges to be redacted in a RedactionInfo object, could be empty redaction ranges
+ * if there's sensitive metadata
+ * @throws IOException if an IOException happens while calculating the redaction ranges
+ */
+ @VisibleForTesting
+ public static RedactionInfo getRedactionRanges(FileInputStream fis, String mimeType)
+ throws IOException {
final LongArray res = new LongArray();
final LongArray freeOffsets = new LongArray();
- try (FileInputStream is = new FileInputStream(file)) {
- final String mimeType = MimeUtils.resolveMimeType(file);
+
+ Trace.beginSection("getRedactionRanges");
+ try {
if (ExifInterface.isSupportedMimeType(mimeType)) {
- final ExifInterface exif = new ExifInterface(is.getFD());
+ final ExifInterface exif = new ExifInterface(fis.getFD());
for (String tag : REDACTED_EXIF_TAGS) {
final long[] range = exif.getAttributeRange(tag);
if (range != null) {
@@ -8007,7 +8818,7 @@
}
if (IsoInterface.isSupportedMimeType(mimeType)) {
- final IsoInterface iso = IsoInterface.fromFileDescriptor(is.getFD());
+ final IsoInterface iso = IsoInterface.fromFileDescriptor(fis.getFD());
for (int box : REDACTED_ISO_BOXES) {
final long[] ranges = iso.getBoxRanges(box);
for (int i = 0; i < ranges.length; i += 2) {
@@ -8021,13 +8832,11 @@
final XmpInterface isoXmp = XmpInterface.fromContainer(iso);
res.addAll(isoXmp.getRedactionRanges());
}
- } catch (FileNotFoundException ignored) {
- // If file not found, then there's nothing to redact
- } catch (IOException e) {
- throw new IOException("Failed to redact " + file, e);
+
+ return new RedactionInfo(res.toArray(), freeOffsets.toArray());
+ } finally {
+ Trace.endSection();
}
- Trace.endSection();
- return new RedactionInfo(res.toArray(), freeOffsets.toArray());
}
/**
@@ -8040,6 +8849,69 @@
return !matcher.matches();
}
+ private FileAccessAttributes queryForFileAttributes(final String path)
+ throws FileNotFoundException {
+ Trace.beginSection("queryFileAttr");
+ final Uri contentUri = FileUtils.getContentUriForPath(path);
+ final String[] projection = new String[]{
+ MediaColumns._ID,
+ MediaColumns.OWNER_PACKAGE_NAME,
+ MediaColumns.IS_PENDING,
+ FileColumns.MEDIA_TYPE,
+ MediaColumns.IS_TRASHED
+ };
+ final String selection = MediaColumns.DATA + "=?";
+ final String[] selectionArgs = new String[]{path};
+ FileAccessAttributes fileAccessAttributes;
+ try (final Cursor c = queryForSingleItemAsMediaProvider(contentUri, projection,
+ selection,
+ selectionArgs, null)) {
+ fileAccessAttributes = FileAccessAttributes.fromCursor(c);
+ }
+ Trace.endSection();
+ return fileAccessAttributes;
+ }
+
+ private void checkIfFileOpenIsPermitted(String path,
+ FileAccessAttributes fileAccessAttributes, String redactedUriId,
+ boolean forWrite) throws FileNotFoundException {
+ final File file = new File(path);
+ Uri fileUri = MediaStore.Files.getContentUri(extractVolumeName(path),
+ fileAccessAttributes.getId());
+ // We don't check ownership for files with IS_PENDING set by FUSE
+ // Please note that even if ownerPackageName is null, the check below will throw an
+ // IllegalStateException
+ if (fileAccessAttributes.isTrashed() || (fileAccessAttributes.isPending()
+ && !isPendingFromFuse(new File(path)))) {
+ requireOwnershipForItem(fileAccessAttributes.getOwnerPackageName(), fileUri);
+ }
+
+ // Check that path looks consistent before uri checks
+ if (!FileUtils.contains(Environment.getStorageDirectory(), file)) {
+ checkWorldReadAccess(file.getAbsolutePath());
+ }
+
+ try {
+ // checkAccess throws FileNotFoundException only from checkWorldReadAccess(),
+ // which we already check above. Hence, handling only SecurityException.
+ if (redactedUriId != null) {
+ fileUri = ContentUris.removeId(fileUri).buildUpon().appendPath(
+ redactedUriId).build();
+ }
+ checkAccess(fileUri, Bundle.EMPTY, file, forWrite);
+ } catch (SecurityException e) {
+ // Check for other Uri formats only when the single uri check flow fails.
+ // Throw the previous exception if the multi-uri checks failed.
+ final String uriId = redactedUriId == null
+ ? Long.toString(fileAccessAttributes.getId()) : redactedUriId;
+ if (getOtherUriGrantsForPath(path, fileAccessAttributes.getMediaType(),
+ uriId, forWrite) == null) {
+ throw e;
+ }
+ }
+ }
+
+
/**
* Checks if the app identified by the given UID is allowed to open the given file for the given
* access mode.
@@ -8065,6 +8937,7 @@
boolean isSuccess = false;
final int originalUid = getBinderUidForFuse(uid, tid);
+ final int callingUserId = uidToUserId(uid);
int mediaCapabilitiesUid = 0;
final PendingOpenInfo pendingOpenInfo;
synchronized (mPendingOpenInfo) {
@@ -8078,26 +8951,30 @@
try {
boolean forceRedaction = false;
String redactedUriId = null;
- if (isSyntheticFilePathForRedactedUri(path, uid)) {
+ if (isSyntheticPath(path, callingUserId)) {
if (forWrite) {
- // Redacted URIs are not allowed to update EXIF headers.
+ // Synthetic URIs are not allowed to update EXIF headers.
return new FileOpenResult(OsConstants.EACCES /* status */, originalUid,
mediaCapabilitiesUid, new long[0]);
}
- redactedUriId = extractFileName(path);
+ if (isRedactedPath(path, callingUserId)) {
+ redactedUriId = extractFileName(path);
- // If path is redacted Uris' path, ioPath must be the real path, ioPath must
- // haven been updated to the real path during onFileLookupForFuse.
- path = ioPath;
+ // If path is redacted Uris' path, ioPath must be the real path, ioPath must
+ // haven been updated to the real path during onFileLookupForFuse.
+ path = ioPath;
- // Irrespective of the permissions we want to redact in this case.
- redact = true;
- forceRedaction = true;
- } else if (isSyntheticDirPath(path, uid)) {
- // we don't support any other transformations under .transforms/synthetic dir
- return new FileOpenResult(OsConstants.ENOENT /* status */, originalUid,
- mediaCapabilitiesUid, new long[0]);
+ // Irrespective of the permissions we want to redact in this case.
+ redact = true;
+ forceRedaction = true;
+ } else if (isPickerPath(path, callingUserId)) {
+ return handlePickerFileOpen(path, originalUid);
+ } else {
+ // we don't support any other transformations under .transforms/synthetic dir
+ return new FileOpenResult(OsConstants.ENOENT /* status */, originalUid,
+ mediaCapabilitiesUid, new long[0]);
+ }
}
if (isPrivatePackagePathNotAccessibleByCaller(path)) {
@@ -8118,55 +8995,23 @@
return new FileOpenResult(OsConstants.EACCES /* status */, originalUid,
mediaCapabilitiesUid, new long[0]);
}
-
- final Uri contentUri = FileUtils.getContentUriForPath(path);
- final String[] projection = new String[]{
- MediaColumns._ID,
- MediaColumns.OWNER_PACKAGE_NAME,
- MediaColumns.IS_PENDING,
- FileColumns.MEDIA_TYPE};
- final String selection = MediaColumns.DATA + "=?";
- final String[] selectionArgs = new String[]{path};
- final long id;
- final int mediaType;
- final boolean isPending;
- String ownerPackageName = null;
- try (final Cursor c = queryForSingleItemAsMediaProvider(contentUri, projection,
- selection,
- selectionArgs, null)) {
- id = c.getLong(0);
- ownerPackageName = c.getString(1);
- isPending = c.getInt(2) != 0;
- mediaType = c.getInt(3);
- }
- final File file = new File(path);
- Uri fileUri = MediaStore.Files.getContentUri(extractVolumeName(path), id);
- // We don't check ownership for files with IS_PENDING set by FUSE
- if (isPending && !isPendingFromFuse(new File(path))) {
- requireOwnershipForItem(ownerPackageName, fileUri);
- }
-
- // Check that path looks consistent before uri checks
- if (!FileUtils.contains(Environment.getStorageDirectory(), file)) {
- checkWorldReadAccess(file.getAbsolutePath());
- }
-
- try {
- // checkAccess throws FileNotFoundException only from checkWorldReadAccess(),
- // which we already check above. Hence, handling only SecurityException.
- if (redactedUriId != null) {
- fileUri = ContentUris.removeId(fileUri).buildUpon().appendPath(
- redactedUriId).build();
- }
- checkAccess(fileUri, Bundle.EMPTY, file, forWrite);
- } catch (SecurityException e) {
- // Check for other Uri formats only when the single uri check flow fails.
- // Throw the previous exception if the multi-uri checks failed.
- final String uriId = redactedUriId == null ? Long.toString(id) : redactedUriId;
- if (getOtherUriGrantsForPath(path, mediaType, uriId, forWrite) == null) {
- throw e;
+ // TODO: Fetch owner id from Android/media directory and check if caller is owner
+ FileAccessAttributes fileAttributes = null;
+ if (XAttrUtils.ENABLE_XATTR_METADATA_FOR_FUSE) {
+ Optional<FileAccessAttributes> fileAttributesThroughXattr =
+ XAttrUtils.getFileAttributesFromXAttr(path,
+ XAttrUtils.FILE_ACCESS_XATTR_KEY);
+ if (fileAttributesThroughXattr.isPresent()) {
+ fileAttributes = fileAttributesThroughXattr.get();
}
}
+
+ // FileAttributes will be null if the xattr call failed or the flag to enable xattr
+ // metadata support is not set
+ if (fileAttributes == null) {
+ fileAttributes = queryForFileAttributes(path);
+ }
+ checkIfFileOpenIsPermitted(path, fileAttributes, redactedUriId, forWrite);
isSuccess = true;
return new FileOpenResult(0 /* status */, originalUid, mediaCapabilitiesUid,
redact ? getRedactionRangesForFuse(path, ioPath, originalUid, uid, tid,
@@ -8558,80 +9403,52 @@
}
}
- /**
- * Checks if the app with the given UID is allowed to create or delete the directory with the
- * given path.
- *
- * @param path File path of the directory that the app wants to create/delete
- * @param uid UID of the app that wants to create/delete the directory
- * @param forCreate denotes whether the operation is directory creation or deletion
- * @return 0 if the operation is allowed, or the following {@code errno} values:
- * <ul>
- * <li>{@link OsConstants#EACCES} if the app tries to create/delete a dir in another app's
- * external directory, or if the calling package is a legacy app that doesn't have
- * WRITE_EXTERNAL_STORAGE permission.
- * <li>{@link OsConstants#EPERM} if the app tries to create/delete a top-level directory.
- * </ul>
- *
- * Called from JNI in jni/MediaProviderWrapper.cpp
- */
- @Keep
- public int isDirectoryCreationOrDeletionAllowedForFuse(
- @NonNull String path, int uid, boolean forCreate) {
- final LocalCallingIdentity token =
- clearLocalCallingIdentity(getCachedCallingIdentityForFuse(uid));
- PulledMetrics.logFileAccessViaFuse(getCallingUidOrSelf(), path);
+ // These need to stay in sync with MediaProviderWrapper.cpp's DirectoryAccessRequestType enum
+ @IntDef(flag = true, prefix = { "DIRECTORY_ACCESS_FOR_" }, value = {
+ DIRECTORY_ACCESS_FOR_READ,
+ DIRECTORY_ACCESS_FOR_WRITE,
+ DIRECTORY_ACCESS_FOR_CREATE,
+ DIRECTORY_ACCESS_FOR_DELETE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @VisibleForTesting
+ @interface DirectoryAccessType {}
- try {
- // App dirs are not indexed, so we don't create an entry for the file.
- if (isPrivatePackagePathNotAccessibleByCaller(path)) {
- Log.e(TAG, "Can't modify another app's external directory!");
- return OsConstants.EACCES;
- }
+ @VisibleForTesting
+ static final int DIRECTORY_ACCESS_FOR_READ = 1;
- if (shouldBypassFuseRestrictions(/*forWrite*/ true, path)) {
- return 0;
- }
- // Legacy apps that made is this far don't have the right storage permission and hence
- // are not allowed to access anything other than their external app directory
- if (isCallingPackageRequestingLegacy()) {
- return OsConstants.EACCES;
- }
+ @VisibleForTesting
+ static final int DIRECTORY_ACCESS_FOR_WRITE = 2;
- final String[] relativePath = sanitizePath(extractRelativePath(path));
- final boolean isTopLevelDir =
- relativePath.length == 1 && TextUtils.isEmpty(relativePath[0]);
- if (isTopLevelDir) {
- // We allow creating the default top level directories only, all other operations on
- // top level directories are not allowed.
- if (forCreate && FileUtils.isDefaultDirectoryName(extractDisplayName(path))) {
- return 0;
- }
- Log.e(TAG,
- "Creating a non-default top level directory or deleting an existing"
- + " one is not allowed!");
- return OsConstants.EPERM;
- }
- return 0;
- } finally {
- restoreLocalCallingIdentity(token);
- }
- }
+ @VisibleForTesting
+ static final int DIRECTORY_ACCESS_FOR_CREATE = 3;
+
+ @VisibleForTesting
+ static final int DIRECTORY_ACCESS_FOR_DELETE = 4;
/**
- * Checks whether the app with the given UID is allowed to open the directory denoted by the
+ * Checks whether the app with the given UID is allowed to access the directory denoted by the
* given path.
*
* @param path directory's path
* @param uid UID of the requesting app
- * @return 0 if it's allowed to open the diretory, {@link OsConstants#EACCES} if the calling
- * package is a legacy app that doesn't have READ_EXTERNAL_STORAGE permission,
- * {@link OsConstants#ENOENT} otherwise.
+ * @param accessType type of access being requested - eg {@link
+ * MediaProvider#DIRECTORY_ACCESS_FOR_READ}
+ * @return 0 if it's allowed to access the directory, {@link OsConstants#ENOENT} for attempts
+ * to access a private package path in Android/data or Android/obb the caller doesn't have
+ * access to, and otherwise {@link OsConstants#EACCES} if the calling package is a legacy app
+ * that doesn't have READ_EXTERNAL_STORAGE permission or for other invalid attempts to access
+ * Android/data or Android/obb dirs.
*
* Called from JNI in jni/MediaProviderWrapper.cpp
*/
@Keep
- public int isOpendirAllowedForFuse(@NonNull String path, int uid, boolean forWrite) {
+ public int isDirAccessAllowedForFuse(@NonNull String path, int uid,
+ @DirectoryAccessType int accessType) {
+ Preconditions.checkArgumentInRange(accessType, 1, DIRECTORY_ACCESS_FOR_DELETE,
+ "accessType");
+
+ final boolean forRead = accessType == DIRECTORY_ACCESS_FOR_READ;
final LocalCallingIdentity token =
clearLocalCallingIdentity(getCachedCallingIdentityForFuse(uid));
PulledMetrics.logFileAccessViaFuse(getCallingUidOrSelf(), path);
@@ -8644,14 +9461,16 @@
return OsConstants.ENOENT;
}
- if (shouldBypassFuseRestrictions(forWrite, path)) {
+ if (shouldBypassFuseRestrictions(/* forWrite= */ !forRead, path)) {
return 0;
}
- // Do not allow apps to open Android/data or Android/obb dirs.
- // On primary volumes, apps that get special access to these directories get it via
- // mount views of lowerfs. On secondary volumes, such apps would return early from
- // shouldBypassFuseRestrictions above.
+ // Do not allow apps that reach this point to access Android/data or Android/obb dirs.
+ // Creation should be via getContext().getExternalFilesDir() etc methods.
+ // Reads and writes on primary volumes should be via mount views of lowerfs for apps
+ // that get special access to these directories.
+ // Reads and writes on secondary volumes would be provided via an early return from
+ // shouldBypassFuseRestrictions above (again just for apps with special access).
if (isDataOrObbPath(path)) {
return OsConstants.EACCES;
}
@@ -8663,22 +9482,34 @@
}
// This is a non-legacy app. Rest of the directories are generally writable
// except for non-default top-level directories.
- if (forWrite) {
+ if (!forRead) {
final String[] relativePath = sanitizePath(extractRelativePath(path));
if (relativePath.length == 0) {
- Log.e(TAG, "Directoy write not allowed on invalid relative path for " + path);
+ Log.e(TAG,
+ "Directory update not allowed on invalid relative path for " + path);
return OsConstants.EPERM;
}
final boolean isTopLevelDir =
relativePath.length == 1 && TextUtils.isEmpty(relativePath[0]);
if (isTopLevelDir) {
- if (FileUtils.isDefaultDirectoryName(extractDisplayName(path))) {
- return 0;
- } else {
- Log.e(TAG,
- "Writing to a non-default top level directory is not allowed!");
+ // We don't allow deletion of any top-level folders
+ if (accessType == DIRECTORY_ACCESS_FOR_DELETE) {
+ Log.e(TAG, "Deleting top level directories are not allowed!");
return OsConstants.EACCES;
}
+
+ // We allow creating or writing to default top-level folders, but we don't
+ // allow creation or writing to non-default top-level folders.
+ if ((accessType == DIRECTORY_ACCESS_FOR_CREATE
+ || accessType == DIRECTORY_ACCESS_FOR_WRITE)
+ && FileUtils.isDefaultDirectoryName(extractDisplayName(path))) {
+ return 0;
+ }
+
+ Log.e(TAG,
+ "Creating or writing to a non-default top level directory is not "
+ + "allowed!");
+ return OsConstants.EACCES;
}
}
@@ -8878,35 +9709,14 @@
* Returns any uri that is granted from the set of Uris passed.
*/
private @Nullable Uri getPermissionGrantedUri(@NonNull List<Uri> uris, boolean forWrite) {
- if (SdkLevel.isAtLeastS()) {
- int[] res = checkUriPermissions(uris, mCallingIdentity.get().pid,
- mCallingIdentity.get().uid, forWrite);
- if (res.length != uris.size()) {
- return null;
- }
- for (int i = 0; i < uris.size(); i++) {
- if (res[i] == PERMISSION_GRANTED) {
- return uris.get(i);
- }
- }
- } else {
- for (Uri uri : uris) {
- if (isUriPermissionGranted(uri, forWrite)) {
- return uri;
- }
+ for (Uri uri : uris) {
+ if (isUriPermissionGranted(uri, forWrite)) {
+ return uri;
}
}
return null;
}
- @RequiresApi(Build.VERSION_CODES.S)
- private int[] checkUriPermissions(@NonNull List<Uri> uris, int pid, int uid, boolean forWrite) {
- final int modeFlags = forWrite
- ? Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- : Intent.FLAG_GRANT_READ_URI_PERMISSION;
- return getContext().checkUriPermissions(uris, pid, uid, modeFlags);
- }
-
private boolean isUriPermissionGranted(Uri uri, boolean forWrite) {
final int modeFlags = forWrite
? Intent.FLAG_GRANT_WRITE_URI_PERMISSION
@@ -8952,6 +9762,20 @@
}
@VisibleForTesting
+ public int getIntDeviceConfig(String namespace, String key, int defaultValue) {
+ if (!canReadDeviceConfig(key, defaultValue)) {
+ return defaultValue;
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return DeviceConfig.getInt(namespace, key, defaultValue);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @VisibleForTesting
public String getStringDeviceConfig(String key, String defaultValue) {
if (!canReadDeviceConfig(key, defaultValue)) {
return defaultValue;
@@ -9073,12 +9897,15 @@
final boolean allowHidden = isCallingPackageAllowedHidden();
final int table = matchUri(uri, allowHidden);
+ final String selection = extras.getString(QUERY_ARG_SQL_SELECTION);
+ final String[] selectionArgs = extras.getStringArray(QUERY_ARG_SQL_SELECTION_ARGS);
+
// First, check to see if caller has direct write access
if (forWrite) {
final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_UPDATE, table, uri, extras, null);
qb.allowColumn(SQLiteQueryBuilder.ROWID_COLUMN);
try (Cursor c = qb.query(helper, new String[] { SQLiteQueryBuilder.ROWID_COLUMN },
- null, null, null, null, null, null, null)) {
+ selection, selectionArgs, null, null, null, null, null)) {
if (c.moveToFirst()) {
// Direct write access granted, yay!
return;
@@ -9102,7 +9929,7 @@
final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_QUERY, table, uri, extras, null);
qb.allowColumn(SQLiteQueryBuilder.ROWID_COLUMN);
try (Cursor c = qb.query(helper, new String[] { SQLiteQueryBuilder.ROWID_COLUMN },
- null, null, null, null, null, null, null)) {
+ selection, selectionArgs, null, null, null, null, null)) {
if (c.moveToFirst()) {
if (!forWrite) {
// Direct read access granted, yay!
@@ -9289,6 +10116,16 @@
}
}
+ public List<String> getSupportedTranscodingRelativePaths() {
+ return mTranscodeHelper.getSupportedRelativePaths();
+ }
+
+ public List<String> getSupportedUncachedRelativePaths() {
+ return StringUtils.verifySupportedUncachedRelativePaths(
+ StringUtils.getStringArrayConfig(getContext(),
+ R.array.config_supported_uncached_relative_paths));
+ }
+
/**
* Creating a new method for Transcoding to avoid any merge conflicts.
* TODO(b/170465810): Remove this when the code is refactored.
@@ -9304,7 +10141,8 @@
boolean volumeAttached = false;
UserHandle user = mCallingIdentity.get().getUser();
for (MediaVolume vol : mAttachedVolumes) {
- if (vol.getName().equals(volumeName) && vol.isVisibleToUser(user)) {
+ if (vol.getName().equals(volumeName)
+ && (vol.isVisibleToUser(user) || vol.isPublicVolume()) ) {
volumeAttached = true;
break;
}
@@ -9336,13 +10174,6 @@
return false;
}
- static boolean isInternalMediaDatabaseName(String name) {
- if (INTERNAL_DATABASE_NAME.equals(name)) {
- return true;
- }
- return false;
- }
-
private @NonNull Uri getBaseContentUri(@NonNull String volumeName) {
return MediaStore.AUTHORITY_URI.buildUpon().appendPath(volumeName).build();
}
@@ -9392,7 +10223,7 @@
// We just finished the database operation above, we know that
// it's ready to answer queries, so notify our DocumentProvider
// so it can answer queries without risking ANR
- MediaDocumentsProvider.onMediaStoreReady(getContext(), volumeName);
+ MediaDocumentsProvider.onMediaStoreReady(getContext());
});
}
return uri;
@@ -9457,6 +10288,10 @@
private DatabaseHelper mInternalDatabase;
private DatabaseHelper mExternalDatabase;
+ private PickerDbFacade mPickerDbFacade;
+ private ExternalDbFacade mExternalDbFacade;
+ private PickerDataLayer mPickerDataLayer;
+ private PickerSyncController mPickerSyncController;
private TranscodeHelper mTranscodeHelper;
// name of the volume currently being scanned by the media scanner (or null)
@@ -9515,6 +10350,12 @@
static final int DOWNLOADS = 800;
static final int DOWNLOADS_ID = 801;
+ static final int PICKER = 900;
+ static final int PICKER_ID = 901;
+ static final int PICKER_INTERNAL_MEDIA = 902;
+ static final int PICKER_INTERNAL_ALBUMS = 903;
+ static final int PICKER_UNRELIABLE_VOLUME = 904;
+
private static final HashSet<Integer> REDACTED_URI_SUPPORTED_TYPES = new HashSet<>(
Arrays.asList(AUDIO_MEDIA_ID, IMAGES_MEDIA_ID, VIDEO_MEDIA_ID, FILES_ID, DOWNLOADS_ID));
@@ -9554,6 +10395,21 @@
}
public LocalUriMatcher(String auth) {
+ // Warning: Do not move these exact string matches below "*/.." matches.
+ // If "*/.." match is added to mPublic children before "picker/#/#", then while matching
+ // "picker/0/10", UriMatcher matches "*" node with "picker" and tries to match "0/10"
+ // with children of "*".
+ // UriMatcher does not look for exact "picker" string match if it finds * node before
+ // it. It finds the first best child match and proceeds the match from there without
+ // looking at other siblings.
+ mPublic.addURI(auth, "picker", PICKER);
+ // TODO(b/195009139): Remove after switching picker URI to new format
+ // content://media/picker/<user-id>/<media-id>
+ mPublic.addURI(auth, "picker/#/#", PICKER_ID);
+ // content://media/picker/<user-id>/<authority>/media/<media-id>
+ mPublic.addURI(auth, "picker/#/*/media/*", PICKER_ID);
+ // content://media/picker/unreliable/<media_id>
+ mPublic.addURI(auth, "picker/unreliable/#", PICKER_UNRELIABLE_VOLUME);
mPublic.addURI(auth, "*/images/media", IMAGES_MEDIA);
mPublic.addURI(auth, "*/images/media/#", IMAGES_MEDIA_ID);
mPublic.addURI(auth, "*/images/media/#/thumbnail", IMAGES_MEDIA_ID_THUMBNAIL);
@@ -9598,6 +10454,8 @@
// NOTE: technically hidden, since Uri is never exposed
mPublic.addURI(auth, "*/version", VERSION);
+ mHidden.addURI(auth, "picker_internal/media", PICKER_INTERNAL_MEDIA);
+ mHidden.addURI(auth, "picker_internal/albums", PICKER_INTERNAL_ALBUMS);
mHidden.addURI(auth, "*", VOLUMES_ID);
mHidden.addURI(auth, null, VOLUMES);
@@ -9726,6 +10584,14 @@
return builder.build();
}
+ public ExternalDbFacade getExternalDbFacade() {
+ return mExternalDbFacade;
+ }
+
+ public PickerSyncController getPickerSyncController() {
+ return mPickerSyncController;
+ }
+
private boolean isCallingPackageSystemGallery() {
return mCallingIdentity.get().hasPermission(PERMISSION_IS_SYSTEM_GALLERY);
}
diff --git a/src/com/android/providers/media/MediaService.java b/src/com/android/providers/media/MediaService.java
index 4178aa4..cf154ab 100644
--- a/src/com/android/providers/media/MediaService.java
+++ b/src/com/android/providers/media/MediaService.java
@@ -27,7 +27,6 @@
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
-import android.os.Bundle;
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageVolume;
@@ -78,7 +77,8 @@
case Intent.ACTION_PACKAGE_FULLY_REMOVED:
case Intent.ACTION_PACKAGE_DATA_CLEARED: {
final String packageName = intent.getData().getSchemeSpecificPart();
- onPackageOrphaned(packageName);
+ final int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
+ onPackageOrphaned(packageName, uid);
break;
}
case Intent.ACTION_MEDIA_SCANNER_SCAN_FILE: {
@@ -117,10 +117,10 @@
}
}
- private void onPackageOrphaned(String packageName) {
+ private void onPackageOrphaned(String packageName, int uid) {
try (ContentProviderClient cpc = getContentResolver()
.acquireContentProviderClient(MediaStore.AUTHORITY)) {
- ((MediaProvider) cpc.getLocalContentProvider()).onPackageOrphaned(packageName);
+ ((MediaProvider) cpc.getLocalContentProvider()).onPackageOrphaned(packageName, uid);
}
}
@@ -150,6 +150,16 @@
public static void onScanVolume(Context context, MediaVolume volume, int reason)
throws IOException {
final String volumeName = volume.getName();
+ if (!MediaStore.VOLUME_INTERNAL.equals(volumeName) && volume.getPath() == null) {
+ /* This is a very unexpected state and can only ever happen with app-cloned users.
+ In general, MediaVolumes should always be mounted and have a path, however, if the
+ user failed to unlock properly, MediaProvider still gets the volume from the
+ StorageManagerService because MediaProvider is special cased there. See
+ StorageManagerService#getVolumeList. Reference bug: b/207723670. */
+ Log.w(TAG, String.format("Skipping volume scan for %s when volume path is null.",
+ volumeName));
+ return;
+ }
UserHandle owner = volume.getUser();
if (owner == null) {
// Can happen for the internal volume
diff --git a/src/com/android/providers/media/MediaUpgradeReceiver.java b/src/com/android/providers/media/MediaUpgradeReceiver.java
index abb326a..38cdc75 100644
--- a/src/com/android/providers/media/MediaUpgradeReceiver.java
+++ b/src/com/android/providers/media/MediaUpgradeReceiver.java
@@ -17,16 +17,17 @@
package com.android.providers.media;
import android.content.BroadcastReceiver;
+import android.content.ContentProviderClient;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
-import android.provider.Column;
+import android.provider.MediaStore;
import android.util.Log;
import com.android.providers.media.util.ForegroundThread;
-import com.android.providers.media.util.Metrics;
import java.io.File;
+import java.util.Optional;
/**
* This will be launched during system boot, after the core system has
@@ -67,27 +68,25 @@
File dbDir = context.getDatabasePath("foo").getParentFile();
String[] files = dbDir.list();
if (files == null) return;
- for (int i=0; i<files.length; i++) {
+
+ MediaProvider mediaProvider = getMediaProvider(context);
+ for (int i = 0; i < files.length; i++) {
String file = files[i];
- if (MediaProvider.isMediaDatabaseName(file)) {
+ Optional<DatabaseHelper> helper = mediaProvider.getDatabaseHelper(file);
+ if (helper.isPresent()) {
long startTime = System.currentTimeMillis();
Log.i(TAG, "---> Start upgrade of media database " + file);
try {
- DatabaseHelper helper = new DatabaseHelper(
- context, file, MediaProvider.isInternalMediaDatabaseName(file),
- false, false, Column.class, Metrics::logSchemaChange, null,
- MediaProvider.MIGRATION_LISTENER, null);
- helper.runWithTransaction((db) -> {
+ helper.get().runWithTransaction((db) -> {
// Perform just enough to force database upgrade
return db.getVersion();
});
- helper.close();
} catch (Throwable t) {
Log.wtf(TAG, "Error during upgrade of media db " + file, t);
- } finally {
}
+
Log.i(TAG, "<--- Finished upgrade of media database " + file
- + " in " + (System.currentTimeMillis()-startTime) + "ms");
+ + " in " + (System.currentTimeMillis() - startTime) + "ms");
}
}
} catch (Throwable t) {
@@ -95,4 +94,14 @@
Log.wtf(TAG, "Error during upgrade attempt.", t);
}
}
+
+ private MediaProvider getMediaProvider(Context context) {
+ try (ContentProviderClient cpc =
+ context.getContentResolver().acquireContentProviderClient(
+ MediaStore.AUTHORITY)) {
+ return (MediaProvider) cpc.getLocalContentProvider();
+ } catch (Exception e) {
+ throw new IllegalStateException("Failed to acquire MediaProvider", e);
+ }
+ }
}
diff --git a/src/com/android/providers/media/MediaVolume.java b/src/com/android/providers/media/MediaVolume.java
index 1ebf5d9..4934365 100644
--- a/src/com/android/providers/media/MediaVolume.java
+++ b/src/com/android/providers/media/MediaVolume.java
@@ -16,15 +16,19 @@
package com.android.providers.media;
+import android.annotation.SuppressLint;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
import android.os.storage.StorageVolume;
import android.provider.MediaStore;
+import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.modules.utils.build.SdkLevel;
+
import java.io.File;
import java.util.Objects;
@@ -60,6 +64,16 @@
*/
private final @Nullable String mId;
+ /**
+ * Whether the volume is managed from outside Android.
+ */
+ private final boolean mExternallyManaged;
+
+ /**
+ * Whether the volume is public.
+ */
+ private final boolean mPublicVolume;
+
public @NonNull String getName() {
return mName;
}
@@ -76,11 +90,22 @@
return mId;
}
- private MediaVolume (@NonNull String name, UserHandle user, File path, String id) {
+ public boolean isExternallyManaged() {
+ return mExternallyManaged;
+ }
+
+ public boolean isPublicVolume() {
+ return mPublicVolume;
+ }
+
+ private MediaVolume (@NonNull String name, UserHandle user, File path, String id,
+ boolean externallyManaged, boolean mPublicVolume) {
this.mName = name;
this.mUser = user;
this.mPath = path;
this.mId = id;
+ this.mExternallyManaged = externallyManaged;
+ this.mPublicVolume = mPublicVolume;
}
private MediaVolume (Parcel in) {
@@ -88,6 +113,8 @@
this.mUser = in.readParcelable(null);
this.mPath = new File(in.readString());
this.mId = in.readString();
+ this.mExternallyManaged = in.readInt() != 0;
+ this.mPublicVolume = in.readInt() != 0;
}
@Override
@@ -95,34 +122,46 @@
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
MediaVolume that = (MediaVolume) obj;
+ // We consciously don't compare the path, because:
+ // 1. On unmount events, the returned path for StorageVolumes is
+ // 'null', and different from a mounted volume.
+ // 2. A volume with a certain ID should never be mounted in two different paths, anyway
return Objects.equals(mName, that.mName) &&
Objects.equals(mUser, that.mUser) &&
- Objects.equals(mPath, that.mPath) &&
- Objects.equals(mId, that.mId);
+ Objects.equals(mId, that.mId) &&
+ (mExternallyManaged == that.mExternallyManaged);
}
@Override
public int hashCode() {
- return Objects.hash(mName, mUser, mPath, mId);
+ return Objects.hash(mName, mUser, mId, mExternallyManaged);
}
public boolean isVisibleToUser(UserHandle user) {
return mUser == null || user.equals(mUser);
}
+ /**
+ * Adding NewApi Suppress Lint to fix some build errors after making
+ * {@link StorageVolume#getOwner()} a public Api
+ */
+ // TODO(b/213658045) : Remove this once the related changes are submitted.
+ @SuppressLint("NewApi")
@NonNull
public static MediaVolume fromStorageVolume(StorageVolume storageVolume) {
String name = storageVolume.getMediaStoreVolumeName();
UserHandle user = storageVolume.getOwner();
File path = storageVolume.getDirectory();
String id = storageVolume.getId();
- return new MediaVolume(name, user, path, id);
+ boolean externallyManaged =
+ SdkLevel.isAtLeastT() ? storageVolume.isExternallyManaged() : false;
+ boolean publicVolume = !externallyManaged && !storageVolume.isPrimary();
+ return new MediaVolume(name, user, path, id, externallyManaged, publicVolume);
}
public static MediaVolume fromInternal() {
String name = MediaStore.VOLUME_INTERNAL;
-
- return new MediaVolume(name, null, null, null);
+ return new MediaVolume(name, null, null, null, false, false);
}
@Override
@@ -136,12 +175,15 @@
dest.writeParcelable(mUser, flags);
dest.writeString(mPath.toString());
dest.writeString(mId);
+ dest.writeInt(mExternallyManaged ? 1 : 0);
+ dest.writeInt(mPublicVolume ? 1 : 0);
}
@Override
public String toString() {
return "MediaVolume name: [" + mName + "] id: [" + mId + "] user: [" + mUser + "] path: ["
- + mPath + "]";
+ + mPath + "] externallyManaged: [" + mExternallyManaged + "] mPublicVolume: ["
+ + mPublicVolume + "]";
}
public static final @android.annotation.NonNull Creator<MediaVolume> CREATOR
diff --git a/src/com/android/providers/media/PermissionActivity.java b/src/com/android/providers/media/PermissionActivity.java
index ee37448..a79890a 100644
--- a/src/com/android/providers/media/PermissionActivity.java
+++ b/src/com/android/providers/media/PermissionActivity.java
@@ -17,6 +17,8 @@
package com.android.providers.media;
import static com.android.providers.media.MediaProvider.AUDIO_MEDIA_ID;
+import static com.android.providers.media.MediaProvider.AUDIO_PLAYLISTS_ID;
+import static com.android.providers.media.MediaProvider.FILES_ID;
import static com.android.providers.media.MediaProvider.IMAGES_MEDIA_ID;
import static com.android.providers.media.MediaProvider.VIDEO_MEDIA_ID;
import static com.android.providers.media.MediaProvider.collectUris;
@@ -25,7 +27,10 @@
import static com.android.providers.media.util.PermissionUtils.checkPermissionAccessMediaLocation;
import static com.android.providers.media.util.PermissionUtils.checkPermissionManageMedia;
import static com.android.providers.media.util.PermissionUtils.checkPermissionManager;
+import static com.android.providers.media.util.PermissionUtils.checkPermissionReadAudio;
+import static com.android.providers.media.util.PermissionUtils.checkPermissionReadImages;
import static com.android.providers.media.util.PermissionUtils.checkPermissionReadStorage;
+import static com.android.providers.media.util.PermissionUtils.checkPermissionReadVideo;
import android.app.Activity;
import android.app.AlertDialog;
@@ -49,6 +54,7 @@
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.provider.MediaStore;
@@ -70,8 +76,10 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.modules.utils.build.SdkLevel;
import com.android.providers.media.MediaProvider.LocalUriMatcher;
import com.android.providers.media.util.Metrics;
+import com.android.providers.media.util.StringUtils;
import java.io.IOException;
import java.util.ArrayList;
@@ -117,6 +125,13 @@
progressDialog.show();
};
+ private boolean mShouldCheckReadAudio;
+ private boolean mShouldCheckReadAudioOrReadVideo;
+ private boolean mShouldCheckReadImages;
+ private boolean mShouldCheckReadVideo;
+ private boolean mShouldCheckMediaPermissions;
+ private boolean mShouldForceShowingDialog;
+
private static final Long LEAST_SHOW_PROGRESS_TIME_MS = 300L;
private static final Long BEFORE_SHOW_PROGRESS_TIME_MS = 300L;
@@ -159,7 +174,7 @@
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
getWindow().setDimAmount(0.0f);
-
+ boolean isTargetSdkAtLeastT = false;
// All untrusted input values here were validated when generating the
// original PendingIntent
try {
@@ -169,6 +184,8 @@
appInfo = resolveCallingAppInfo();
label = resolveAppLabel(appInfo);
verb = resolveVerb();
+ isTargetSdkAtLeastT = appInfo.targetSdkVersion > Build.VERSION_CODES.S_V2;
+ mShouldCheckMediaPermissions = isTargetSdkAtLeastT && SdkLevel.isAtLeastT();
data = resolveData();
volumeName = MediaStore.getVolumeName(uris.get(0));
} catch (Exception e) {
@@ -181,8 +198,23 @@
// Create Progress dialog
createProgressDialog();
- if (!shouldShowActionDialog(this, -1 /* pid */, appInfo.uid, getCallingPackage(),
- null /* attributionTag */, verb)) {
+ final boolean shouldShowActionDialog;
+ if (mShouldCheckMediaPermissions) {
+ if (mShouldForceShowingDialog) {
+ shouldShowActionDialog = true;
+ } else {
+ shouldShowActionDialog = shouldShowActionDialog(this, -1 /* pid */, appInfo.uid,
+ getCallingPackage(), null /* attributionTag */, verb,
+ mShouldCheckMediaPermissions, mShouldCheckReadAudio, mShouldCheckReadImages,
+ mShouldCheckReadVideo, mShouldCheckReadAudioOrReadVideo,
+ isTargetSdkAtLeastT);
+ }
+ } else {
+ shouldShowActionDialog = shouldShowActionDialog(this, -1 /* pid */, appInfo.uid,
+ getCallingPackage(), null /* attributionTag */, verb);
+ }
+
+ if (!shouldShowActionDialog) {
onPositiveAction(null, 0);
return;
}
@@ -240,7 +272,9 @@
@Override
public void onDestroy() {
super.onDestroy();
- mHandler.removeCallbacks(mShowProgressDialogRunnable);
+ if (mHandler != null) {
+ mHandler.removeCallbacks(mShowProgressDialogRunnable);
+ }
// Cancel and interrupt the AsyncTask of the positive action. This avoids
// calling the old activity during "onPostExecute", but the AsyncTask could
// still finish its background task. For now we are ok with:
@@ -375,6 +409,18 @@
@VisibleForTesting
static boolean shouldShowActionDialog(@NonNull Context context, int pid, int uid,
@NonNull String packageName, @Nullable String attributionTag, @NonNull String verb) {
+ return shouldShowActionDialog(context, pid, uid, packageName, attributionTag,
+ verb, /* shouldCheckMediaPermissions */ false, /* shouldCheckReadAudio */ false,
+ /* shouldCheckReadImages */ false, /* shouldCheckReadVideo */ false,
+ /* mShouldCheckReadAudioOrReadVideo */ false, /* isTargetSdkAtLeastT */ false);
+ }
+
+ @VisibleForTesting
+ static boolean shouldShowActionDialog(@NonNull Context context, int pid, int uid,
+ @NonNull String packageName, @Nullable String attributionTag, @NonNull String verb,
+ boolean shouldCheckMediaPermissions, boolean shouldCheckReadAudio,
+ boolean shouldCheckReadImages, boolean shouldCheckReadVideo,
+ boolean mShouldCheckReadAudioOrReadVideo, boolean isTargetSdkAtLeastT) {
// Favorite-related requests are automatically granted for now; we still
// make developers go through this no-op dialog flow to preserve our
// ability to start prompting in the future
@@ -382,12 +428,49 @@
return false;
}
- // check READ_EXTERNAL_STORAGE and MANAGE_EXTERNAL_STORAGE permissions
- if (!checkPermissionReadStorage(context, pid, uid, packageName, attributionTag)
- && !checkPermissionManager(context, pid, uid, packageName, attributionTag)) {
- Log.d(TAG, "No permission READ_EXTERNAL_STORAGE or MANAGE_EXTERNAL_STORAGE");
- return true;
+ // no MANAGE_EXTERNAL_STORAGE permission
+ if (!checkPermissionManager(context, pid, uid, packageName, attributionTag)) {
+ if (shouldCheckMediaPermissions) {
+ // check READ_MEDIA_AUDIO
+ if (shouldCheckReadAudio && !checkPermissionReadAudio(context, pid, uid,
+ packageName, attributionTag, isTargetSdkAtLeastT)) {
+ Log.d(TAG, "No permission READ_MEDIA_AUDIO or MANAGE_EXTERNAL_STORAGE");
+ return true;
+ }
+
+ // check READ_MEDIA_IMAGES
+ if (shouldCheckReadImages && !checkPermissionReadImages(context, pid, uid,
+ packageName, attributionTag, isTargetSdkAtLeastT)) {
+ Log.d(TAG, "No permission READ_MEDIA_IMAGES or MANAGE_EXTERNAL_STORAGE");
+ return true;
+ }
+
+ // check READ_MEDIA_VIDEO
+ if (shouldCheckReadVideo && !checkPermissionReadVideo(context, pid, uid,
+ packageName, attributionTag, isTargetSdkAtLeastT)) {
+ Log.d(TAG, "No permission READ_MEDIA_VIDEO or MANAGE_EXTERNAL_STORAGE");
+ return true;
+ }
+
+ // For subtitle case, check READ_MEDIA_AUDIO or READ_MEDIA_VIDEO
+ if (mShouldCheckReadAudioOrReadVideo
+ && !checkPermissionReadAudio(context, pid, uid, packageName, attributionTag,
+ isTargetSdkAtLeastT)
+ && !checkPermissionReadVideo(context, pid, uid, packageName, attributionTag,
+ isTargetSdkAtLeastT)) {
+ Log.d(TAG, "No permission READ_MEDIA_AUDIO, READ_MEDIA_VIDEO or "
+ + "MANAGE_EXTERNAL_STORAGE");
+ return true;
+ }
+ } else {
+ // check READ_EXTERNAL_STORAGE
+ if (!checkPermissionReadStorage(context, pid, uid, packageName, attributionTag)) {
+ Log.d(TAG, "No permission READ_EXTERNAL_STORAGE or MANAGE_EXTERNAL_STORAGE");
+ return true;
+ }
+ }
}
+
// check MANAGE_MEDIA permission
if (!checkPermissionManageMedia(context, pid, uid, packageName, attributionTag)) {
Log.d(TAG, "No permission MANAGE_MEDIA");
@@ -483,13 +566,41 @@
private @NonNull String resolveData() {
final LocalUriMatcher matcher = new LocalUriMatcher(MediaStore.AUTHORITY);
final int firstMatch = matcher.matchUri(uris.get(0), false);
+ parseDataToCheckPermissions(firstMatch);
+ boolean isMixedTypes = false;
+
for (int i = 1; i < uris.size(); i++) {
final int match = matcher.matchUri(uris.get(i), false);
if (match != firstMatch) {
+ // If we don't need to check new permission, we can return DATA_GENERIC here. We
+ // don't need to resolve the other uris.
+ if (!mShouldCheckMediaPermissions) {
+ return DATA_GENERIC;
+ }
// Any mismatch means we need to use generic strings
- return DATA_GENERIC;
+ isMixedTypes = true;
+ }
+
+ parseDataToCheckPermissions(match);
+
+ if (isMixedTypes && mShouldForceShowingDialog) {
+ // Already know the data is mixed types and should force showing dialog. Don't need
+ // to resolve the other uris.
+ break;
+ }
+
+ if (mShouldCheckReadAudio && mShouldCheckReadImages && mShouldCheckReadVideo
+ && mShouldCheckReadAudioOrReadVideo) {
+ // Already need to check all permissions for the mixed types. Don't need to resolve
+ // the other uris.
+ break;
}
}
+
+ if (isMixedTypes) {
+ return DATA_GENERIC;
+ }
+
switch (firstMatch) {
case AUDIO_MEDIA_ID: return DATA_AUDIO;
case VIDEO_MEDIA_ID: return DATA_VIDEO;
@@ -498,17 +609,42 @@
}
}
+ private void parseDataToCheckPermissions(int match) {
+ switch (match) {
+ case AUDIO_MEDIA_ID:
+ case AUDIO_PLAYLISTS_ID:
+ mShouldCheckReadAudio = true;
+ break;
+ case VIDEO_MEDIA_ID:
+ mShouldCheckReadVideo = true;
+ break;
+ case IMAGES_MEDIA_ID:
+ mShouldCheckReadImages = true;
+ break;
+ case FILES_ID:
+ // PermissionActivity is not exported. And we have a check in
+ // MediaProvider#createRequest method. If it matches FILES_ID, it is subtitle case.
+ // Check audio or video for it.
+ mShouldCheckReadAudioOrReadVideo = true;
+ break;
+ default:
+ // It is not the expected case. Force showing the dialog
+ mShouldForceShowingDialog = true;
+
+ }
+ }
+
/**
* Resolve the dialog title string to be displayed to the user. All
* arguments have been bound and this string is ready to be displayed.
*/
private @Nullable CharSequence resolveTitleText() {
final String resName = "permission_" + verb + "_" + data;
- final int resId = getResources().getIdentifier(resName, "plurals",
+ final int resId = getResources().getIdentifier(resName, "string",
getResources().getResourcePackageName(R.string.app_label));
if (resId != 0) {
final int count = uris.size();
- final CharSequence text = getResources().getQuantityText(resId, count);
+ final CharSequence text = StringUtils.getICUFormatString(getResources(), count, resId);
return TextUtils.expandTemplate(text, label, String.valueOf(count));
} else {
// We always need a string to prompt the user with
@@ -522,11 +658,11 @@
*/
private @Nullable CharSequence resolveProgressMessageText() {
final String resName = "permission_progress_" + verb + "_" + data;
- final int resId = getResources().getIdentifier(resName, "plurals",
+ final int resId = getResources().getIdentifier(resName, "string",
getResources().getResourcePackageName(R.string.app_label));
if (resId != 0) {
final int count = uris.size();
- final CharSequence text = getResources().getQuantityText(resId, count);
+ final CharSequence text = StringUtils.getICUFormatString(getResources(), count, resId);
return TextUtils.expandTemplate(text, String.valueOf(count));
} else {
// Only some actions have a progress message string; it's okay if
@@ -656,7 +792,9 @@
final ImageView thumbFull = bodyView.requireViewById(R.id.thumb_full);
if (result.full != null) {
result.bindFull(thumbFull);
- } else {
+ } else if (result.thumbnail != null) {
+ result.bindThumbnail(thumbFull, /* shouldClip */ false);
+ } else if (result.mimeIcon != null) {
thumbFull.setScaleType(ImageView.ScaleType.FIT_CENTER);
thumbFull.setBackground(new ColorDrawable(getColor(R.color.thumb_gray_color)));
result.bindMimeIcon(thumbFull);
@@ -691,9 +829,11 @@
final int shownCount = Math.min(visualResults.size(), MAX_THUMBS - 1);
final int moreCount = results.size() - shownCount;
- final CharSequence moreText = TextUtils.expandTemplate(res.getQuantityText(
- R.plurals.permission_more_thumb, moreCount), String.valueOf(moreCount));
-
+ final CharSequence moreText =
+ TextUtils.expandTemplate(
+ StringUtils.getICUFormatString(
+ res, moreCount, R.string.permission_more_thumb),
+ String.valueOf(moreCount));
thumbMoreText.setText(moreText);
thumbMoreContainer.setVisibility(View.VISIBLE);
gradientView.setVisibility(View.VISIBLE);
@@ -710,8 +850,8 @@
final Description desc = visualResults.get(i);
final ImageView imageView = thumbs.get(i);
if (desc.thumbnail != null) {
- desc.bindThumbnail(imageView);
- } else {
+ desc.bindThumbnail(imageView, /* shouldClip */ true);
+ } else if (desc.mimeIcon != null) {
desc.bindMimeIcon(imageView);
}
}
@@ -731,8 +871,11 @@
if (list.size() >= MAX_THUMBS && results.size() > list.size()) {
final int moreCount = results.size() - list.size();
- final CharSequence moreText = TextUtils.expandTemplate(res.getQuantityText(
- R.plurals.permission_more_text, moreCount), String.valueOf(moreCount));
+ final CharSequence moreText =
+ TextUtils.expandTemplate(
+ StringUtils.getICUFormatString(
+ res, moreCount, R.string.permission_more_text),
+ String.valueOf(moreCount));
list.add(moreText);
break;
}
@@ -804,12 +947,12 @@
return thumbnail != null || full != null || mimeIcon != null;
}
- public void bindThumbnail(ImageView imageView) {
+ public void bindThumbnail(ImageView imageView, boolean shouldClip) {
Objects.requireNonNull(thumbnail);
imageView.setImageBitmap(thumbnail);
imageView.setContentDescription(contentDescription);
imageView.setVisibility(View.VISIBLE);
- imageView.setClipToOutline(true);
+ imageView.setClipToOutline(shouldClip);
}
public void bindFull(ImageView imageView) {
diff --git a/src/com/android/providers/media/PickerUriResolver.java b/src/com/android/providers/media/PickerUriResolver.java
new file mode 100644
index 0000000..dde22ce
--- /dev/null
+++ b/src/com/android/providers/media/PickerUriResolver.java
@@ -0,0 +1,302 @@
+/*
+ * 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;
+
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static com.android.providers.media.photopicker.util.CursorUtils.getCursorString;
+import static com.android.providers.media.util.FileUtils.toFuseFile;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
+import android.provider.MediaStore;
+import android.provider.CloudMediaProviderContract;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.providers.media.photopicker.data.PickerDbFacade;
+import com.android.providers.media.photopicker.data.model.UserId;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.List;
+
+/**
+ * Utility class for Picker Uris, it handles (includes permission checks, incoming args
+ * validations etc) and redirects picker URIs to the correct resolver.
+ */
+public class PickerUriResolver {
+ private static final String TAG = "PickerUriResolver";
+
+ private static final String PICKER_SEGMENT = "picker";
+ private static final String PICKER_INTERNAL_SEGMENT = "picker_internal";
+ /** A uri with prefix "content://media/picker" is considered as a picker uri */
+ public static final Uri PICKER_URI = MediaStore.AUTHORITY_URI.buildUpon().
+ appendPath(PICKER_SEGMENT).build();
+ /**
+ * Internal picker URI with prefix "content://media/picker_internal" to retrieve merged
+ * and deduped cloud and local items.
+ */
+ public static final Uri PICKER_INTERNAL_URI = MediaStore.AUTHORITY_URI.buildUpon().
+ appendPath(PICKER_INTERNAL_SEGMENT).build();
+
+ public static final String MEDIA_PATH = "media";
+ public static final String ALBUM_PATH = "albums";
+
+ private final Context mContext;
+ private final PickerDbFacade mDbFacade;
+
+ PickerUriResolver(Context context, PickerDbFacade dbFacade) {
+ mContext = context;
+ mDbFacade = dbFacade;
+ }
+
+ public ParcelFileDescriptor openFile(Uri uri, String mode, CancellationSignal signal,
+ int callingPid, int callingUid) throws FileNotFoundException {
+ if (ParcelFileDescriptor.parseMode(mode) != ParcelFileDescriptor.MODE_READ_ONLY) {
+ throw new SecurityException("PhotoPicker Uris can only be accessed to read."
+ + " Uri: " + uri);
+ }
+
+ checkUriPermission(uri, callingPid, callingUid);
+
+ final ContentResolver resolver;
+ try {
+ resolver = getContentResolverForUserId(uri);
+ } catch (IllegalStateException e) {
+ // This is to be consistent with MediaProvider's response when a file is not found.
+ Log.e(TAG, "No item at " + uri, e);
+ throw new FileNotFoundException("No item at " + uri);
+ }
+ if (canHandleUriInUser(uri)) {
+ return openPickerFile(uri);
+ }
+ return resolver.openFile(uri, mode, signal);
+ }
+
+ public AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts,
+ CancellationSignal signal, int callingPid, int callingUid)
+ throws FileNotFoundException {
+ checkUriPermission(uri, callingPid, callingUid);
+
+ final ContentResolver resolver;
+ try {
+ resolver = getContentResolverForUserId(uri);
+ } catch (IllegalStateException e) {
+ // This is to be consistent with MediaProvider's response when a file is not found.
+ Log.e(TAG, "No item at " + uri, e);
+ throw new FileNotFoundException("No item at " + uri);
+ }
+ if (canHandleUriInUser(uri)) {
+ return new AssetFileDescriptor(openPickerFile(uri), 0,
+ AssetFileDescriptor.UNKNOWN_LENGTH);
+ }
+ return resolver.openTypedAssetFile(uri, mimeTypeFilter, opts, signal);
+ }
+
+ public Cursor query(Uri uri, String[] projection, int callingPid, int callingUid) {
+ checkUriPermission(uri, callingPid, callingUid);
+ try {
+ return queryInternal(uri, projection);
+ } catch (IllegalStateException e) {
+ // This is to be consistent with MediaProvider, it returns an empty cursor if the row
+ // does not exist.
+ Log.e(TAG, "File not found for uri: " + uri, e);
+ return new MatrixCursor(projection == null ? new String[] {} : projection);
+ }
+ }
+
+ private Cursor queryInternal(Uri uri, String[] projection) {
+ final ContentResolver resolver = getContentResolverForUserId(uri);
+
+ if (canHandleUriInUser(uri)) {
+ if (projection == null || projection.length == 0) {
+ projection = new String[]{
+ MediaStore.PickerMediaColumns.DISPLAY_NAME,
+ MediaStore.PickerMediaColumns.DATA,
+ MediaStore.PickerMediaColumns.MIME_TYPE,
+ MediaStore.PickerMediaColumns.DATE_TAKEN,
+ MediaStore.PickerMediaColumns.SIZE,
+ MediaStore.PickerMediaColumns.DURATION_MILLIS
+ };
+ }
+
+ return queryPickerUri(uri, projection);
+ }
+ return resolver.query(uri, /* projection */ null, /* queryArgs */ null,
+ /* cancellationSignal */ null);
+ }
+
+ public String getType(@NonNull Uri uri) {
+ // There's no permission check because ContentProviders allow anyone to check the mimetype
+ // of a URI
+ try (Cursor cursor = queryInternal(uri, new String[]{MediaStore.MediaColumns.MIME_TYPE})) {
+ if (cursor != null && cursor.getCount() == 1 && cursor.moveToFirst()) {
+ return getCursorString(cursor,
+ CloudMediaProviderContract.MediaColumns.MIME_TYPE);
+ }
+ }
+
+ throw new IllegalArgumentException("Failed to getType for uri: " + uri);
+ }
+
+ public static Uri getMediaUri(String authority) {
+ return Uri.parse("content://" + authority + "/"
+ + CloudMediaProviderContract.URI_PATH_MEDIA);
+ }
+
+ public static Uri getDeletedMediaUri(String authority) {
+ return Uri.parse("content://" + authority + "/"
+ + CloudMediaProviderContract.URI_PATH_DELETED_MEDIA);
+ }
+
+ public static Uri getMediaCollectionInfoUri(String authority) {
+ return Uri.parse("content://" + authority + "/"
+ + CloudMediaProviderContract.URI_PATH_MEDIA_COLLECTION_INFO);
+ }
+
+ public static Uri getAlbumUri(String authority) {
+ return Uri.parse("content://" + authority + "/"
+ + CloudMediaProviderContract.URI_PATH_ALBUM);
+ }
+
+ public static Uri createSurfaceControllerUri(String authority) {
+ return Uri.parse("content://" + authority + "/"
+ + CloudMediaProviderContract.URI_PATH_SURFACE_CONTROLLER);
+ }
+
+ private ParcelFileDescriptor openPickerFile(Uri uri) throws FileNotFoundException {
+ final File file = getPickerFileFromUri(uri);
+ if (file == null) {
+ throw new FileNotFoundException("File not found for uri: " + uri);
+ }
+ return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
+ }
+
+ @VisibleForTesting
+ File getPickerFileFromUri(Uri uri) {
+ final String[] projection = new String[] { MediaStore.PickerMediaColumns.DATA };
+ try (Cursor cursor = queryPickerUri(uri, projection)) {
+ if (cursor != null && cursor.getCount() == 1 && cursor.moveToFirst()) {
+ String path = getCursorString(cursor, MediaStore.PickerMediaColumns.DATA);
+ // First replace /sdcard with /storage/emulated path
+ path = path.replaceFirst("/sdcard", "/storage/emulated/" + MediaStore.MY_USER_ID);
+ // Then convert /storage/emulated patht to /mnt/user/ path
+ return toFuseFile(new File(path));
+ }
+ }
+ return null;
+ }
+
+ @VisibleForTesting
+ Cursor queryPickerUri(Uri uri, String[] projection) {
+ uri = unwrapProviderUri(uri);
+ return mDbFacade.queryMediaIdForApps(uri.getHost(), uri.getLastPathSegment(),
+ projection);
+ }
+
+ public static Uri wrapProviderUri(Uri uri, int userId) {
+ final List<String> segments = uri.getPathSegments();
+ if (segments.size() != 2) {
+ throw new IllegalArgumentException("Unexpected provider URI: " + uri);
+ }
+
+ Uri.Builder builder = initializeUriBuilder(MediaStore.AUTHORITY);
+ builder.appendPath(PICKER_SEGMENT);
+ builder.appendPath(String.valueOf(userId));
+ builder.appendPath(uri.getHost());
+
+ for (int i = 0; i < segments.size(); i++) {
+ builder.appendPath(segments.get(i));
+ }
+
+ return builder.build();
+ }
+
+ @VisibleForTesting
+ static Uri unwrapProviderUri(Uri uri) {
+ List<String> segments = uri.getPathSegments();
+ if (segments.size() != 5) {
+ throw new IllegalArgumentException("Unexpected picker provider URI: " + uri);
+ }
+
+ // segments.get(0) == 'picker'
+ final String userId = segments.get(1);
+ final String host = segments.get(2);
+ segments = segments.subList(3, segments.size());
+
+ Uri.Builder builder = initializeUriBuilder(userId + "@" + host);
+
+ for (int i = 0; i < segments.size(); i++) {
+ builder.appendPath(segments.get(i));
+ }
+ return builder.build();
+ }
+
+ private static Uri.Builder initializeUriBuilder(String authority) {
+ final Uri.Builder builder = Uri.EMPTY.buildUpon();
+ builder.scheme("content");
+ builder.encodedAuthority(authority);
+
+ return builder;
+ }
+
+ @VisibleForTesting
+ static int getUserId(Uri uri) {
+ // content://media/picker/<user-id>/<media-id>/...
+ return Integer.parseInt(uri.getPathSegments().get(1));
+ }
+
+ private void checkUriPermission(Uri uri, int pid, int uid) {
+ if (!isSelf(uid) && mContext.checkUriPermission(uri, pid, uid,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION) != PERMISSION_GRANTED) {
+ throw new SecurityException("Calling uid ( " + uid + " ) does not have permission to " +
+ "access picker uri: " + uri);
+ }
+ }
+
+ private boolean isSelf(int uid) {
+ return UserHandle.getAppId(android.os.Process.myUid()) == UserHandle.getAppId(uid);
+ }
+
+ private boolean canHandleUriInUser(Uri uri) {
+ // If MPs user_id matches the URIs user_id, we can handle this URI in this MP user,
+ // otherwise, we'd have to re-route to MP matching URI user_id
+ return getUserId(uri) == mContext.getUser().getIdentifier();
+ }
+
+ @VisibleForTesting
+ ContentResolver getContentResolverForUserId(Uri uri) {
+ final UserId userId = UserId.of(UserHandle.of(getUserId(uri)));
+ try {
+ return userId.getContentResolver(mContext);
+ } catch (NameNotFoundException e) {
+ throw new IllegalStateException("Cannot find content resolver for uri: " + uri, e);
+ }
+ }
+}
diff --git a/src/com/android/providers/media/TranscodeHelper.java b/src/com/android/providers/media/TranscodeHelper.java
index 488c40d..cdaa740 100644
--- a/src/com/android/providers/media/TranscodeHelper.java
+++ b/src/com/android/providers/media/TranscodeHelper.java
@@ -19,6 +19,7 @@
import android.net.Uri;
import android.os.Bundle;
import java.io.PrintWriter;
+import java.util.List;
/** Interface over MediaTranscodeManager access */
public interface TranscodeHelper {
@@ -28,7 +29,7 @@
public boolean transcode(String src, String dst, int uid, int reason);
- public String getIoPath(String path, int uid);
+ public String prepareIoPath(String path, int uid);
public int shouldTranscode(String path, int uid, Bundle bundle);
@@ -43,4 +44,6 @@
public boolean deleteCachedTranscodeFile(long rowId);
public void dump(PrintWriter writer);
+
+ public List<String> getSupportedRelativePaths();
}
diff --git a/src/com/android/providers/media/TranscodeHelperImpl.java b/src/com/android/providers/media/TranscodeHelperImpl.java
index 8017a59..09e132f 100644
--- a/src/com/android/providers/media/TranscodeHelperImpl.java
+++ b/src/com/android/providers/media/TranscodeHelperImpl.java
@@ -32,6 +32,7 @@
import static com.android.providers.media.MediaProviderStatsLog.TRANSCODING_DATA__TRANSCODE_RESULT__FAIL;
import static com.android.providers.media.MediaProviderStatsLog.TRANSCODING_DATA__TRANSCODE_RESULT__SUCCESS;
import static com.android.providers.media.MediaProviderStatsLog.TRANSCODING_DATA__TRANSCODE_RESULT__UNDEFINED;
+import static com.android.providers.media.util.SyntheticPathUtils.createSparseFile;
import android.annotation.IntRange;
import android.annotation.LongDef;
@@ -52,6 +53,7 @@
import android.content.res.XmlResourceParser;
import android.database.Cursor;
import android.media.ApplicationMediaCapabilities;
+import android.media.MediaCodec;
import android.media.MediaFeature;
import android.media.MediaFormat;
import android.media.MediaTranscodingManager;
@@ -90,11 +92,12 @@
import androidx.core.app.NotificationManagerCompat;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.BackgroundThread;
import com.android.modules.utils.build.SdkLevel;
-import com.android.providers.media.util.BackgroundThread;
import com.android.providers.media.util.FileUtils;
import com.android.providers.media.util.ForegroundThread;
import com.android.providers.media.util.SQLiteQueryBuilder;
+import com.android.providers.media.util.StringUtils;
import java.io.BufferedReader;
import java.io.File;
@@ -103,13 +106,14 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
-import java.io.RandomAccessFile;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -144,6 +148,9 @@
private static final int MY_UID = android.os.Process.myUid();
private static final int MAX_TRANSCODE_DURATION_MS = (int) TimeUnit.MINUTES.toMillis(1);
+ // Whether the device has HDR plugin for transcoding HDR to SDR video.
+ private boolean mHasHdrPlugin = false;
+
/**
* Force enable an app to support the HEVC media capability
*
@@ -202,7 +209,7 @@
}
/** Coefficient to 'guess' how long a transcoding session might take */
- private static final double TRANSCODING_TIMEOUT_COEFFICIENT = 2;
+ private static final double TRANSCODING_TIMEOUT_COEFFICIENT = 10;
/** Coefficient to 'guess' how large a transcoded file might be */
private static final double TRANSCODING_SIZE_COEFFICIENT = 2;
@@ -225,6 +232,7 @@
private final StorageManager mStorageManager;
private final ActivityManager mActivityManager;
private final File mTranscodeDirectory;
+ private final List<String> mSupportedRelativePaths;
@GuardedBy("mLock")
private UUID mTranscodeVolumeUuid;
@@ -276,6 +284,9 @@
MAX_TRANSCODE_DURATION_MS);
mTranscodeDenialController = new TranscodeDenialController(mActivityManager,
mTranscodingUiNotifier, maxTranscodeDurationMs);
+ mSupportedRelativePaths = verifySupportedRelativePaths(StringUtils.getStringArrayConfig(
+ mContext, R.array.config_supported_transcoding_relative_paths));
+ mHasHdrPlugin = hasHDRPlugin();
parseTranscodeCompatManifest();
// The storage namespace is a boot namespace so we actually don't expect this to be changed
@@ -283,6 +294,39 @@
mMediaProvider.addOnPropertiesChangedListener(properties -> parseTranscodeCompatManifest());
}
+ private boolean hasHDRPlugin() {
+ MediaCodec decoder = null;
+ boolean hasPlugin = false;
+ try {
+ decoder = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_HEVC);
+ // We could query the HDR plugin with any resolution. But as normal HDR video is at
+ // least 1080P(1920x1080), so we create a 1080P video format to query.
+ MediaFormat decoderFormat = MediaFormat.createVideoFormat(
+ MediaFormat.MIMETYPE_VIDEO_HEVC, 1920, 1080);
+ decoderFormat.setInteger(MediaFormat.KEY_COLOR_TRANSFER_REQUEST,
+ MediaFormat.COLOR_TRANSFER_SDR_VIDEO);
+ decoder.configure(decoderFormat, null, null, 0);
+ MediaFormat inputFormat = decoder.getInputFormat();
+ if (inputFormat.getInteger(MediaFormat.KEY_COLOR_TRANSFER_REQUEST)
+ == MediaFormat.COLOR_TRANSFER_SDR_VIDEO) {
+ hasPlugin = true;
+ }
+ } catch (Exception ioe) {
+ hasPlugin = false;
+ } finally {
+ if (decoder != null) {
+ try {
+ decoder.stop();
+ decoder.release();
+ } catch (Exception e) {
+ Log.w(TAG, "Unable to stop decoder", e);
+ }
+ }
+ }
+ Log.i(TAG, "Device HDR Plugin is available: " + hasPlugin);
+ return hasPlugin;
+ }
+
/**
* Regex that matches path of transcode file. The regex only
* matches emulated volume, for files in other volumes we don't
@@ -515,7 +559,7 @@
* @param uid app requesting IO
*
*/
- public String getIoPath(String path, int uid) {
+ public String prepareIoPath(String path, int uid) {
// This can only happen when we are in a version that supports transcoding.
// So, no need to check for the SDK version here.
@@ -541,18 +585,12 @@
updateTranscodeStatus(path, TRANSCODE_EMPTY);
}
- final File file = new File(path);
- long maxFileSize = (long) (file.length() * 2);
- mTranscodeDirectory.mkdirs();
- try (RandomAccessFile raf = new RandomAccessFile(transcodeFile, "rw")) {
- raf.setLength(maxFileSize);
- } catch (IOException e) {
- Log.e(TAG, "Failed to initialise transcoding for file " + path, e);
- transcodeFile.delete();
+ final long maxFileSize = (long) (new File(path).length() * 2);
+ if (createSparseFile(transcodeFile, maxFileSize)) {
return transcodePath;
}
- return transcodePath;
+ return "";
}
private static int getMediaCapabilitiesUid(int uid, Bundle bundle) {
@@ -778,10 +816,22 @@
}
int result = 0;
+ boolean isHdr10Plus = isHdr10Plus(cursor.getInt(1), cursor.getInt(2));
+ // If the video is a HDR video and the device does not have HDR plugin, we will return
+ // the original file regardless whether the app supports HEVC due to not all the devices
+ // support transcoding 10bit HEVC to 8bit AVC. This check needs to be removed when
+ // devices add support for it.
+ boolean isTranscodeUnsupported = isHdr10Plus && !mHasHdrPlugin;
+ if (isTranscodeUnsupported) {
+ return Pair.create(0, 0L);
+ }
+
if (isHevc(cursor.getString(0))) {
result |= FLAG_HEVC;
}
- if (isHdr10Plus(cursor.getInt(1), cursor.getInt(2))) {
+ // Set the HDR flag if the device has HDR plugin. If HDR plugin is not available,
+ // we will make the transcode decision based on whether the app supports HEVC or not.
+ if (isHdr10Plus) {
result |= FLAG_HDR_10_PLUS;
}
return Pair.create(result, cursor.getLong(3));
@@ -803,14 +853,38 @@
}
public boolean supportsTranscode(String path) {
- File file = new File(path);
- String name = file.getName();
- final String cameraRelativePath =
- String.format("%s/%s/", Environment.DIRECTORY_DCIM, DIRECTORY_CAMERA);
+ final File file = new File(path);
+ final String name = file.getName();
+ final String relativePath = FileUtils.extractRelativePath(path);
- return !isTranscodeFile(path) && name.toLowerCase(Locale.ROOT).endsWith(".mp4")
- && path.startsWith("/storage/emulated/")
- && cameraRelativePath.equalsIgnoreCase(FileUtils.extractRelativePath(path));
+ if (isTranscodeFile(path) || !name.toLowerCase(Locale.ROOT).endsWith(".mp4")
+ || !path.startsWith("/storage/emulated/")) {
+ return false;
+ }
+
+ for (String supportedRelativePath : mSupportedRelativePaths) {
+ if (supportedRelativePath.equalsIgnoreCase(relativePath)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static List<String> verifySupportedRelativePaths(List<String> relativePaths) {
+ final List<String> verifiedPaths = new ArrayList<>();
+ final String lowerCaseDcimDir = Environment.DIRECTORY_DCIM.toLowerCase(Locale.ROOT) + "/";
+
+ for (String path : relativePaths) {
+ if (path.toLowerCase(Locale.ROOT).startsWith(lowerCaseDcimDir) && path.endsWith("/")) {
+ verifiedPaths.add(path);
+ } else {
+ Log.w(TAG, "Transcoding relative path must be a descendant of DCIM/ and end with"
+ + " '/'. Ignoring: " + path);
+ }
+ }
+
+ return verifiedPaths;
}
private Optional<Boolean> checkAppCompatSupport(int uid, int fileFlags) {
@@ -1147,6 +1221,7 @@
.setSourceFileDescriptor(srcPfd)
.setDestinationFileDescriptor(dstPfd)
.build();
+
TranscodingSession session = mediaTranscodeManager.enqueueRequest(request,
ForegroundThread.getExecutor(),
s -> {
@@ -1458,11 +1533,16 @@
synchronized (mLock) {
writer.println("mAppCompatMediaCapabilities=" + mAppCompatMediaCapabilities);
writer.println("mStorageTranscodingSessions=" + mStorageTranscodingSessions);
-
+ writer.println("mSupportedTranscodingRelativePaths=" + mSupportedRelativePaths);
+ writer.println("mHasHdrPlugin=" + mHasHdrPlugin);
dumpFinishedSessions(writer);
}
}
+ public List<String> getSupportedRelativePaths() {
+ return mSupportedRelativePaths;
+ }
+
private void dumpFinishedSessions(PrintWriter writer) {
synchronized (mLock) {
writer.println("mSuccessfulTranscodeSessions=" + mSuccessfulTranscodeSessions.keySet());
diff --git a/src/com/android/providers/media/TranscodeHelperNoOp.java b/src/com/android/providers/media/TranscodeHelperNoOp.java
index 355f21d..77d3118 100644
--- a/src/com/android/providers/media/TranscodeHelperNoOp.java
+++ b/src/com/android/providers/media/TranscodeHelperNoOp.java
@@ -20,6 +20,9 @@
import android.os.Bundle;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* No-op transcode helper to avoid loading MediaTranscodeManager classes in Android R
*/
@@ -32,7 +35,7 @@
return false;
}
- public String getIoPath(String path, int uid) {
+ public String prepareIoPath(String path, int uid) {
return null;
}
@@ -57,4 +60,8 @@
}
public void dump(PrintWriter writer) {}
+
+ public List<String> getSupportedRelativePaths() {
+ return new ArrayList<String>();
+ }
}
diff --git a/src/com/android/providers/media/VolumeCache.java b/src/com/android/providers/media/VolumeCache.java
index 5495a26..90f7d3b 100644
--- a/src/com/android/providers/media/VolumeCache.java
+++ b/src/com/android/providers/media/VolumeCache.java
@@ -28,14 +28,12 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
-import android.util.LongSparseArray;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import com.android.providers.media.util.FileUtils;
import com.android.providers.media.util.UserCache;
-
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
@@ -88,6 +86,24 @@
}
}
+ /**
+ * @return List of paths to unreliable volumes if any, an empty list otherwise
+ */
+ public @NonNull List<File> getUnreliableVolumePath() throws FileNotFoundException {
+ List<File> unreliableVolumes = new ArrayList<>();
+ synchronized (mLock) {
+ for (MediaVolume volume : mExternalVolumes){
+ final File volPath = volume.getPath();
+ if (volPath != null && volPath.getPath() != null
+ && !volPath.getPath().startsWith("/storage/")){
+ unreliableVolumes.add(volPath);
+ }
+ }
+ }
+
+ return unreliableVolumes;
+ }
+
public @NonNull MediaVolume findVolume(@NonNull String volumeName, @NonNull UserHandle user)
throws FileNotFoundException {
synchronized (mLock) {
diff --git a/src/com/android/providers/media/dao/FileRow.java b/src/com/android/providers/media/dao/FileRow.java
new file mode 100644
index 0000000..3410c0c
--- /dev/null
+++ b/src/com/android/providers/media/dao/FileRow.java
@@ -0,0 +1,157 @@
+/*
+ * 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.
+ */
+
+package com.android.providers.media.dao;
+
+/** DAO object representing database row of Files table of a MediaProvider database. */
+public class FileRow {
+
+ private final long mId;
+ private String mPath;
+ private String mOwnerPackageName;
+ private String mVolumeName;
+ private int mMediaType;
+ private boolean mIsDownload;
+ private boolean mIsPending;
+ private boolean mIsTrashed;
+ private boolean mIsFavorite;
+ private int mSpecialFormat;
+
+ public static class Builder {
+ private final long mId;
+ private String mPath;
+ private String mOwnerPackageName;
+ private String mVolumeName;
+ private int mMediaType;
+ private boolean mIsDownload;
+ private boolean mIsPending;
+ private boolean mIsTrashed;
+ private boolean mIsFavorite;
+ private int mSpecialFormat;
+
+ Builder(long id) {
+ this.mId = id;
+ }
+
+ public Builder setPath(String path) {
+ this.mPath = path;
+ return this;
+ }
+
+ public Builder setOwnerPackageName(String ownerPackageName) {
+ this.mOwnerPackageName = ownerPackageName;
+ return this;
+ }
+
+ public Builder setVolumeName(String volumeName) {
+ this.mVolumeName = volumeName;
+ return this;
+ }
+
+ public Builder setMediaType(int mediaType) {
+ this.mMediaType = mediaType;
+ return this;
+ }
+
+ public Builder setIsDownload(boolean download) {
+ mIsDownload = download;
+ return this;
+ }
+
+ public Builder setIsPending(boolean pending) {
+ mIsPending = pending;
+ return this;
+ }
+
+ public Builder setIsTrashed(boolean trashed) {
+ mIsTrashed = trashed;
+ return this;
+ }
+
+ public Builder setIsFavorite(boolean favorite) {
+ mIsFavorite = favorite;
+ return this;
+ }
+
+ public Builder setSpecialFormat(int specialFormat) {
+ this.mSpecialFormat = specialFormat;
+ return this;
+ }
+
+ public FileRow build() {
+ FileRow fileRow = new FileRow(this.mId);
+ fileRow.mPath = this.mPath;
+ fileRow.mOwnerPackageName = this.mOwnerPackageName;
+ fileRow.mVolumeName = this.mVolumeName;
+ fileRow.mMediaType = this.mMediaType;
+ fileRow.mIsDownload = this.mIsDownload;
+ fileRow.mIsPending = this.mIsPending;
+ fileRow.mIsTrashed = this.mIsTrashed;
+ fileRow.mIsFavorite = this.mIsFavorite;
+ fileRow.mSpecialFormat = this.mSpecialFormat;
+
+ return fileRow;
+ }
+ }
+
+ public static Builder newBuilder(long id) {
+ return new Builder(id);
+ }
+
+ private FileRow(long id) {
+ this.mId = id;
+ }
+
+ public long getId() {
+ return mId;
+ }
+
+ public String getPath() {
+ return mPath;
+ }
+
+ public String getOwnerPackageName() {
+ return mOwnerPackageName;
+ }
+
+ public String getVolumeName() {
+ return mVolumeName;
+ }
+
+ public int getMediaType() {
+ return mMediaType;
+ }
+
+ public boolean isDownload() {
+ return mIsDownload;
+ }
+
+ public boolean isPending() {
+ return mIsPending;
+ }
+
+ public boolean isTrashed() {
+ return mIsTrashed;
+ }
+
+ public boolean isFavorite() {
+ return mIsFavorite;
+ }
+
+ public int getSpecialFormat() {
+ return mSpecialFormat;
+ }
+}
diff --git a/src/com/android/providers/media/fuse/ExternalStorageServiceImpl.java b/src/com/android/providers/media/fuse/ExternalStorageServiceImpl.java
index 1f8f3cd..84f4205 100644
--- a/src/com/android/providers/media/fuse/ExternalStorageServiceImpl.java
+++ b/src/com/android/providers/media/fuse/ExternalStorageServiceImpl.java
@@ -23,6 +23,7 @@
import android.os.Environment;
import android.os.OperationCanceledException;
import android.os.ParcelFileDescriptor;
+import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.provider.MediaStore;
import android.service.storage.ExternalStorageService;
@@ -31,10 +32,13 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.modules.utils.build.SdkLevel;
import com.android.providers.media.MediaProvider;
import com.android.providers.media.MediaService;
import com.android.providers.media.MediaVolume;
+import com.android.modules.utils.BackgroundThread;
+
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
@@ -62,6 +66,17 @@
MediaProvider mediaProvider = getMediaProvider();
+ boolean uncachedMode = false;
+ if (SdkLevel.isAtLeastT()) {
+ StorageVolume vol =
+ getSystemService(StorageManager.class).getStorageVolume(upperFileSystemPath);
+ if (vol != null && vol.isExternallyManaged()) {
+ // Cache should be disabled when the volume is externally managed.
+ Log.i(TAG, "Disabling cache for externally managed volume " + upperFileSystemPath);
+ uncachedMode = true;
+ }
+ }
+
synchronized (sLock) {
if (sFuseDaemons.containsKey(sessionId)) {
Log.w(TAG, "Session already started with id: " + sessionId);
@@ -70,8 +85,13 @@
// We only use the upperFileSystemPath because the media process is mounted as
// REMOUNT_MODE_PASS_THROUGH which guarantees that all /storage paths are bind
// mounts of the lower filesystem.
+ final String[] supportedTranscodingRelativePaths =
+ mediaProvider.getSupportedTranscodingRelativePaths().toArray(new String[0]);
+ final String[] supportedUncachedRelativePaths =
+ mediaProvider.getSupportedUncachedRelativePaths().toArray(new String[0]);
FuseDaemon daemon = new FuseDaemon(mediaProvider, this, deviceFd, sessionId,
- upperFileSystemPath.getPath());
+ upperFileSystemPath.getPath(), uncachedMode,
+ supportedTranscodingRelativePaths, supportedUncachedRelativePaths);
daemon.start();
sFuseDaemons.put(sessionId, daemon);
}
diff --git a/src/com/android/providers/media/fuse/FuseDaemon.java b/src/com/android/providers/media/fuse/FuseDaemon.java
index 1175249..97e14fe 100644
--- a/src/com/android/providers/media/fuse/FuseDaemon.java
+++ b/src/com/android/providers/media/fuse/FuseDaemon.java
@@ -22,7 +22,9 @@
import androidx.annotation.NonNull;
import com.android.internal.annotations.GuardedBy;
+import com.android.providers.media.FdAccessResult;
import com.android.providers.media.MediaProvider;
+import com.android.providers.media.util.FileUtils;
import java.io.File;
import java.io.IOException;
@@ -40,18 +42,27 @@
private final MediaProvider mMediaProvider;
private final int mFuseDeviceFd;
private final String mPath;
+ private final boolean mUncachedMode;
+ private final String[] mSupportedTranscodingRelativePaths;
+ private final String[] mSupportedUncachedRelativePaths;
private final ExternalStorageServiceImpl mService;
@GuardedBy("mLock")
private long mPtr;
public FuseDaemon(@NonNull MediaProvider mediaProvider,
@NonNull ExternalStorageServiceImpl service, @NonNull ParcelFileDescriptor fd,
- @NonNull String sessionId, @NonNull String path) {
+ @NonNull String sessionId, @NonNull String path, boolean uncachedMode,
+ String[] supportedTranscodingRelativePaths, String[] supportedUncachedRelativePaths) {
mMediaProvider = Objects.requireNonNull(mediaProvider);
mService = Objects.requireNonNull(service);
setName(Objects.requireNonNull(sessionId));
mFuseDeviceFd = Objects.requireNonNull(fd).detachFd();
mPath = Objects.requireNonNull(path);
+ mUncachedMode = uncachedMode;
+ mSupportedTranscodingRelativePaths
+ = Objects.requireNonNull(supportedTranscodingRelativePaths);
+ mSupportedUncachedRelativePaths
+ = Objects.requireNonNull(supportedUncachedRelativePaths);
}
/** Starts a FUSE session. Does not return until the lower filesystem is unmounted. */
@@ -67,7 +78,9 @@
}
Log.i(TAG, "Starting thread for " + getName() + " ...");
- native_start(ptr, mFuseDeviceFd, mPath); // Blocks
+ native_start(ptr, mFuseDeviceFd, mPath, mUncachedMode,
+ mSupportedTranscodingRelativePaths,
+ mSupportedUncachedRelativePaths); // Blocks
Log.i(TAG, "Exiting thread for " + getName() + " ...");
synchronized (mLock) {
@@ -95,7 +108,7 @@
Log.e(TAG, "initializeDeviceId failed, FUSE daemon unavailable");
return;
}
- String path = mMediaProvider.getFuseFile(new File(mPath)).getAbsolutePath();
+ String path = FileUtils.toFuseFile(new File(mPath)).getAbsolutePath();
native_initialize_device_id(mPtr, path);
}
}
@@ -155,6 +168,21 @@
}
/**
+ * Checks if the FuseDaemon uses the FUSE passthrough feature.
+ *
+ * @return {@code true} if the FuseDaemon uses FUSE passthrough, {@code false} otherwise
+ */
+ public boolean usesFusePassthrough() {
+ synchronized (mLock) {
+ if (mPtr == 0) {
+ Log.i(TAG, "usesFusePassthrough failed, FUSE daemon unavailable");
+ return false;
+ }
+ return native_uses_fuse_passthrough(mPtr);
+ }
+ }
+
+ /**
* Invalidates FUSE VFS dentry cache for {@code path}
*/
public void invalidateFuseDentryCache(String path) {
@@ -167,27 +195,30 @@
}
}
- public String getOriginalMediaFormatFilePath(ParcelFileDescriptor fileDescriptor)
+ public FdAccessResult checkFdAccess(ParcelFileDescriptor fileDescriptor, int uid)
throws IOException {
synchronized (mLock) {
if (mPtr == 0) {
throw new IOException("FUSE daemon unavailable");
}
- return native_get_original_media_format_file_path(mPtr, fileDescriptor.getFd());
+ return native_check_fd_access(mPtr, fileDescriptor.getFd(), uid);
}
}
private native long native_new(MediaProvider mediaProvider);
// Takes ownership of the passed in file descriptor!
- private native void native_start(long daemon, int deviceFd, String path);
+ private native void native_start(long daemon, int deviceFd, String path,
+ boolean uncachedMode, String[] supportedTranscodingRelativePaths,
+ String[] supportedUncachedRelativePaths);
private native void native_delete(long daemon);
private native boolean native_should_open_with_fuse(long daemon, String path, boolean readLock,
int fd);
+ private native boolean native_uses_fuse_passthrough(long daemon);
private native void native_invalidate_fuse_dentry_cache(long daemon, String path);
private native boolean native_is_started(long daemon);
- private native String native_get_original_media_format_file_path(long daemon, int fd);
+ private native FdAccessResult native_check_fd_access(long daemon, int fd, int uid);
private native void native_initialize_device_id(long daemon, String path);
public static native boolean native_is_fuse_thread();
}
diff --git a/src/com/android/providers/media/metrics/MPUiEventLoggerImpl.java b/src/com/android/providers/media/metrics/MPUiEventLoggerImpl.java
new file mode 100644
index 0000000..fadb70c
--- /dev/null
+++ b/src/com/android/providers/media/metrics/MPUiEventLoggerImpl.java
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+package com.android.providers.media.metrics;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.logging.InstanceId;
+import com.android.internal.logging.UiEventLogger;
+import com.android.providers.media.MediaProviderStatsLog;
+
+public class MPUiEventLoggerImpl implements UiEventLogger {
+
+ @Override
+ public void log(@NonNull UiEventEnum event) {
+ log(event, 0, null);
+ }
+
+ @Override
+ public void log(@NonNull UiEventEnum event, @Nullable InstanceId instance) {
+ logWithInstanceId(event, 0, null, instance);
+ }
+
+ @Override
+ public void log(@NonNull UiEventEnum event, int uid, @Nullable String packageName) {
+ final int eventID = event.getId();
+ if (eventID > 0) {
+ MediaProviderStatsLog.write(MediaProviderStatsLog.UI_EVENT_REPORTED,
+ /* event_id = 1 */ eventID,
+ /* uid = 2 */ uid,
+ /* package_name = 3 */ packageName,
+ /* instance_id = 4 */ 0);
+ }
+ }
+
+ @Override
+ public void logWithInstanceId(@NonNull UiEventEnum event, int uid, @Nullable String packageName,
+ @Nullable InstanceId instance) {
+ final int eventID = event.getId();
+ if ((eventID > 0) && (instance != null)) {
+ MediaProviderStatsLog.write(MediaProviderStatsLog.UI_EVENT_REPORTED,
+ /* event_id = 1 */ eventID,
+ /* uid = 2 */ uid,
+ /* package_name = 3 */ packageName,
+ /* instance_id = 4 */ instance.getId());
+ } else {
+ log(event, uid, packageName);
+ }
+ }
+
+ @Override
+ public void logWithPosition(@NonNull UiEventEnum event, int uid, @Nullable String packageName,
+ int position) {
+ final int eventID = event.getId();
+ if (eventID > 0) {
+ MediaProviderStatsLog.write(MediaProviderStatsLog.RANKING_SELECTED,
+ /* event_id = 1 */ eventID,
+ /* package_name = 2 */ packageName,
+ /* instance_id = 3 */ 0,
+ /* position_picked = 4 */ position,
+ /* is_pinned = 5 */ false);
+ }
+ }
+
+ @Override
+ public void logWithInstanceIdAndPosition(@NonNull UiEventEnum event, int uid,
+ @Nullable String packageName, @Nullable InstanceId instance, int position) {
+ final int eventID = event.getId();
+ if ((eventID > 0) && (instance != null)) {
+ MediaProviderStatsLog.write(MediaProviderStatsLog.RANKING_SELECTED,
+ /* event_id = 1 */ eventID,
+ /* package_name = 2 */ packageName,
+ /* instance_id = 3 */ instance.getId(),
+ /* position_picked = 4 */ position,
+ /* is_pinned = 5 */ false);
+ } else {
+ logWithPosition(event, uid, packageName, position);
+ }
+ }
+}
diff --git a/src/com/android/providers/media/metrics/PulledMetrics.java b/src/com/android/providers/media/metrics/PulledMetrics.java
index f9c5fac..b1986f2 100644
--- a/src/com/android/providers/media/metrics/PulledMetrics.java
+++ b/src/com/android/providers/media/metrics/PulledMetrics.java
@@ -26,8 +26,8 @@
import androidx.annotation.NonNull;
+import com.android.modules.utils.BackgroundThread;
import com.android.providers.media.fuse.FuseDaemon;
-import com.android.providers.media.util.BackgroundThread;
import java.util.List;
@@ -80,21 +80,27 @@
if (!isInitialized) {
return;
}
-
- storageAccessMetrics.logMimeType(uid, mimeType);
+ BackgroundThread.getExecutor().execute(() -> {
+ storageAccessMetrics.logMimeType(uid, mimeType);
+ });
}
/**
* Logs the storage access and attributes it to the given {@code uid}.
*
- * <p>Should only be called from a FUSE thread.
+ * <p>This is a no-op if it's called from a non-FUSE thread.
*/
public static void logFileAccessViaFuse(int uid, @NonNull String file) {
if (!isInitialized) {
return;
}
-
- storageAccessMetrics.logAccessViaFuse(uid, file);
+ // Log only if it's a FUSE thread
+ if (!FuseDaemon.native_is_fuse_thread()) {
+ return;
+ }
+ BackgroundThread.getExecutor().execute(() -> {
+ storageAccessMetrics.logAccessViaFuse(uid, file);
+ });
}
/**
@@ -111,7 +117,9 @@
if (FuseDaemon.native_is_fuse_thread()) {
return;
}
- storageAccessMetrics.logAccessViaMediaProvider(uid, volumeName);
+ BackgroundThread.getExecutor().execute(() -> {
+ storageAccessMetrics.logAccessViaMediaProvider(uid, volumeName);
+ });
}
private static class StatsPullCallbackHandler implements StatsManager.StatsPullAtomCallback {
diff --git a/src/com/android/providers/media/photopicker/PhotoPickerActivity.java b/src/com/android/providers/media/photopicker/PhotoPickerActivity.java
index 66182bc..4ec540f 100644
--- a/src/com/android/providers/media/photopicker/PhotoPickerActivity.java
+++ b/src/com/android/providers/media/photopicker/PhotoPickerActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -16,116 +16,603 @@
package com.android.providers.media.photopicker;
+import static com.android.providers.media.photopicker.data.PickerResult.getPickerResponseIntent;
+import static com.android.providers.media.photopicker.util.LayoutModeUtils.MODE_PHOTOS_TAB;
+
import android.app.Activity;
-import android.content.ClipData;
-import android.content.ClipDescription;
-import android.content.ContentUris;
+import android.content.BroadcastReceiver;
+import android.content.Context;
import android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
-import android.provider.MediaStore;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.ListView;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+import android.view.WindowInsetsController;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
+import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.ViewModelProvider;
+
+import com.android.internal.logging.InstanceId;
+import com.android.internal.logging.InstanceIdSequence;
import com.android.providers.media.R;
+import com.android.providers.media.photopicker.data.Selection;
+import com.android.providers.media.photopicker.data.UserIdManager;
+import com.android.providers.media.photopicker.data.model.UserId;
+import com.android.providers.media.photopicker.ui.TabContainerFragment;
+import com.android.providers.media.photopicker.util.LayoutModeUtils;
+import com.android.providers.media.photopicker.viewmodel.PickerViewModel;
-import com.google.common.collect.ImmutableList;
+import com.google.android.material.bottomsheet.BottomSheetBehavior;
+import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback;
+import com.google.android.material.tabs.TabLayout;
+import com.google.common.collect.Lists;
+
+import java.util.List;
/**
* Photo Picker allows users to choose one or more photos and/or videos to share with an app. The
* app does not get access to all photos/videos.
*/
-public class PhotoPickerActivity extends Activity {
+public class PhotoPickerActivity extends AppCompatActivity {
+ private static final String TAG = "PhotoPickerActivity";
+ private static final float BOTTOM_SHEET_PEEK_HEIGHT_PERCENTAGE = 0.60f;
+ private static final float HIDE_PROFILE_BUTTON_THRESHOLD = -0.5f;
+ private static final String LOGGER_INSTANCE_ID_ARG = "loggerInstanceIdArg";
- public static final String TAG = "PhotoPickerActivity";
+ private PickerViewModel mPickerViewModel;
+ private Selection mSelection;
+ private BottomSheetBehavior mBottomSheetBehavior;
+ private View mBottomBar;
+ private View mBottomSheetView;
+ private View mFragmentContainerView;
+ private View mDragBar;
+ private View mPrivacyText;
+ private View mProfileButton;
+ private TabLayout mTabLayout;
+ private Toolbar mToolbar;
+ private CrossProfileListeners mCrossProfileListeners;
+
+ @ColorInt
+ private int mDefaultBackgroundColor;
+
+ @ColorInt
+ private int mToolBarIconColor;
+
+ private int mToolbarHeight = 0;
+ private boolean mIsAccessibilityEnabled;
@Override
public void onCreate(Bundle savedInstanceState) {
+ // We use the device default theme as the base theme. Apply the material them for the
+ // material components. We use force "false" here, only values that are not already defined
+ // in the base theme will be copied.
+ getTheme().applyStyle(R.style.PickerMaterialTheme, /* force */ false);
+
super.onCreate(savedInstanceState);
- // TODO(b/168001592) Change layout to show photos & options.
- setContentView(R.layout.photo_picker);
- Button button = findViewById(R.id.button);
- button.setOnClickListener(v -> respondEmpty());
+ setContentView(R.layout.activity_photo_picker);
- // TODO(b/168001592) Handle multiple selection option.
+ mToolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(mToolbar);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- // TODO(b/168001592) Filter using given mime type.
+ final int[] attrs = new int[]{R.attr.actionBarSize, R.attr.pickerTextColor};
+ final TypedArray ta = obtainStyledAttributes(attrs);
+ // Save toolbar height so that we can use it as padding for FragmentContainerView
+ mToolbarHeight = ta.getDimensionPixelSize(/* index */ 0, /* defValue */ -1);
+ mToolBarIconColor = ta.getColor(/* index */ 1,/* defValue */ -1);
+ ta.recycle();
- // TODO(b/168001592) Show a photo grid instead of ListView.
- ListView photosList = findViewById(R.id.names_list);
- ArrayAdapter<PhotoEntry> photosAdapter = new ArrayAdapter<>(
- this, android.R.layout.simple_list_item_1);
- photosList.setAdapter(photosAdapter);
- // Clicking an item in the list returns its URI for now.
- photosList.setOnItemClickListener((parent, view, position, id) -> {
- respondPhoto(photosAdapter.getItem(position));
- });
+ mDefaultBackgroundColor = getColor(R.color.picker_background_color);
+ mPickerViewModel = createViewModel();
+ mSelection = mPickerViewModel.getSelection();
- // Show the list of photo names for now.
- ImmutableList.Builder<PhotoEntry> imageRowsBuilder = ImmutableList.builder();
- String[] projection = new String[] {
- MediaStore.MediaColumns._ID,
- MediaStore.MediaColumns.DISPLAY_NAME
- };
- // TODO(b/168001592) call query() from worker thread.
- Cursor cursor = getApplicationContext().getContentResolver().query(
- MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
- projection, null, null);
- int idColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID);
- int nameColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME);
- // TODO(b/168001592) Use better image loading (e.g. use paging, glide).
- while (cursor.moveToNext()) {
- imageRowsBuilder.add(
- new PhotoEntry(cursor.getLong(idColumn), cursor.getString(nameColumn)));
+ try {
+ mPickerViewModel.parseValuesFromIntent(getIntent());
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Finished activity due to an exception while parsing extras", e);
+ setCancelledResultAndFinishSelf();
}
- photosAdapter.addAll(imageRowsBuilder.build());
+
+ mDragBar = findViewById(R.id.drag_bar);
+ mPrivacyText = findViewById(R.id.privacy_text);
+ mBottomBar = findViewById(R.id.picker_bottom_bar);
+ mProfileButton = findViewById(R.id.profile_button);
+
+ mTabLayout = findViewById(R.id.tab_layout);
+
+ AccessibilityManager accessibilityManager = getSystemService(AccessibilityManager.class);
+ mIsAccessibilityEnabled = accessibilityManager.isEnabled();
+ accessibilityManager.addAccessibilityStateChangeListener(
+ enabled -> mIsAccessibilityEnabled = enabled);
+
+ initBottomSheetBehavior();
+ restoreState(savedInstanceState);
+
+ // Call this after state is restored, to use the correct LOGGER_INSTANCE_ID_ARG
+ mPickerViewModel.logPickerOpened(getCallingPackage());
+
+ // Save the fragment container layout so that we can adjust the padding based on preview or
+ // non-preview mode.
+ mFragmentContainerView = findViewById(R.id.fragment_container);
+
+ mCrossProfileListeners = new CrossProfileListeners();
}
- private void respondPhoto(PhotoEntry photoEntry) {
- Uri contentUri = ContentUris.withAppendedId(
- MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
- photoEntry.id);
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ // This is required to unregister any broadcast receivers.
+ mCrossProfileListeners.onDestroy();
+ }
- Intent response = new Intent();
- // TODO(b/168001592) Confirm if this flag is enough to grant the access we want.
- response.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ /**
+ * Warning: This method is needed for tests, we are not customizing anything here.
+ * Allowing ourselves to control ViewModel creation helps us mock the ViewModel for test.
+ */
+ @VisibleForTesting
+ @NonNull
+ protected PickerViewModel createViewModel() {
+ return new ViewModelProvider(this).get(PickerViewModel.class);
+ }
- // TODO(b/168001592) Use a better label and accurate mime types.
- if (getIntent().getBooleanExtra(Intent.EXTRA_ALLOW_MULTIPLE, false)) {
- ClipDescription clipDescription = new ClipDescription(
- "Photo Picker ClipData",
- new String[]{"image/*", "video/*"});
- ClipData clipData = new ClipData(clipDescription, new ClipData.Item(contentUri));
- response.setClipData(clipData);
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event){
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ if (mBottomSheetBehavior.getState() == BottomSheetBehavior.STATE_COLLAPSED) {
+
+ Rect outRect = new Rect();
+ mBottomSheetView.getGlobalVisibleRect(outRect);
+
+ if (!outRect.contains((int)event.getRawX(), (int)event.getRawY())) {
+ mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
+ }
+ }
+ }
+ return super.dispatchTouchEvent(event);
+ }
+
+ @Override
+ public boolean onSupportNavigateUp() {
+ onBackPressed();
+ return true;
+ }
+
+ @Override
+ public void setTitle(CharSequence title) {
+ super.setTitle(title);
+ getSupportActionBar().setTitle(title);
+ }
+
+ /**
+ * Called when owning activity is saving state to be used to restore state during creation.
+ *
+ * @param state Bundle to save state
+ */
+ @Override
+ public void onSaveInstanceState(Bundle state) {
+ super.onSaveInstanceState(state);
+ saveBottomSheetState();
+ state.putParcelable(LOGGER_INSTANCE_ID_ARG, mPickerViewModel.getInstanceId());
+ }
+
+ private void restoreState(Bundle savedInstanceState) {
+ if (savedInstanceState != null) {
+ restoreBottomSheetState();
+ mPickerViewModel.setInstanceId(
+ savedInstanceState.getParcelable(LOGGER_INSTANCE_ID_ARG));
} else {
- response.setData(contentUri);
+ setupInitialLaunchState();
}
+ }
- setResult(Activity.RESULT_OK, response);
+ /**
+ * Sets up states for the initial launch. This includes updating common layouts, selecting
+ * Photos tab item and saving the current bottom sheet state for later.
+ */
+ private void setupInitialLaunchState() {
+ updateCommonLayouts(MODE_PHOTOS_TAB, /* title */ "");
+ TabContainerFragment.show(getSupportFragmentManager());
+ saveBottomSheetState();
+ }
+
+ private void initBottomSheetBehavior() {
+ mBottomSheetView = findViewById(R.id.bottom_sheet);
+ mBottomSheetBehavior = BottomSheetBehavior.from(mBottomSheetView);
+ initStateForBottomSheet();
+
+ mBottomSheetBehavior.addBottomSheetCallback(createBottomSheetCallBack());
+ setRoundedCornersForBottomSheet();
+ }
+
+ private BottomSheetCallback createBottomSheetCallBack() {
+ return new BottomSheetCallback() {
+ private boolean mIsHiddenDueToBottomSheetClosing = false;
+ @Override
+ public void onStateChanged(@NonNull View bottomSheet, int newState) {
+ if (newState == BottomSheetBehavior.STATE_HIDDEN) {
+ finish();
+ }
+ saveBottomSheetState();
+ }
+
+ @Override
+ public void onSlide(@NonNull View bottomSheet, float slideOffset) {
+ // slideOffset = -1 is when bottomsheet is completely hidden
+ // slideOffset = 0 is when bottomsheet is in collapsed mode
+ // slideOffset = 1 is when bottomsheet is in expanded mode
+ // We hide the Profile button if the bottomsheet is 50% in between collapsed state
+ // and hidden state.
+ if (slideOffset < HIDE_PROFILE_BUTTON_THRESHOLD &&
+ mProfileButton.getVisibility() == View.VISIBLE) {
+ mProfileButton.setVisibility(View.GONE);
+ mIsHiddenDueToBottomSheetClosing = true;
+ return;
+ }
+
+ // We need to handle this state if the user is swiping till the bottom of the
+ // screen but then swipes up bottom sheet suddenly
+ if (slideOffset > HIDE_PROFILE_BUTTON_THRESHOLD &&
+ mIsHiddenDueToBottomSheetClosing) {
+ mProfileButton.setVisibility(View.VISIBLE);
+ mIsHiddenDueToBottomSheetClosing = false;
+ }
+ }
+ };
+ }
+
+ private void setRoundedCornersForBottomSheet() {
+ final float cornerRadius =
+ getResources().getDimensionPixelSize(R.dimen.picker_top_corner_radius);
+ final ViewOutlineProvider viewOutlineProvider = new ViewOutlineProvider() {
+ @Override
+ public void getOutline(final View view, final Outline outline) {
+ outline.setRoundRect(0, 0, view.getWidth(),
+ (int)(view.getHeight() + cornerRadius), cornerRadius);
+ }
+ };
+ mBottomSheetView.setOutlineProvider(viewOutlineProvider);
+ }
+
+ private void initStateForBottomSheet() {
+ if (!mIsAccessibilityEnabled && !mSelection.canSelectMultiple()
+ && !isOrientationLandscape()) {
+ final int peekHeight = getBottomSheetPeekHeight(this);
+ mBottomSheetBehavior.setPeekHeight(peekHeight);
+ mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
+ } else {
+ mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
+ mBottomSheetBehavior.setSkipCollapsed(true);
+ }
+ }
+
+ private static int getBottomSheetPeekHeight(Context context) {
+ final WindowManager windowManager = context.getSystemService(WindowManager.class);
+ final Rect displayBounds = windowManager.getCurrentWindowMetrics().getBounds();
+ return (int) (displayBounds.height() * BOTTOM_SHEET_PEEK_HEIGHT_PERCENTAGE);
+ }
+
+ private void restoreBottomSheetState() {
+ // BottomSheet is always EXPANDED for landscape
+ if (isOrientationLandscape()) {
+ return;
+ }
+ final int savedState = mPickerViewModel.getBottomSheetState();
+ if (isValidBottomSheetState(savedState)) {
+ mBottomSheetBehavior.setState(savedState);
+ }
+ }
+
+ private void saveBottomSheetState() {
+ // Do not save state for landscape or preview mode. This is because they are always in
+ // STATE_EXPANDED state.
+ if (isOrientationLandscape() || !mBottomSheetView.getClipToOutline()) {
+ return;
+ }
+ mPickerViewModel.setBottomSheetState(mBottomSheetBehavior.getState());
+ }
+
+ private boolean isValidBottomSheetState(int state) {
+ return state == BottomSheetBehavior.STATE_COLLAPSED ||
+ state == BottomSheetBehavior.STATE_EXPANDED;
+ }
+
+ private boolean isOrientationLandscape() {
+ return getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
+ }
+
+ public void setResultAndFinishSelf() {
+ setResult(Activity.RESULT_OK, getPickerResponseIntent(mSelection.canSelectMultiple(),
+ mSelection.getSelectedItems()));
finish();
}
-
- private void respondEmpty() {
- setResult(Activity.RESULT_OK);
+ private void setCancelledResultAndFinishSelf() {
+ setResult(Activity.RESULT_CANCELED);
finish();
}
- private static class PhotoEntry {
- private long id;
- private String name;
+ /**
+ * Updates the common views such as Title, Toolbar, Navigation bar, status bar and bottom sheet
+ * behavior
+ *
+ * @param mode {@link LayoutModeUtils.Mode} which describes the layout mode to update.
+ * @param title the title to set for the Activity
+ */
+ public void updateCommonLayouts(LayoutModeUtils.Mode mode, String title) {
+ updateTitle(title);
+ updateToolbar(mode);
+ updateStatusBarAndNavigationBar(mode);
+ updateBottomSheetBehavior(mode);
+ updateFragmentContainerViewPadding(mode);
+ updateDragBarVisibility(mode);
+ updatePrivacyTextVisibility(mode);
+ // The bottom bar and profile button are not shown on preview, hide them in preview. We
+ // handle the visibility of them in TabFragment. We don't need to make them shown in
+ // non-preview page here.
+ if (mode.isPreview) {
+ mBottomBar.setVisibility(View.GONE);
+ mProfileButton.setVisibility(View.GONE);
+ }
+ }
- PhotoEntry(long id, String name) {
- this.id = id;
- this.name = name;
+ private void updateTitle(String title) {
+ setTitle(title);
+ }
+
+ /**
+ * Updates the icons and show/hide the tab layout with {@code mode}.
+ *
+ * @param mode {@link LayoutModeUtils.Mode} which describes the layout mode to update.
+ */
+ private void updateToolbar(@NonNull LayoutModeUtils.Mode mode) {
+ final boolean isPreview = mode.isPreview;
+ final boolean shouldShowTabLayout = mode.isPhotosTabOrAlbumsTab;
+ // 1. Set the tabLayout visibility
+ mTabLayout.setVisibility(shouldShowTabLayout ? View.VISIBLE : View.GONE);
+
+ // 2. Set the toolbar color
+ final ColorDrawable toolbarColor;
+ if (isPreview && !shouldShowTabLayout) {
+ if (isOrientationLandscape()) {
+ // Toolbar in Preview will have transparent color in Landscape mode.
+ toolbarColor = new ColorDrawable(getColor(android.R.color.transparent));
+ } else {
+ // Toolbar in Preview will have a solid color with 90% opacity in Portrait mode.
+ toolbarColor = new ColorDrawable(getColor(R.color.preview_scrim_solid_color));
+ }
+ } else {
+ toolbarColor = new ColorDrawable(mDefaultBackgroundColor);
+ }
+ getSupportActionBar().setBackgroundDrawable(toolbarColor);
+
+ // 3. Set the toolbar icon.
+ final Drawable icon;
+ if (shouldShowTabLayout) {
+ icon = getDrawable(R.drawable.ic_close);
+ } else {
+ icon = getDrawable(R.drawable.ic_arrow_back);
+ // Preview mode has dark background, hence icons will be WHITE in color
+ icon.setTint(isPreview ? Color.WHITE : mToolBarIconColor);
+ }
+ getSupportActionBar().setHomeAsUpIndicator(icon);
+ getSupportActionBar().setHomeActionContentDescription(
+ shouldShowTabLayout ? android.R.string.cancel
+ : R.string.abc_action_bar_up_description);
+ }
+
+ /**
+ * Updates status bar and navigation bar
+ *
+ * @param mode {@link LayoutModeUtils.Mode} which describes the layout mode to update.
+ */
+ private void updateStatusBarAndNavigationBar(@NonNull LayoutModeUtils.Mode mode) {
+ final boolean isPreview = mode.isPreview;
+ final int navigationBarColor = isPreview ? getColor(R.color.preview_background_color) :
+ mDefaultBackgroundColor;
+ getWindow().setNavigationBarColor(navigationBarColor);
+
+ final int statusBarColor = isPreview ? getColor(R.color.preview_background_color) :
+ getColor(android.R.color.transparent);
+ getWindow().setStatusBarColor(statusBarColor);
+
+ // Update the system bar appearance
+ final int mask = WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
+ int appearance = 0;
+ if (!isPreview) {
+ final int uiModeNight =
+ getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+
+ if (uiModeNight == Configuration.UI_MODE_NIGHT_NO) {
+ // If the system is not in Dark theme, set the system bars to light mode.
+ appearance = mask;
+ }
+ }
+ getWindow().getInsetsController().setSystemBarsAppearance(appearance, mask);
+ }
+
+ /**
+ * Updates the bottom sheet behavior
+ *
+ * @param mode {@link LayoutModeUtils.Mode} which describes the layout mode to update.
+ */
+ private void updateBottomSheetBehavior(@NonNull LayoutModeUtils.Mode mode) {
+ final boolean isPreview = mode.isPreview;
+ if (mBottomSheetView != null) {
+ mBottomSheetView.setClipToOutline(!isPreview);
+ // TODO(b/197241815): Add animation downward swipe for preview should go back to
+ // the photo in photos grid
+ mBottomSheetBehavior.setDraggable(!isPreview);
+ }
+ if (isPreview) {
+ if (mBottomSheetBehavior.getState() != BottomSheetBehavior.STATE_EXPANDED) {
+ // Sets bottom sheet behavior state to STATE_EXPANDED if it's not already expanded.
+ // This is useful when user goes to Preview mode which is always Full screen.
+ // TODO(b/197241815): Add animation preview to full screen and back transition to
+ // partial screen. This is similar to long press animation.
+ mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
+ }
+ } else {
+ restoreBottomSheetState();
+ }
+ }
+
+ /**
+ * Updates the FragmentContainerView padding.
+ * <p>
+ * For Preview mode, toolbar overlaps the Fragment content, hence the padding will be set to 0.
+ * For Non-Preview mode, toolbar doesn't overlap the contents of the fragment, hence we set the
+ * padding as the height of the toolbar.
+ */
+ private void updateFragmentContainerViewPadding(@NonNull LayoutModeUtils.Mode mode) {
+ if (mFragmentContainerView == null) return;
+
+ final int topPadding;
+ if (mode.isPreview) {
+ topPadding = 0;
+ } else {
+ topPadding = mToolbarHeight;
}
- @Override
- public String toString() {
- return name;
+ mFragmentContainerView.setPadding(mFragmentContainerView.getPaddingLeft(),
+ topPadding, mFragmentContainerView.getPaddingRight(),
+ mFragmentContainerView.getPaddingBottom());
+ }
+
+ private void updateDragBarVisibility(@NonNull LayoutModeUtils.Mode mode) {
+ final boolean shouldShowDragBar = !mode.isPreview;
+ mDragBar.setVisibility(shouldShowDragBar ? View.VISIBLE : View.GONE);
+ }
+
+ private void updatePrivacyTextVisibility(@NonNull LayoutModeUtils.Mode mode) {
+ // The privacy text is only shown on the Photos tab and Albums tab
+ final boolean shouldShowPrivacyMessage = mode.isPhotosTabOrAlbumsTab;
+ mPrivacyText.setVisibility(shouldShowPrivacyMessage ? View.VISIBLE : View.GONE);
+ }
+
+ private class CrossProfileListeners {
+
+ private final List<String> MANAGED_PROFILE_FILTER_ACTIONS = Lists.newArrayList(
+ Intent.ACTION_MANAGED_PROFILE_ADDED, // add profile button switch
+ Intent.ACTION_MANAGED_PROFILE_REMOVED, // remove profile button switch
+ Intent.ACTION_MANAGED_PROFILE_UNLOCKED, // activate profile button switch
+ Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE // disable profile button switch
+ );
+
+ private final UserIdManager mUserIdManager;
+
+ public CrossProfileListeners() {
+ mUserIdManager = mPickerViewModel.getUserIdManager();
+
+ registerBroadcastReceivers();
+ }
+
+ public void onDestroy() {
+ unregisterReceiver(mReceiver);
+ }
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+
+ final UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER);
+ final UserId userId = UserId.of(userHandle);
+
+ // We only need to refresh the layout when the received profile user is the
+ // managed user corresponding to the current profile or a new work profile is added
+ // for the current user.
+ if (!userId.equals(mUserIdManager.getManagedUserId()) &&
+ !action.equals(Intent.ACTION_MANAGED_PROFILE_ADDED)) {
+ return;
+ }
+
+ switch (action) {
+ case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
+ handleWorkProfileOff();
+ break;
+ case Intent.ACTION_MANAGED_PROFILE_REMOVED:
+ handleWorkProfileRemoved();
+ break;
+ case Intent.ACTION_MANAGED_PROFILE_UNLOCKED:
+ handleWorkProfileOn();
+ break;
+ case Intent.ACTION_MANAGED_PROFILE_ADDED:
+ handleWorkProfileAdded();
+ break;
+ default:
+ // do nothing
+ }
+ }
+ };
+
+ private void registerBroadcastReceivers() {
+ final IntentFilter managedProfileFilter = new IntentFilter();
+ for (String managedProfileAction : MANAGED_PROFILE_FILTER_ACTIONS) {
+ managedProfileFilter.addAction(managedProfileAction);
+ }
+ registerReceiver(mReceiver, managedProfileFilter);
+ }
+
+ private void handleWorkProfileOff() {
+ if (mUserIdManager.isManagedUserSelected()) {
+ switchToPersonalProfileInitialLaunchState();
+ }
+ mUserIdManager.updateWorkProfileOffValue();
+ }
+
+ private void handleWorkProfileRemoved() {
+ if (mUserIdManager.isManagedUserSelected()) {
+ switchToPersonalProfileInitialLaunchState();
+ }
+ mUserIdManager.resetUserIds();
+ }
+
+ private void handleWorkProfileAdded() {
+ mUserIdManager.resetUserIds();
+ }
+
+ private void handleWorkProfileOn() {
+ // Update UI for switch to profile button
+ // When the managed profile becomes available, the provider may not be available
+ // immediately, we need to check if it is ready before we reload the content.
+ mUserIdManager.waitForMediaProviderToBeAvailable();
+ }
+
+ private void switchToPersonalProfileInitialLaunchState() {
+ final FragmentManager fragmentManager = getSupportFragmentManager();
+ // Clear all back stacks in FragmentManager
+ fragmentManager.popBackStackImmediate(/* name */ null,
+ FragmentManager.POP_BACK_STACK_INCLUSIVE);
+
+ // We reset the state of the PhotoPicker as we do not want to make any
+ // assumptions on the state of the PhotoPicker when it was in Work Profile mode.
+ resetToPersonalProfile();
+ }
+
+ /**
+ * Reset to Photo Picker initial launch state (Photos grid tab) in personal profile mode.
+ */
+ private void resetToPersonalProfile() {
+ mPickerViewModel.resetToPersonalProfile();
+ setupInitialLaunchState();
}
}
}
diff --git a/src/com/android/providers/media/photopicker/PhotoPickerProvider.java b/src/com/android/providers/media/photopicker/PhotoPickerProvider.java
new file mode 100644
index 0000000..34d6040
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/PhotoPickerProvider.java
@@ -0,0 +1,163 @@
+/*
+ * 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;
+
+import static android.provider.CloudMediaProviderContract.EXTRA_AUTHORITY;
+import static android.provider.CloudMediaProviderContract.EXTRA_LOOPING_PLAYBACK_ENABLED;
+import static android.provider.CloudMediaProviderContract.EXTRA_MEDIASTORE_THUMB;
+import static android.provider.CloudMediaProviderContract.EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED;
+
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.graphics.Point;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.OperationCanceledException;
+import android.os.ParcelFileDescriptor;
+import android.provider.CloudMediaProvider;
+import android.provider.MediaStore;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.providers.media.LocalCallingIdentity;
+import com.android.providers.media.MediaProvider;
+import com.android.providers.media.photopicker.data.CloudProviderQueryExtras;
+import com.android.providers.media.photopicker.data.ExternalDbFacade;
+import com.android.providers.media.photopicker.ui.remotepreview.RemotePreviewHandler;
+import com.android.providers.media.photopicker.ui.remotepreview.RemoteSurfaceController;
+
+import java.io.FileNotFoundException;
+
+/**
+ * Implements the {@link CloudMediaProvider} interface over the local items in the MediaProvider
+ * database.
+ */
+public class PhotoPickerProvider extends CloudMediaProvider {
+ private static final String TAG = "PhotoPickerProvider";
+
+ private MediaProvider mMediaProvider;
+ private ExternalDbFacade mDbFacade;
+
+ @Override
+ public boolean onCreate() {
+ mMediaProvider = getMediaProvider();
+ mDbFacade = mMediaProvider.getExternalDbFacade();
+ return true;
+ }
+
+ @Override
+ public Cursor onQueryMedia(@Nullable Bundle extras) {
+ // TODO(b/190713331): Handle extra_page
+ final CloudProviderQueryExtras queryExtras =
+ CloudProviderQueryExtras.fromCloudMediaBundle(extras);
+
+ return mDbFacade.queryMedia(queryExtras.getGeneration(), queryExtras.getAlbumId(),
+ queryExtras.getMimeType());
+ }
+
+ @Override
+ public Cursor onQueryDeletedMedia(@Nullable Bundle extras) {
+ final CloudProviderQueryExtras queryExtras =
+ CloudProviderQueryExtras.fromCloudMediaBundle(extras);
+
+ return mDbFacade.queryDeletedMedia(queryExtras.getGeneration());
+ }
+
+ @Override
+ public Cursor onQueryAlbums(@Nullable Bundle extras) {
+ final CloudProviderQueryExtras queryExtras =
+ CloudProviderQueryExtras.fromCloudMediaBundle(extras);
+
+ return mDbFacade.queryAlbums(queryExtras.getMimeType());
+ }
+
+ @Override
+ public AssetFileDescriptor onOpenPreview(@NonNull String mediaId, @NonNull Point size,
+ @NonNull Bundle extras, @NonNull CancellationSignal signal)
+ throws FileNotFoundException {
+ final Bundle opts = new Bundle();
+ opts.putParcelable(ContentResolver.EXTRA_SIZE, size);
+
+ String mimeTypeFilter = null;
+ if (extras.getBoolean(EXTRA_MEDIASTORE_THUMB)) {
+ // This is a request for thumbnail, set "image/*" to get cached thumbnails from
+ // MediaProvider.
+ mimeTypeFilter = "image/*";
+ }
+
+ final LocalCallingIdentity token = mMediaProvider.clearLocalCallingIdentity();
+ try {
+ return mMediaProvider.openTypedAssetFile(fromMediaId(mediaId), mimeTypeFilter, opts);
+ } finally {
+ mMediaProvider.restoreLocalCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public ParcelFileDescriptor onOpenMedia(@NonNull String mediaId,
+ @NonNull Bundle extras, @NonNull CancellationSignal signal)
+ throws FileNotFoundException {
+ final LocalCallingIdentity token = mMediaProvider.clearLocalCallingIdentity();
+ try {
+ return mMediaProvider.openFile(fromMediaId(mediaId), "r");
+ } finally {
+ mMediaProvider.restoreLocalCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public Bundle onGetMediaCollectionInfo(@Nullable Bundle extras) {
+ final CloudProviderQueryExtras queryExtras =
+ CloudProviderQueryExtras.fromCloudMediaBundle(extras);
+
+ return mDbFacade.getMediaCollectionInfo(queryExtras.getGeneration());
+ }
+
+ @Override
+ @Nullable
+ public CloudMediaSurfaceController onCreateCloudMediaSurfaceController(@NonNull Bundle config,
+ CloudMediaSurfaceStateChangedCallback callback) {
+ if (RemotePreviewHandler.isRemotePreviewEnabled()) {
+ final String authority = config.getString(EXTRA_AUTHORITY,
+ PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY);
+ final boolean enableLoop = config.getBoolean(EXTRA_LOOPING_PLAYBACK_ENABLED, false);
+ final boolean muteAudio = config.getBoolean(EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED,
+ false);
+ return new RemoteSurfaceController(getContext(), authority, enableLoop, muteAudio,
+ callback);
+ }
+ return null;
+ }
+
+ private MediaProvider getMediaProvider() {
+ ContentResolver cr = getContext().getContentResolver();
+ try (ContentProviderClient cpc = cr.acquireContentProviderClient(MediaStore.AUTHORITY)) {
+ return (MediaProvider) cpc.getLocalContentProvider();
+ } catch (OperationCanceledException e) {
+ throw new IllegalStateException("Failed to acquire MediaProvider", e);
+ }
+ }
+
+ private static Uri fromMediaId(String mediaId) {
+ return MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL,
+ Long.parseLong(mediaId));
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/PickerDataLayer.java b/src/com/android/providers/media/photopicker/PickerDataLayer.java
new file mode 100644
index 0000000..a62ba39
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/PickerDataLayer.java
@@ -0,0 +1,192 @@
+/*
+ * 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;
+
+import static android.provider.CloudMediaProviderContract.METHOD_GET_MEDIA_COLLECTION_INFO;
+
+import static com.android.providers.media.PickerUriResolver.getAlbumUri;
+import static com.android.providers.media.PickerUriResolver.getMediaCollectionInfoUri;
+
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.MergeCursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.CloudMediaProviderContract;
+import android.provider.MediaStore;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.providers.media.photopicker.data.CloudProviderQueryExtras;
+import com.android.providers.media.photopicker.data.PickerDbFacade;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Fetches data for the picker UI from the db and cloud/local providers
+ */
+public class PickerDataLayer {
+ private static final String TAG = "PickerDataLayer";
+
+ private final Context mContext;
+ private final PickerDbFacade mDbFacade;
+ private final PickerSyncController mSyncController;
+ private final String mLocalProvider;
+
+ public PickerDataLayer(Context context, PickerDbFacade dbFacade,
+ PickerSyncController syncController) {
+ mContext = context;
+ mDbFacade = dbFacade;
+ mSyncController = syncController;
+ mLocalProvider = dbFacade.getLocalProvider();
+ }
+
+ public Cursor fetchMedia(Bundle queryArgs) {
+ final CloudProviderQueryExtras queryExtras
+ = CloudProviderQueryExtras.fromMediaStoreBundle(queryArgs, mLocalProvider);
+ final String albumId = queryExtras.getAlbumId();
+ final String authority = queryExtras.getAlbumAuthority();
+ // Use media table for all media except albums. Merged categories like,
+ // favorites and video are tagged in the media table and are not a part of
+ // album_media.
+ if (TextUtils.isEmpty(albumId) || isMergedAlbum(queryExtras)) {
+ // Refresh the 'media' table
+ mSyncController.syncAllMedia();
+
+ if (TextUtils.isEmpty(albumId)) {
+ // Notify that the picker is launched in case there's any pending UI notification
+ mSyncController.notifyPickerLaunch();
+ }
+
+ // Fetch all merged and deduped cloud and local media from 'media' table
+ // This also matches 'merged' albums like Favorites because |authority| will
+ // be null, hence we have to fetch the data from the picker db
+ return mDbFacade.queryMediaForUi(queryExtras.toQueryFilter());
+ } else {
+ // The album type here can only be local or cloud because merged categories like,
+ // Favorites and Videos would hit the first condition.
+ // Refresh the 'album_media' table
+ mSyncController.syncAlbumMedia(albumId, isLocal(authority));
+
+ // Fetch album specific media for local or cloud from 'album_media' table
+ return mDbFacade.queryAlbumMediaForUi(queryExtras.toQueryFilter(), authority);
+ }
+ }
+
+ /**
+ * Checks if the query is for a merged album type.
+ * Some albums are not cloud only, they are merged from files on devices and the cloudprovider.
+ */
+ private boolean isMergedAlbum(CloudProviderQueryExtras queryExtras) {
+ final boolean isFavorite = queryExtras.isFavorite();
+ final boolean isVideo = queryExtras.isVideo();
+ return isFavorite || isVideo;
+ }
+
+ public Cursor fetchAlbums(Bundle queryArgs) {
+ // Refresh the 'media' table so that 'merged' albums (Favorites and Videos) are up to date
+ mSyncController.syncAllMedia();
+
+ final String cloudProvider = mDbFacade.getCloudProvider();
+ final CloudProviderQueryExtras queryExtras
+ = CloudProviderQueryExtras.fromMediaStoreBundle(queryArgs, mLocalProvider);
+ final Bundle cloudMediaArgs = queryExtras.toCloudMediaBundle();
+ final List<Cursor> cursors = new ArrayList<>();
+ final Bundle cursorExtra = new Bundle();
+ cursorExtra.putString(MediaStore.EXTRA_CLOUD_PROVIDER, cloudProvider);
+
+ // Favorites and Videos are merged albums.
+ final Cursor mergedAlbums = mDbFacade.getMergedAlbums(queryExtras.toQueryFilter());
+ if (mergedAlbums != null) {
+ cursors.add(mergedAlbums);
+ }
+
+ final Cursor localAlbums = queryProviderAlbums(mLocalProvider, cloudMediaArgs);
+ if (localAlbums != null) {
+ cursors.add(localAlbums);
+ }
+
+ final Cursor cloudAlbums = queryProviderAlbums(cloudProvider, cloudMediaArgs);
+ if (cloudAlbums != null) {
+ cursors.add(cloudAlbums);
+ }
+
+ if (cursors.isEmpty()) {
+ return null;
+ }
+
+ MergeCursor mergeCursor = new MergeCursor(cursors.toArray(new Cursor[cursors.size()]));
+ mergeCursor.setExtras(cursorExtra);
+ return mergeCursor;
+ }
+
+ public AccountInfo fetchCloudAccountInfo() {
+ final String cloudProvider = mDbFacade.getCloudProvider();
+ if (cloudProvider == null) {
+ return null;
+ }
+
+ try {
+ final Bundle accountBundle = mContext.getContentResolver().call(
+ getMediaCollectionInfoUri(cloudProvider), METHOD_GET_MEDIA_COLLECTION_INFO,
+ /* arg */ null, /* extras */ null);
+ final String accountName = accountBundle.getString(
+ CloudMediaProviderContract.MediaCollectionInfo.ACCOUNT_NAME);
+ final Intent configIntent = (Intent) accountBundle.getParcelable(
+ CloudMediaProviderContract.MediaCollectionInfo.ACCOUNT_CONFIGURATION_INTENT);
+
+ if (accountName == null) {
+ return null;
+ }
+
+ return new AccountInfo(accountName, configIntent);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to fetch account info from cloud provider: " + cloudProvider, e);
+ return null;
+ }
+ }
+
+ private Cursor queryProviderAlbums(String authority, Bundle queryArgs) {
+ if (authority == null) {
+ // Can happen if there is no cloud provider
+ return null;
+ }
+
+ return query(getAlbumUri(authority), queryArgs);
+ }
+
+ private Cursor query(Uri uri, Bundle extras) {
+ return mContext.getContentResolver().query(uri, /* projection */ null, extras,
+ /* cancellationSignal */ null);
+ }
+
+ private boolean isLocal(String authority) {
+ return mLocalProvider.equals(authority);
+ }
+
+ public static class AccountInfo {
+ public final String accountName;
+ public final Intent accountConfigurationIntent;
+
+ public AccountInfo(String accountName, Intent accountConfigurationIntent) {
+ this.accountName = accountName;
+ this.accountConfigurationIntent = accountConfigurationIntent;
+ }
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/PickerSyncController.java b/src/com/android/providers/media/photopicker/PickerSyncController.java
new file mode 100644
index 0000000..d0fb856
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/PickerSyncController.java
@@ -0,0 +1,909 @@
+/*
+ * 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;
+
+import static android.content.ContentResolver.EXTRA_HONORED_ARGS;
+import static android.provider.CloudMediaProviderContract.EXTRA_ALBUM_ID;
+import static android.provider.CloudMediaProviderContract.EXTRA_MEDIA_COLLECTION_ID;
+import static android.provider.CloudMediaProviderContract.EXTRA_PAGE_TOKEN;
+import static android.provider.CloudMediaProviderContract.EXTRA_SYNC_GENERATION;
+import static android.provider.CloudMediaProviderContract.MediaCollectionInfo;
+
+import static com.android.providers.media.PickerUriResolver.getDeletedMediaUri;
+import static com.android.providers.media.PickerUriResolver.getMediaCollectionInfoUri;
+import static com.android.providers.media.PickerUriResolver.getMediaUri;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.storage.StorageManager;
+import android.provider.CloudMediaProvider;
+import android.provider.CloudMediaProviderContract;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Log;
+import android.widget.Toast;
+
+import androidx.annotation.GuardedBy;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.modules.utils.BackgroundThread;
+import com.android.modules.utils.build.SdkLevel;
+import com.android.providers.media.R;
+import com.android.providers.media.photopicker.data.PickerDbFacade;
+import com.android.providers.media.util.ForegroundThread;
+import com.android.providers.media.util.StringUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Syncs the local and currently enabled cloud {@link CloudMediaProvider} instances on the device
+ * into the picker db.
+ */
+public class PickerSyncController {
+ private static final String TAG = "PickerSyncController";
+
+ public static final String SYNC_DELAY_MS = "default_sync_delay_ms";
+ public static final String ALLOWED_CLOUD_PROVIDERS_KEY = "allowed_cloud_providers";
+
+ private static final String PREFS_KEY_CLOUD_PROVIDER_AUTHORITY = "cloud_provider_authority";
+ private static final String PREFS_KEY_CLOUD_PROVIDER_PENDING_NOTIFICATTION =
+ "cloud_provider_pending_notification";
+ private static final String PREFS_KEY_CLOUD_PREFIX = "cloud_provider:";
+ private static final String PREFS_KEY_LOCAL_PREFIX = "local_provider:";
+
+ private static final String PICKER_USER_PREFS_FILE_NAME = "picker_user_prefs";
+ public static final String PICKER_SYNC_PREFS_FILE_NAME = "picker_sync_prefs";
+ public static final String LOCAL_PICKER_PROVIDER_AUTHORITY =
+ "com.android.providers.media.photopicker";
+
+ private static final int SYNC_TYPE_NONE = 0;
+ private static final int SYNC_TYPE_MEDIA_INCREMENTAL = 1;
+ private static final int SYNC_TYPE_MEDIA_FULL = 2;
+ private static final int SYNC_TYPE_MEDIA_RESET = 3;
+
+ @IntDef(flag = false, prefix = { "SYNC_TYPE_" }, value = {
+ SYNC_TYPE_NONE,
+ SYNC_TYPE_MEDIA_INCREMENTAL,
+ SYNC_TYPE_MEDIA_FULL,
+ SYNC_TYPE_MEDIA_RESET,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface SyncType {}
+
+ private final Object mLock = new Object();
+ private final PickerDbFacade mDbFacade;
+ private final Context mContext;
+ private final SharedPreferences mSyncPrefs;
+ private final SharedPreferences mUserPrefs;
+ private final String mLocalProvider;
+ private final long mSyncDelayMs;
+ private final Runnable mSyncAllMediaCallback;
+ private final Set<String> mAllowedCloudProviders;
+
+ @GuardedBy("mLock")
+ private CloudProviderInfo mCloudProviderInfo;
+
+ public PickerSyncController(Context context, PickerDbFacade dbFacade,
+ String localProvider, String allowedCloudProviders, long syncDelayMs) {
+ mContext = context;
+ mSyncPrefs = mContext.getSharedPreferences(PICKER_SYNC_PREFS_FILE_NAME,
+ Context.MODE_PRIVATE);
+ mUserPrefs = mContext.getSharedPreferences(PICKER_USER_PREFS_FILE_NAME,
+ Context.MODE_PRIVATE);
+ mDbFacade = dbFacade;
+ mLocalProvider = localProvider;
+ mSyncDelayMs = syncDelayMs;
+ mSyncAllMediaCallback = this::syncAllMedia;
+
+ final String cachedAuthority = mUserPrefs.getString(
+ PREFS_KEY_CLOUD_PROVIDER_AUTHORITY, null);
+
+ mAllowedCloudProviders = parseAllowedCloudProviders(allowedCloudProviders);
+
+ final CloudProviderInfo defaultInfo = getDefaultCloudProviderInfo(cachedAuthority);
+
+ if (Objects.equals(defaultInfo.authority, cachedAuthority)) {
+ // Just set it without persisting since it's not changing and persisting would
+ // notify the user that cloud media is now available
+ mCloudProviderInfo = defaultInfo;
+ } else {
+ // Persist it so that we notify the user that cloud media is now available
+ persistCloudProviderInfo(defaultInfo);
+ }
+
+ Log.d(TAG, "Initialized cloud provider to: " + mCloudProviderInfo.authority);
+ }
+
+ /**
+ * Syncs the local and currently enabled cloud {@link CloudMediaProvider} instances
+ */
+ public void syncAllMedia() {
+ syncAllMediaFromProvider(mLocalProvider, /* retryOnFailure */ true);
+
+ synchronized (mLock) {
+ final String cloudProvider = mCloudProviderInfo.authority;
+
+ syncAllMediaFromProvider(cloudProvider, /* retryOnFailure */ true);
+
+ // Reset the album_media table every time we sync all media
+ resetAlbumMedia();
+
+ // Set the latest cloud provider on the facade
+ mDbFacade.setCloudProvider(cloudProvider);
+ }
+ }
+
+ /**
+ * Syncs album media from the local and currently enabled cloud {@link CloudMediaProvider}
+ * instances
+ */
+ public void syncAlbumMedia(String albumId, boolean isLocal) {
+ if (isLocal) {
+ syncAlbumMediaFromProvider(mLocalProvider, albumId);
+ } else {
+ synchronized (mLock) {
+ syncAlbumMediaFromProvider(mCloudProviderInfo.authority, albumId);
+ }
+ }
+ }
+
+ private void resetAlbumMedia() {
+ executeSyncAlbumReset(mLocalProvider, /* albumId */ null);
+
+ synchronized (mLock) {
+ final String cloudProvider = mCloudProviderInfo.authority;
+ executeSyncAlbumReset(cloudProvider, /* albumId */ null);
+ }
+ }
+
+ private void resetAllMedia(String authority) {
+ executeSyncReset(authority);
+ resetCachedMediaCollectionInfo(authority);
+ }
+
+ /**
+ * Returns the supported cloud {@link CloudMediaProvider} infos.
+ */
+ public CloudProviderInfo getCloudProviderInfo(String authority) {
+ for (CloudProviderInfo info : getSupportedCloudProviders(/* ignoreAllowList */ false)) {
+ if (info.authority.equals(authority)) {
+ return info;
+ }
+ }
+
+ return CloudProviderInfo.EMPTY;
+ }
+
+ /**
+ * Returns the supported cloud {@link CloudMediaProvider} authorities.
+ */
+ @VisibleForTesting
+ List<CloudProviderInfo> getSupportedCloudProviders() {
+ return getSupportedCloudProviders(/* ignoreAllowList */ false);
+ }
+
+ private List<CloudProviderInfo> getSupportedCloudProviders(boolean ignoreAllowList) {
+ final List<CloudProviderInfo> result = new ArrayList<>();
+
+ final PackageManager pm = mContext.getPackageManager();
+ final Intent intent = new Intent(CloudMediaProviderContract.PROVIDER_INTERFACE);
+ final List<ResolveInfo> providers = pm.queryIntentContentProviders(intent, /* flags */ 0);
+
+ for (ResolveInfo info : providers) {
+ ProviderInfo providerInfo = info.providerInfo;
+ if (providerInfo.authority != null
+ && CloudMediaProviderContract.MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION.equals(
+ providerInfo.readPermission)
+ && (ignoreAllowList
+ || mAllowedCloudProviders.contains(providerInfo.authority))) {
+ result.add(new CloudProviderInfo(providerInfo.authority,
+ providerInfo.applicationInfo.packageName,
+ providerInfo.applicationInfo.uid));
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Enables a provider with {@code authority} as the default cloud {@link CloudMediaProvider}.
+ * If {@code authority} is set to {@code null}, it simply clears the cloud provider.
+ *
+ * Note, that this doesn't sync the new provider after switching, however, no cloud items will
+ * be available from the picker db until the next sync. Callers should schedule a sync in the
+ * background after switching providers.
+ *
+ * @return {@code true} if the provider was successfully enabled or cleared, {@code false}
+ * otherwise
+ */
+ public boolean setCloudProvider(String authority) {
+ synchronized (mLock) {
+ if (Objects.equals(mCloudProviderInfo.authority, authority)) {
+ Log.w(TAG, "Cloud provider already set: " + authority);
+ return true;
+ }
+ }
+
+ final CloudProviderInfo newProviderInfo = getCloudProviderInfo(authority);
+ if (authority == null || !newProviderInfo.isEmpty()) {
+ synchronized (mLock) {
+ final String oldAuthority = mCloudProviderInfo.authority;
+ persistCloudProviderInfo(newProviderInfo);
+ resetCachedMediaCollectionInfo(newProviderInfo.authority);
+
+ // Disable cloud provider queries on the db until next sync
+ // This will temporarily *clear* the cloud provider on the db facade and prevent
+ // any queries from seeing cloud media until a sync where the cloud provider will be
+ // reset on the facade
+ mDbFacade.setCloudProvider(null);
+
+ Log.i(TAG, "Cloud provider changed successfully. Old: "
+ + oldAuthority + ". New: " + newProviderInfo.authority);
+ }
+
+ return true;
+ }
+
+ Log.w(TAG, "Cloud provider not supported: " + authority);
+ return false;
+ }
+
+ /**
+ * Set cloud provider and update allowed cloud providers
+ */
+ @VisibleForTesting
+ public void forceSetCloudProvider(String authority) {
+ if (authority == null) {
+ mAllowedCloudProviders.clear();
+ } else {
+ mAllowedCloudProviders.add(authority);
+ }
+
+ setCloudProvider(authority);
+ }
+
+ public String getCloudProvider() {
+ synchronized (mLock) {
+ return mCloudProviderInfo.authority;
+ }
+ }
+
+ public String getLocalProvider() {
+ return mLocalProvider;
+ }
+
+ public boolean isProviderEnabled(String authority) {
+ if (mLocalProvider.equals(authority)) {
+ return true;
+ }
+
+ synchronized (mLock) {
+ if (!mCloudProviderInfo.isEmpty() && mCloudProviderInfo.authority.equals(authority)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public boolean isProviderEnabled(String authority, int uid) {
+ if (uid == Process.myUid() && mLocalProvider.equals(authority)) {
+ return true;
+ }
+
+ synchronized (mLock) {
+ if (!mCloudProviderInfo.isEmpty() && uid == mCloudProviderInfo.uid
+ && mCloudProviderInfo.authority.equals(authority)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public boolean isProviderSupported(String authority, int uid) {
+ if (uid == Process.myUid() && mLocalProvider.equals(authority)) {
+ return true;
+ }
+
+ // TODO(b/232738117): Enforce allow list here. This works around some CTS failure late in
+ // Android T. The current implementation is fine since cloud providers is only supported
+ // for app developers testing.
+ final List<CloudProviderInfo> infos = getSupportedCloudProviders(
+ /* ignoreAllowList */ true);
+ for (CloudProviderInfo info : infos) {
+ if (info.uid == uid && info.authority.equals(authority)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Notifies about media events like inserts/updates/deletes from cloud and local providers and
+ * syncs the changes in the background.
+ *
+ * There is a delay before executing the background sync to artificially throttle the burst
+ * notifications.
+ */
+ public void notifyMediaEvent() {
+ BackgroundThread.getHandler().removeCallbacks(mSyncAllMediaCallback);
+ BackgroundThread.getHandler().postDelayed(mSyncAllMediaCallback, mSyncDelayMs);
+ }
+
+ /**
+ * Notifies about package removal
+ */
+ public void notifyPackageRemoval(String packageName) {
+ synchronized (mLock) {
+ if (mCloudProviderInfo.matches(packageName)) {
+ Log.i(TAG, "Package " + packageName
+ + " is the current cloud provider and got removed");
+ setCloudProvider(null);
+ }
+ }
+ }
+
+ /**
+ * Notifies about picker UI launched
+ */
+ public void notifyPickerLaunch() {
+ final String packageName;
+ synchronized (mLock) {
+ packageName = mCloudProviderInfo.packageName;
+ }
+
+ final boolean hasPendingNotification = mUserPrefs.getBoolean(
+ PREFS_KEY_CLOUD_PROVIDER_PENDING_NOTIFICATTION, false);
+
+ if (!hasPendingNotification || (packageName == null)) {
+ Log.d(TAG, "No pending UI notification");
+ return;
+ }
+
+ // Offload showing the UI on a fg thread to avoid the expensive binder request
+ // to fetch the app name blocking the picker launch
+ ForegroundThread.getHandler().post(() -> {
+ Log.i(TAG, "Cloud media now available in the picker");
+
+ final PackageManager pm = mContext.getPackageManager();
+ String appName = packageName;
+ try {
+ ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0);
+ appName = (String) pm.getApplicationLabel(appInfo);
+ } catch (final NameNotFoundException e) {
+ Log.i(TAG, "Failed to get appName for package: " + packageName);
+ }
+
+ final String message = mContext.getResources().getString(R.string.picker_cloud_sync,
+ appName);
+ Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();
+ });
+
+ // Clear the notification
+ final SharedPreferences.Editor editor = mUserPrefs.edit();
+ editor.putBoolean(PREFS_KEY_CLOUD_PROVIDER_PENDING_NOTIFICATTION, false);
+ editor.apply();
+ }
+
+ private void syncAlbumMediaFromProvider(String authority, String albumId) {
+ final Bundle queryArgs = new Bundle();
+ queryArgs.putString(EXTRA_ALBUM_ID, albumId);
+
+ try {
+ executeSyncAlbumReset(authority, albumId);
+
+ if (authority != null) {
+ executeSyncAddAlbum(authority, albumId, queryArgs);
+ }
+ } catch (RuntimeException e) {
+ // Unlike syncAllMediaFromProvider, we don't retry here because any errors would have
+ // occurred in fetching all the album_media since incremental sync is not supported.
+ // A full sync is therefore unlikely to resolve any issue
+ Log.e(TAG, "Failed to sync album media", e);
+ }
+ }
+
+ private void syncAllMediaFromProvider(String authority, boolean retryOnFailure) {
+ try {
+ final SyncRequestParams params = getSyncRequestParams(authority);
+
+ switch (params.syncType) {
+ case SYNC_TYPE_MEDIA_RESET:
+ // Can only happen when |authority| has been set to null and we need to clean up
+ resetAllMedia(authority);
+ break;
+ case SYNC_TYPE_MEDIA_FULL:
+ resetAllMedia(authority);
+
+ // Pass a mutable empty bundle intentionally because it might be populated with
+ // the next page token as part of a query to a cloud provider supporting
+ // pagination
+ executeSyncAdd(authority, params.getMediaCollectionId(),
+ /* isIncrementalSync */ false, /* queryArgs */ new Bundle());
+
+ // Commit sync position
+ cacheMediaCollectionInfo(authority, params.latestMediaCollectionInfo);
+ break;
+ case SYNC_TYPE_MEDIA_INCREMENTAL:
+ final Bundle queryArgs = new Bundle();
+ queryArgs.putLong(EXTRA_SYNC_GENERATION, params.syncGeneration);
+
+ executeSyncAdd(authority, params.getMediaCollectionId(),
+ /* isIncrementalSync */ true, queryArgs);
+ executeSyncRemove(authority, params.getMediaCollectionId(), queryArgs);
+
+ // Commit sync position
+ cacheMediaCollectionInfo(authority, params.latestMediaCollectionInfo);
+ break;
+ case SYNC_TYPE_NONE:
+ break;
+ default:
+ throw new IllegalArgumentException("Unexpected sync type: " + params.syncType);
+ }
+ } catch (RuntimeException e) {
+ // Reset all media for the cloud provider in case it never succeeds
+ resetAllMedia(authority);
+
+ // Attempt a full sync. If this fails, the db table would have been reset,
+ // flushing all old content and leaving the picker UI empty.
+ Log.e(TAG, "Failed to sync all media. Reset media and retry: " + retryOnFailure, e);
+ if (retryOnFailure) {
+ syncAllMediaFromProvider(authority, /* retryOnFailure */ false);
+ }
+ }
+ }
+
+ private void executeSyncReset(String authority) {
+ Log.i(TAG, "Executing SyncReset. authority: " + authority);
+
+ try (PickerDbFacade.DbWriteOperation operation =
+ mDbFacade.beginResetMediaOperation(authority)) {
+ final int writeCount = operation.execute(null /* cursor */);
+ operation.setSuccess();
+
+ Log.i(TAG, "SyncReset. Authority: " + authority + ". Result count: " + writeCount);
+ }
+ }
+
+ private void executeSyncAlbumReset(String authority, String albumId) {
+ Log.i(TAG, "Executing SyncAlbumReset. authority: " + authority + ". albumId: "
+ + albumId);
+
+ try (PickerDbFacade.DbWriteOperation operation =
+ mDbFacade.beginResetAlbumMediaOperation(authority, albumId)) {
+ final int writeCount = operation.execute(null /* cursor */);
+ operation.setSuccess();
+
+ Log.i(TAG, "Successfully executed SyncResetAlbum. authority: " + authority
+ + ". albumId: " + albumId + ". Result count: " + writeCount);
+ }
+ }
+
+ private void executeSyncAdd(String authority, String expectedMediaCollectionId,
+ boolean isIncrementalSync, Bundle queryArgs) {
+ final Uri uri = getMediaUri(authority);
+ final List<String> expectedHonoredArgs = new ArrayList<>();
+ if (isIncrementalSync) {
+ expectedHonoredArgs.add(EXTRA_SYNC_GENERATION);
+ }
+
+ Log.i(TAG, "Executing SyncAdd. authority: " + authority);
+ try (PickerDbFacade.DbWriteOperation operation =
+ mDbFacade.beginAddMediaOperation(authority)) {
+ executePagedSync(uri, expectedMediaCollectionId, expectedHonoredArgs, queryArgs,
+ operation);
+ }
+ }
+
+ private void executeSyncAddAlbum(String authority, String albumId, Bundle queryArgs) {
+ final Uri uri = getMediaUri(authority);
+
+ Log.i(TAG, "Executing SyncAddAlbum. authority: " + authority + ". albumId: " + albumId);
+ try (PickerDbFacade.DbWriteOperation operation =
+ mDbFacade.beginAddAlbumMediaOperation(authority, albumId)) {
+
+ // We don't need to validate the mediaCollectionId for album_media sync since it's
+ // always a full sync
+ executePagedSync(uri, /* mediaCollectionId */ null, Arrays.asList(EXTRA_ALBUM_ID),
+ queryArgs, operation);
+ }
+ }
+
+ private void executeSyncRemove(String authority, String mediaCollectionId, Bundle queryArgs) {
+ final Uri uri = getDeletedMediaUri(authority);
+
+ Log.i(TAG, "Executing SyncRemove. authority: " + authority);
+ try (PickerDbFacade.DbWriteOperation operation =
+ mDbFacade.beginRemoveMediaOperation(authority)) {
+ executePagedSync(uri, mediaCollectionId, Arrays.asList(EXTRA_SYNC_GENERATION),
+ queryArgs, operation);
+ }
+ }
+
+ private void persistCloudProviderInfo(CloudProviderInfo info) {
+ synchronized (mLock) {
+ mCloudProviderInfo = info;
+ }
+
+ final String authority = info.authority;
+ final SharedPreferences.Editor editor = mUserPrefs.edit();
+
+ if (info.isEmpty()) {
+ editor.remove(PREFS_KEY_CLOUD_PROVIDER_AUTHORITY);
+ editor.putBoolean(PREFS_KEY_CLOUD_PROVIDER_PENDING_NOTIFICATTION, false);
+ } else {
+ editor.putString(PREFS_KEY_CLOUD_PROVIDER_AUTHORITY, authority);
+ editor.putBoolean(PREFS_KEY_CLOUD_PROVIDER_PENDING_NOTIFICATTION, true);
+ }
+
+ editor.apply();
+
+ if (SdkLevel.isAtLeastT()) {
+ try {
+ StorageManager sm = mContext.getSystemService(StorageManager.class);
+ sm.setCloudMediaProvider(authority);
+ } catch (SecurityException e) {
+ // When run as part of the unit tests, the notification fails because only the
+ // MediaProvider uid can notify
+ Log.w(TAG, "Failed to notify the system of cloud provider update to: " + authority);
+ }
+ }
+
+ Log.d(TAG, "Updated cloud provider to: " + authority);
+ }
+
+ private void cacheMediaCollectionInfo(String authority, Bundle bundle) {
+ if (authority == null) {
+ Log.d(TAG, "Ignoring cache media info for null authority with bundle: " + bundle);
+ return;
+ }
+
+ final SharedPreferences.Editor editor = mSyncPrefs.edit();
+
+ if (bundle == null) {
+ editor.remove(getPrefsKey(authority, MediaCollectionInfo.MEDIA_COLLECTION_ID));
+ editor.remove(getPrefsKey(authority, MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION));
+ } else {
+ final String collectionId = bundle.getString(MediaCollectionInfo.MEDIA_COLLECTION_ID);
+ final long generation = bundle.getLong(
+ MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION);
+
+ editor.putString(getPrefsKey(authority, MediaCollectionInfo.MEDIA_COLLECTION_ID),
+ collectionId);
+ editor.putLong(getPrefsKey(authority, MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION),
+ generation);
+ }
+
+ editor.apply();
+ }
+
+ private void resetCachedMediaCollectionInfo(String authority) {
+ cacheMediaCollectionInfo(authority, /* bundle */ null);
+ }
+
+ private Bundle getCachedMediaCollectionInfo(String authority) {
+ final Bundle bundle = new Bundle();
+
+ final String collectionId = mSyncPrefs.getString(
+ getPrefsKey(authority, MediaCollectionInfo.MEDIA_COLLECTION_ID),
+ /* default */ null);
+ final long generation = mSyncPrefs.getLong(
+ getPrefsKey(authority, MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION),
+ /* default */ -1);
+
+ bundle.putString(MediaCollectionInfo.MEDIA_COLLECTION_ID, collectionId);
+ bundle.putLong(MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION, generation);
+
+ return bundle;
+ }
+
+ private Bundle getLatestMediaCollectionInfo(String authority) {
+ return mContext.getContentResolver().call(getMediaCollectionInfoUri(authority),
+ CloudMediaProviderContract.METHOD_GET_MEDIA_COLLECTION_INFO, /* arg */ null,
+ /* extras */ null);
+ }
+
+ @SyncType
+ private SyncRequestParams getSyncRequestParams(String authority) {
+ if (authority == null) {
+ // Only cloud authority can be null
+ Log.d(TAG, "Fetching SyncRequestParams. Null cloud authority. Result: SYNC_TYPE_RESET");
+ return SyncRequestParams.forResetMedia();
+ }
+
+ final Bundle cachedMediaCollectionInfo = getCachedMediaCollectionInfo(authority);
+ final Bundle latestMediaCollectionInfo = getLatestMediaCollectionInfo(authority);
+
+ final String latestCollectionId =
+ latestMediaCollectionInfo.getString(MediaCollectionInfo.MEDIA_COLLECTION_ID);
+ final long latestGeneration =
+ latestMediaCollectionInfo.getLong(MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION);
+
+ final String cachedCollectionId =
+ cachedMediaCollectionInfo.getString(MediaCollectionInfo.MEDIA_COLLECTION_ID);
+ final long cachedGeneration = cachedMediaCollectionInfo.getLong(
+ MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION);
+
+ Log.d(TAG, "Fetching SyncRequestParams. Authority: " + authority
+ + ". LatestMediaCollectionInfo: " + latestMediaCollectionInfo
+ + ". CachedMediaCollectionInfo: " + cachedMediaCollectionInfo);
+
+ if (TextUtils.isEmpty(latestCollectionId) || latestGeneration < 0) {
+ throw new IllegalStateException("Unexpected media collection info. mediaCollectionId: "
+ + latestCollectionId + ". lastMediaSyncGeneration: " + latestGeneration);
+ }
+
+ if (!Objects.equals(latestCollectionId, cachedCollectionId)) {
+ Log.d(TAG, "SyncRequestParams. Authority: " + authority + ". Result: SYNC_TYPE_FULL");
+ return SyncRequestParams.forFullMedia(latestMediaCollectionInfo);
+ }
+
+ if (cachedGeneration == latestGeneration) {
+ Log.d(TAG, "SyncRequestParams. Authority: " + authority + ". Result: SYNC_TYPE_NONE");
+ return SyncRequestParams.forNone();
+ }
+
+ Log.d(TAG, "SyncRequestParams. Authority: " + authority
+ + ". Result: SYNC_TYPE_INCREMENTAL");
+ return SyncRequestParams.forIncremental(cachedGeneration, latestMediaCollectionInfo);
+ }
+
+ private String getPrefsKey(String authority, String key) {
+ return (isLocal(authority) ? PREFS_KEY_LOCAL_PREFIX : PREFS_KEY_CLOUD_PREFIX) + key;
+ }
+
+ private boolean isLocal(String authority) {
+ return mLocalProvider.equals(authority);
+ }
+
+ private Cursor query(Uri uri, Bundle extras) {
+ return mContext.getContentResolver().query(uri, /* projection */ null, extras,
+ /* cancellationSignal */ null);
+ }
+
+ private void executePagedSync(Uri uri, String expectedMediaCollectionId,
+ List<String> expectedHonoredArgs, Bundle queryArgs,
+ PickerDbFacade.DbWriteOperation dbWriteOperation) {
+ int cursorCount = 0;
+ int totalRowcount = 0;
+ // Set to check the uniqueness of tokens across pages.
+ Set<String> tokens = new ArraySet<>();
+
+ String nextPageToken = null;
+ do {
+ if (nextPageToken != null) {
+ queryArgs.putString(EXTRA_PAGE_TOKEN, nextPageToken);
+ }
+
+ try (Cursor cursor = query(uri, queryArgs)) {
+ nextPageToken = validateCursor(cursor, expectedMediaCollectionId,
+ expectedHonoredArgs, tokens);
+
+ int writeCount = dbWriteOperation.execute(cursor);
+
+ totalRowcount += writeCount;
+ cursorCount += cursor.getCount();
+ }
+ } while (nextPageToken != null);
+
+ dbWriteOperation.setSuccess();
+ Log.i(TAG, "Paged sync successful. QueryArgs: " + queryArgs + ". Result count: "
+ + totalRowcount + ". Cursor count: " + cursorCount);
+ }
+
+ private CloudProviderInfo getDefaultCloudProviderInfo(String cachedProvider) {
+ final List<CloudProviderInfo> infos =
+ getSupportedCloudProviders(/* ignoreAllowList */ false);
+
+ if (infos.size() == 1) {
+ Log.i(TAG, "Only 1 cloud provider found, hence "
+ + infos.get(0).authority + " is the default");
+ return infos.get(0);
+ } else {
+ final String defaultCloudProviderAuthority = StringUtils.getStringConfig(
+ mContext, R.string.config_default_cloud_provider_authority);
+ Log.i(TAG, "Found multiple cloud providers but OEM default is: "
+ + defaultCloudProviderAuthority);
+
+ if (cachedProvider != null) {
+ for (CloudProviderInfo info : infos) {
+ if (info.authority.equals(defaultCloudProviderAuthority)) {
+ return info;
+ }
+ }
+ }
+
+ if (defaultCloudProviderAuthority != null) {
+ for (CloudProviderInfo info : infos) {
+ if (info.authority.equals(defaultCloudProviderAuthority)) {
+ return info;
+ }
+ }
+ }
+ }
+
+ // No default set or default not installed
+ return CloudProviderInfo.EMPTY;
+ }
+
+ private Set<String> parseAllowedCloudProviders(String config) {
+ Set<String> allowedProviders = new ArraySet<>();
+ final String[] allowedProvidersConfig = config.split(",");
+
+ if (allowedProvidersConfig.length == 0 || allowedProvidersConfig[0].isEmpty()) {
+ Log.i(TAG, "Empty allowed cloud providers");
+ return allowedProviders;
+ }
+
+ for (String cloudProvider : allowedProvidersConfig) {
+ Log.d(TAG, "Parsed allowed cloud provider: " + cloudProvider + " from device config");
+ allowedProviders.add(cloudProvider);
+ }
+
+ Log.i(TAG, "Parsed " + allowedProviders.size() + " allowed providers from device config");
+ return allowedProviders;
+ }
+
+ private static String validateCursor(Cursor cursor, String expectedMediaCollectionId,
+ List<String> expectedHonoredArgs, Set<String> usedPageTokens) {
+ final Bundle bundle = cursor.getExtras();
+
+ if (bundle == null) {
+ throw new IllegalStateException("Unable to verify the media collection id");
+ }
+
+ final String mediaCollectionId = bundle.getString(EXTRA_MEDIA_COLLECTION_ID);
+ final String pageToken = bundle.getString(EXTRA_PAGE_TOKEN);
+ List<String> honoredArgs = bundle.getStringArrayList(EXTRA_HONORED_ARGS);
+ if (honoredArgs == null) {
+ honoredArgs = new ArrayList<>();
+ }
+
+ if (expectedMediaCollectionId != null
+ && !expectedMediaCollectionId.equals(mediaCollectionId)) {
+ throw new IllegalStateException("Mismatched media collection id. Expected: "
+ + expectedMediaCollectionId + ". Found: " + mediaCollectionId);
+ }
+
+ if (!honoredArgs.containsAll(expectedHonoredArgs)) {
+ throw new IllegalStateException("Unspecified honored args. Expected: "
+ + Arrays.toString(expectedHonoredArgs.toArray())
+ + ". Found: " + Arrays.toString(honoredArgs.toArray()));
+ }
+
+ if (usedPageTokens.contains(pageToken)) {
+ throw new IllegalStateException("Found repeated page token: " + pageToken);
+ } else {
+ usedPageTokens.add(pageToken);
+ }
+
+ return pageToken;
+ }
+
+ @VisibleForTesting
+ static class CloudProviderInfo {
+ static final CloudProviderInfo EMPTY = new CloudProviderInfo();
+ private final String authority;
+ private final String packageName;
+ private final int uid;
+
+ private CloudProviderInfo() {
+ this.authority = null;
+ this.packageName = null;
+ this.uid = -1;
+ }
+
+ CloudProviderInfo(String authority, String packageName, int uid) {
+ Objects.requireNonNull(authority);
+ Objects.requireNonNull(packageName);
+
+ this.authority = authority;
+ this.packageName = packageName;
+ this.uid = uid;
+ }
+
+ boolean isEmpty() {
+ return equals(EMPTY);
+ }
+
+ boolean matches(String packageName) {
+ return !isEmpty() && this.packageName.equals(packageName);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null || getClass() != obj.getClass()) return false;
+
+ CloudProviderInfo that = (CloudProviderInfo) obj;
+
+ return Objects.equals(authority, that.authority) &&
+ Objects.equals(packageName, that.packageName) &&
+ Objects.equals(uid, that.uid);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(authority, packageName, uid);
+ }
+ }
+
+ private static class SyncRequestParams {
+ private static final SyncRequestParams SYNC_REQUEST_NONE =
+ new SyncRequestParams(SYNC_TYPE_NONE);
+ private static final SyncRequestParams SYNC_REQUEST_MEDIA_RESET =
+ new SyncRequestParams(SYNC_TYPE_MEDIA_RESET);
+
+ private final int syncType;
+ // Only valid for SYNC_TYPE_INCREMENTAL
+ private final long syncGeneration;
+ // Only valid for SYNC_TYPE_[INCREMENTAL|FULL]
+ private final Bundle latestMediaCollectionInfo;
+
+ private SyncRequestParams(@SyncType int syncType) {
+ this(syncType, /* syncGeneration */ 0, /* latestMediaCollectionInfo */ null);
+ }
+
+ private SyncRequestParams(@SyncType int syncType, long syncGeneration,
+ Bundle latestMediaCollectionInfo) {
+ this.syncType = syncType;
+ this.syncGeneration = syncGeneration;
+ this.latestMediaCollectionInfo = latestMediaCollectionInfo;
+ }
+
+ String getMediaCollectionId() {
+ return latestMediaCollectionInfo.getString(MediaCollectionInfo.MEDIA_COLLECTION_ID);
+ }
+
+ static SyncRequestParams forNone() {
+ return SYNC_REQUEST_NONE;
+ }
+
+ static SyncRequestParams forResetMedia() {
+ return SYNC_REQUEST_MEDIA_RESET;
+ }
+
+ static SyncRequestParams forFullMedia(Bundle latestMediaCollectionInfo) {
+ return new SyncRequestParams(SYNC_TYPE_MEDIA_FULL, /* generation */ 0,
+ latestMediaCollectionInfo);
+ }
+
+ static SyncRequestParams forIncremental(long generation, Bundle latestMediaCollectionInfo) {
+ return new SyncRequestParams(SYNC_TYPE_MEDIA_INCREMENTAL, generation,
+ latestMediaCollectionInfo);
+ }
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/UnreliableVolumeFacade.java b/src/com/android/providers/media/photopicker/UnreliableVolumeFacade.java
new file mode 100644
index 0000000..5006663
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/UnreliableVolumeFacade.java
@@ -0,0 +1,115 @@
+/*
+ * 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;
+
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteConstraintException;
+import android.database.sqlite.SQLiteDatabase;
+
+import android.net.Uri;
+import android.util.Log;
+
+import com.android.providers.media.photopicker.data.UnreliableVolumeDatabaseHelper;
+import com.android.providers.media.util.SQLiteQueryBuilder;
+
+import java.util.List;
+
+public class UnreliableVolumeFacade {
+ private static final String TAG = "UnreliableVolumeFacade";
+ private static final String TABLE_NAME = "media";
+
+ private static final int FAIL = -1;
+ private static final int SUCCESS = 1;
+
+ private SQLiteDatabase mDatabase;
+ private SQLiteQueryBuilder mQueryBuilder;
+
+ private static final String[] mColumns = new String[]{
+ UnreliableVolumeDatabaseHelper.MediaColumns._ID,
+ UnreliableVolumeDatabaseHelper.MediaColumns._DATA,
+ UnreliableVolumeDatabaseHelper.MediaColumns.DATE_MODIFIED,
+ UnreliableVolumeDatabaseHelper.MediaColumns.DISPLAY_NAME,
+ UnreliableVolumeDatabaseHelper.MediaColumns.SIZE_BYTES,
+ UnreliableVolumeDatabaseHelper.MediaColumns.MIME_TYPE
+ };
+
+ public UnreliableVolumeFacade(Context context) {
+ UnreliableVolumeDatabaseHelper dbHelper = new UnreliableVolumeDatabaseHelper(context);
+ mDatabase = dbHelper.getWritableDatabase();
+ mQueryBuilder = createQueryBuilder();
+ }
+
+ /**
+ * @return the media item from the media table with given {@code uri}
+ */
+ public Cursor queryMediaId(Uri uri) {
+ String id = String.valueOf(ContentUris.parseId(uri));
+ final String selection = UnreliableVolumeDatabaseHelper.MediaColumns._ID + " = " + id;
+ return mDatabase.query(TABLE_NAME, mColumns, selection, /* selectionArgs */ null,
+ /* groupBy */ null, /* having */ null, /* orderBy */ null);
+ }
+
+ /**
+ * @return {@link Cursor} with all the rows in media table
+ */
+ public Cursor queryMediaAll() {
+ return mDatabase.query(TABLE_NAME, mColumns, /* selection */ null, /* selectionArgs */ null,
+ /* groupBy */ null, /* having */ null, /* orderBy */ null);
+ }
+
+ private SQLiteQueryBuilder createQueryBuilder() {
+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ qb.setTables(TABLE_NAME);
+
+ return qb;
+ }
+
+ private int insertFile(ContentValues value) {
+ try {
+ if (mQueryBuilder.insert(mDatabase, value) > 0) {
+ return SUCCESS;
+ }
+ } catch (SQLiteConstraintException e) {
+ Log.e(TAG, "Failed to insert picker db media. ContentValues: " + value, e);
+ }
+ return FAIL;
+ }
+
+ public int insertMedia(List<ContentValues> values) {
+ int numberItemsInserted = 0;
+ mDatabase.beginTransaction();
+ try {
+ for (ContentValues value : values) {
+ if (insertFile(value) == SUCCESS) {
+ numberItemsInserted++;
+ }
+ }
+ mDatabase.setTransactionSuccessful();
+ } finally {
+ mDatabase.endTransaction();
+ }
+
+ return numberItemsInserted;
+ }
+
+ public void deleteMedia() {
+ mDatabase.delete(TABLE_NAME, /* whereClause */null, /* whereArgs */null);
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/providers/media/photopicker/data/CloudProviderQueryExtras.java b/src/com/android/providers/media/photopicker/data/CloudProviderQueryExtras.java
new file mode 100644
index 0000000..0100c05
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/data/CloudProviderQueryExtras.java
@@ -0,0 +1,157 @@
+/*
+ * 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.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_DEFAULT;
+
+import android.os.Bundle;
+import android.provider.CloudMediaProviderContract;
+import android.provider.CloudMediaProviderContract.AlbumColumns;
+import android.provider.MediaStore;
+
+/**
+ * Represents the {@link CloudMediaProviderContract} extra filters from a {@link Bundle}.
+ */
+public class CloudProviderQueryExtras {
+ private final String mAlbumId;
+ private final String mAlbumAuthority;
+ private final String mMimeType;
+ private final long mSizeBytes;
+ private final long mGeneration;
+ private final int mLimit;
+ private final boolean mIsFavorite;
+ private final boolean mIsVideo;
+
+ private CloudProviderQueryExtras() {
+ mAlbumId = STRING_DEFAULT;
+ mAlbumAuthority = STRING_DEFAULT;
+ mMimeType = STRING_DEFAULT;
+ mSizeBytes = LONG_DEFAULT;
+ mGeneration = LONG_DEFAULT;
+ mLimit = LIMIT_DEFAULT;
+ mIsFavorite = BOOLEAN_DEFAULT;
+ mIsVideo = BOOLEAN_DEFAULT;
+ }
+
+ private CloudProviderQueryExtras (String albumId, String albumAuthority, String mimeType,
+ long sizeBytes, long generation, int limit, boolean isFavorite, boolean isVideo) {
+ mAlbumId = albumId;
+ mAlbumAuthority = albumAuthority;
+ mMimeType = mimeType;
+ mSizeBytes = sizeBytes;
+ mGeneration = generation;
+ mLimit = limit;
+ mIsFavorite = isFavorite;
+ mIsVideo = isVideo;
+ }
+
+ public static CloudProviderQueryExtras fromMediaStoreBundle(Bundle bundle,
+ String localProvider) {
+ if (bundle == null) {
+ return new 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 long sizeBytes = bundle.getLong(MediaStore.QUERY_ARG_SIZE_BYTES, LONG_DEFAULT);
+ final long generation = LONG_DEFAULT;
+ final int limit = bundle.getInt(MediaStore.QUERY_ARG_LIMIT, LIMIT_DEFAULT);
+
+ final boolean isFavorite = localProvider.equals(albumAuthority)
+ && AlbumColumns.ALBUM_ID_FAVORITES.equals(albumId);
+ final boolean isVideo = localProvider.equals(albumAuthority)
+ && AlbumColumns.ALBUM_ID_VIDEOS.equals(albumId);
+
+ return new CloudProviderQueryExtras(albumId, albumAuthority, mimeType, sizeBytes,
+ generation, limit, isFavorite, isVideo);
+ }
+
+ public static CloudProviderQueryExtras fromCloudMediaBundle(Bundle bundle) {
+ if (bundle == null) {
+ return new 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 long sizeBytes = bundle.getLong(CloudMediaProviderContract.EXTRA_SIZE_LIMIT_BYTES,
+ LONG_DEFAULT);
+ final long generation = bundle.getLong(CloudMediaProviderContract.EXTRA_SYNC_GENERATION,
+ LONG_DEFAULT);
+ final int limit = LIMIT_DEFAULT;
+
+ final boolean isFavorite = BOOLEAN_DEFAULT;
+ final boolean isVideo = BOOLEAN_DEFAULT;
+
+ return new CloudProviderQueryExtras(albumId, albumAuthority, mimeType, sizeBytes,
+ generation, limit, isFavorite, isVideo);
+ }
+
+ public PickerDbFacade.QueryFilter toQueryFilter() {
+ PickerDbFacade.QueryFilterBuilder qfb = new PickerDbFacade.QueryFilterBuilder(mLimit);
+ qfb.setSizeBytes(mSizeBytes);
+ qfb.setMimeType(mMimeType);
+ qfb.setIsFavorite(mIsFavorite);
+ qfb.setIsVideo(mIsVideo);
+ qfb.setAlbumId(mAlbumId);
+ return qfb.build();
+ }
+
+ public Bundle toCloudMediaBundle() {
+ final Bundle extras = new Bundle();
+ extras.putString(CloudMediaProviderContract.EXTRA_ALBUM_ID, mAlbumId);
+ extras.putString(CloudMediaProviderContract.EXTRA_MIME_TYPE, mMimeType);
+ extras.putLong(CloudMediaProviderContract.EXTRA_SIZE_LIMIT_BYTES, mSizeBytes);
+
+ return extras;
+ }
+
+ public String getAlbumId() {
+ return mAlbumId;
+ }
+
+ public String getAlbumAuthority() {
+ return mAlbumAuthority;
+ }
+
+ public String getMimeType() {
+ return mMimeType;
+ }
+
+ public long getSizeBytes() {
+ return mSizeBytes;
+ }
+
+ public long getGeneration() {
+ return mGeneration;
+ }
+
+ public boolean isFavorite() {
+ return mIsFavorite;
+ }
+
+ public boolean isVideo() {
+ return mIsVideo;
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/data/ExternalDbFacade.java b/src/com/android/providers/media/photopicker/data/ExternalDbFacade.java
new file mode 100644
index 0000000..d849c1f
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/data/ExternalDbFacade.java
@@ -0,0 +1,495 @@
+/*
+ * 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.data;
+
+import static android.content.ContentResolver.EXTRA_HONORED_ARGS;
+import static android.provider.CloudMediaProviderContract.AlbumColumns;
+import static android.provider.CloudMediaProviderContract.AlbumColumns.ALBUM_ID_CAMERA;
+import static android.provider.CloudMediaProviderContract.AlbumColumns.ALBUM_ID_DOWNLOADS;
+import static android.provider.CloudMediaProviderContract.AlbumColumns.ALBUM_ID_SCREENSHOTS;
+import static android.provider.CloudMediaProviderContract.EXTRA_ALBUM_ID;
+import static android.provider.CloudMediaProviderContract.EXTRA_MEDIA_COLLECTION_ID;
+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.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;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.sqlite.SQLiteConstraintException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.os.Bundle;
+import android.os.Environment;
+import android.provider.CloudMediaProviderContract;
+import android.provider.MediaStore;
+import android.provider.MediaStore.Files.FileColumns;
+import android.provider.MediaStore.MediaColumns;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.providers.media.DatabaseHelper;
+import com.android.providers.media.VolumeCache;
+import com.android.providers.media.photopicker.PickerSyncController;
+import com.android.providers.media.util.MimeUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * This is a facade that hides the complexities of executing some SQL statements on the external db.
+ * It does not do any caller permission checks and is only intended for internal use within the
+ * MediaProvider for the Photo Picker.
+ */
+public class ExternalDbFacade {
+ private static final String TAG = "ExternalDbFacade";
+ @VisibleForTesting
+ static final String TABLE_FILES = "files";
+
+ @VisibleForTesting
+ static final String TABLE_DELETED_MEDIA = "deleted_media";
+ @VisibleForTesting
+ static final String COLUMN_OLD_ID = "old_id";
+ private static final String COLUMN_OLD_ID_AS_ID = COLUMN_OLD_ID + " AS " +
+ CloudMediaProviderContract.MediaColumns.ID;
+ private static final String COLUMN_GENERATION_MODIFIED = MediaColumns.GENERATION_MODIFIED;
+
+ private static final String[] PROJECTION_MEDIA_COLUMNS = new String[] {
+ MediaColumns._ID + " AS " + CloudMediaProviderContract.MediaColumns.ID,
+ "COALESCE(" + MediaColumns.DATE_TAKEN + "," + MediaColumns.DATE_MODIFIED +
+ "* 1000) AS " + CloudMediaProviderContract.MediaColumns.DATE_TAKEN_MILLIS,
+ MediaColumns.GENERATION_MODIFIED + " AS " +
+ CloudMediaProviderContract.MediaColumns.SYNC_GENERATION,
+ MediaColumns.SIZE + " AS " + CloudMediaProviderContract.MediaColumns.SIZE_BYTES,
+ MediaColumns.MIME_TYPE + " AS " + CloudMediaProviderContract.MediaColumns.MIME_TYPE,
+ FileColumns._SPECIAL_FORMAT + " AS " +
+ CloudMediaProviderContract.MediaColumns.STANDARD_MIME_TYPE_EXTENSION,
+ MediaColumns.DURATION + " AS " + CloudMediaProviderContract.MediaColumns.DURATION_MILLIS,
+ MediaColumns.IS_FAVORITE + " AS " + CloudMediaProviderContract.MediaColumns.IS_FAVORITE
+ };
+ private static final String[] PROJECTION_MEDIA_INFO = new String[] {
+ "MAX(" + MediaColumns.GENERATION_MODIFIED + ") AS "
+ + MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION
+ };
+ private static final String[] PROJECTION_ALBUM_DB = new String[] {
+ "COUNT(" + MediaColumns._ID + ") AS " + CloudMediaProviderContract.AlbumColumns.MEDIA_COUNT,
+ "MAX(COALESCE(" + MediaColumns.DATE_TAKEN + "," + MediaColumns.DATE_MODIFIED +
+ "* 1000)) AS " + CloudMediaProviderContract.AlbumColumns.DATE_TAKEN_MILLIS,
+ MediaColumns._ID + " AS " + CloudMediaProviderContract.AlbumColumns.MEDIA_COVER_ID,
+ };
+
+ private static final String WHERE_IMAGE_TYPE = FileColumns.MEDIA_TYPE + " = "
+ + FileColumns.MEDIA_TYPE_IMAGE;
+ private static final String WHERE_VIDEO_TYPE = FileColumns.MEDIA_TYPE + " = "
+ + FileColumns.MEDIA_TYPE_VIDEO;
+ private static final String WHERE_MEDIA_TYPE = WHERE_IMAGE_TYPE + " OR " + WHERE_VIDEO_TYPE;
+ private static final String WHERE_IS_DOWNLOAD = MediaColumns.IS_DOWNLOAD + " = 1";
+ private static final String WHERE_NOT_TRASHED = MediaColumns.IS_TRASHED + " = 0";
+ private static final String WHERE_NOT_PENDING = MediaColumns.IS_PENDING + " = 0";
+ private static final String WHERE_GREATER_GENERATION =
+ MediaColumns.GENERATION_MODIFIED + " > ?";
+ private static final String WHERE_RELATIVE_PATH = MediaStore.MediaColumns.RELATIVE_PATH
+ + " LIKE ?";
+ private static final String WHERE_MIME_TYPE = MediaStore.MediaColumns.MIME_TYPE
+ + " LIKE ?";
+ private static final String WHERE_VOLUME_IN_PREFIX = MediaStore.MediaColumns.VOLUME_NAME
+ + " IN %s";
+
+ public static final String RELATIVE_PATH_SCREENSHOTS =
+ "%/" + Environment.DIRECTORY_SCREENSHOTS + "/%";
+
+ public static final String RELATIVE_PATH_CAMERA = Environment.DIRECTORY_DCIM + "/Camera/%";
+
+ @VisibleForTesting
+ static String[] LOCAL_ALBUM_IDS = {
+ ALBUM_ID_CAMERA,
+ ALBUM_ID_SCREENSHOTS,
+ ALBUM_ID_DOWNLOADS
+ };
+
+ private final Context mContext;
+ private final DatabaseHelper mDatabaseHelper;
+ private final VolumeCache mVolumeCache;
+
+ public ExternalDbFacade(Context context, DatabaseHelper databaseHelper,
+ VolumeCache volumeCache) {
+ mContext = context;
+ mDatabaseHelper = databaseHelper;
+ mVolumeCache = volumeCache;
+ }
+
+ /**
+ * Returns {@code true} if the PhotoPicker should be notified of this change, {@code false}
+ * otherwise
+ */
+ public boolean onFileInserted(int mediaType, boolean isPending) {
+ if (!mDatabaseHelper.isExternal()) {
+ return false;
+ }
+
+ return !isPending && MimeUtils.isImageOrVideoMediaType(mediaType);
+ }
+
+ /**
+ * Adds or removes media to the deleted_media tables
+ *
+ * Returns {@code true} if the PhotoPicker should be notified of this change, {@code false}
+ * otherwise
+ */
+ public boolean onFileUpdated(long oldId, int oldMediaType, int newMediaType,
+ boolean oldIsTrashed, boolean newIsTrashed, boolean oldIsPending,
+ boolean newIsPending, boolean oldIsFavorite, boolean newIsFavorite,
+ int oldSpecialFormat, int newSpecialFormat) {
+ if (!mDatabaseHelper.isExternal()) {
+ return false;
+ }
+
+ final boolean oldIsMedia= MimeUtils.isImageOrVideoMediaType(oldMediaType);
+ final boolean newIsMedia = MimeUtils.isImageOrVideoMediaType(newMediaType);
+
+ final boolean oldIsVisible = !oldIsTrashed && !oldIsPending;
+ final boolean newIsVisible = !newIsTrashed && !newIsPending;
+
+ final boolean oldIsVisibleMedia = oldIsVisible && oldIsMedia;
+ final boolean newIsVisibleMedia = newIsVisible && newIsMedia;
+
+ if (!oldIsVisibleMedia && newIsVisibleMedia) {
+ // Was not visible media and is now visible media
+ removeDeletedMedia(oldId);
+ return true;
+ } else if (oldIsVisibleMedia && !newIsVisibleMedia) {
+ // Was visible media and is now not visible media
+ addDeletedMedia(oldId);
+ return true;
+ }
+
+ if (newIsVisibleMedia) {
+ return (oldIsFavorite != newIsFavorite) || (oldSpecialFormat != newSpecialFormat);
+ }
+
+
+ // Do nothing, not an interesting change
+ return false;
+ }
+
+ /**
+ * Adds or removes media to the deleted_media tables
+ *
+ * Returns {@code true} if the PhotoPicker should be notified of this change, {@code false}
+ * otherwise
+ */
+ public boolean onFileDeleted(long id, int mediaType) {
+ if (!mDatabaseHelper.isExternal()) {
+ return false;
+ }
+ if (!MimeUtils.isImageOrVideoMediaType(mediaType)) {
+ return false;
+ }
+
+ addDeletedMedia(id);
+ return true;
+ }
+
+ /**
+ * Adds media with row id {@code oldId} to the deleted_media table. Returns {@code true} if
+ * if it was successfully added, {@code false} otherwise.
+ */
+ @VisibleForTesting
+ boolean addDeletedMedia(long oldId) {
+ return mDatabaseHelper.runWithTransaction((db) -> {
+ SQLiteQueryBuilder qb = createDeletedMediaQueryBuilder();
+
+ ContentValues cv = new ContentValues();
+ cv.put(COLUMN_OLD_ID, oldId);
+ cv.put(COLUMN_GENERATION_MODIFIED, DatabaseHelper.getGeneration(db));
+
+ try {
+ return qb.insert(db, cv) > 0;
+ } catch (SQLiteConstraintException e) {
+ String select = COLUMN_OLD_ID + " = ?";
+ String[] selectionArgs = new String[] {String.valueOf(oldId)};
+
+ return qb.update(db, cv, select, selectionArgs) > 0;
+ }
+ });
+ }
+
+ /**
+ * Removes media with row id {@code oldId} from the deleted_media table. Returns {@code true} if
+ * it was successfully removed, {@code false} otherwise.
+ */
+ @VisibleForTesting
+ boolean removeDeletedMedia(long oldId) {
+ return mDatabaseHelper.runWithTransaction(db -> {
+ SQLiteQueryBuilder qb = createDeletedMediaQueryBuilder();
+
+ return qb.delete(db, COLUMN_OLD_ID + " = ?", new String[] {String.valueOf(oldId)}) > 0;
+ });
+ }
+
+ /**
+ * Returns all items from the deleted_media table.
+ */
+ public Cursor queryDeletedMedia(long generation) {
+ final Cursor cursor = mDatabaseHelper.runWithTransaction(db -> {
+ SQLiteQueryBuilder qb = createDeletedMediaQueryBuilder();
+ String[] projection = new String[] {COLUMN_OLD_ID_AS_ID};
+ String select = COLUMN_GENERATION_MODIFIED + " > ?";
+ String[] selectionArgs = new String[] {String.valueOf(generation)};
+
+ return qb.query(db, projection, select, selectionArgs, /* groupBy */ null,
+ /* having */ null, /* orderBy */ null);
+ });
+
+ cursor.setExtras(getCursorExtras(generation, /* albumId */ null));
+ return cursor;
+ }
+
+ /**
+ * 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) {
+ final List<String> selectionArgs = new ArrayList<>();
+ final String orderBy = CloudMediaProviderContract.MediaColumns.DATE_TAKEN_MILLIS + " DESC";
+
+ final Cursor cursor = mDatabaseHelper.runWithTransaction(db -> {
+ SQLiteQueryBuilder qb = createMediaQueryBuilder();
+ qb.appendWhereStandalone(WHERE_GREATER_GENERATION);
+ selectionArgs.add(String.valueOf(generation));
+
+ selectionArgs.addAll(appendWhere(qb, albumId, mimeType));
+
+ return qb.query(db, PROJECTION_MEDIA_COLUMNS, /* select */ null,
+ selectionArgs.toArray(new String[selectionArgs.size()]), /* groupBy */ null,
+ /* having */ null, orderBy);
+ });
+
+ cursor.setExtras(getCursorExtras(generation, albumId));
+ return cursor;
+ }
+
+ private Bundle getCursorExtras(long generation, String albumId) {
+ final Bundle bundle = new Bundle();
+ final ArrayList<String> honoredArgs = new ArrayList<>();
+
+ if (generation > LONG_DEFAULT) {
+ honoredArgs.add(EXTRA_SYNC_GENERATION);
+ }
+ if (!TextUtils.isEmpty(albumId)) {
+ honoredArgs.add(EXTRA_ALBUM_ID);
+ }
+
+ bundle.putString(EXTRA_MEDIA_COLLECTION_ID, getMediaCollectionId());
+ bundle.putStringArrayList(EXTRA_HONORED_ARGS, honoredArgs);
+
+ return bundle;
+ }
+
+ /**
+ * Returns the total count and max {@link MediaColumns#GENERATION_MODIFIED} value
+ * of the media items in the files table greater than {@code generation}.
+ */
+ private Cursor getMediaCollectionInfoCursor(long generation) {
+ final String[] selectionArgs = new String[] {String.valueOf(generation)};
+ final String[] projection = new String[] {
+ MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION
+ };
+
+ return mDatabaseHelper.runWithTransaction(db -> {
+ SQLiteQueryBuilder qbMedia = createMediaQueryBuilder();
+ qbMedia.appendWhereStandalone(WHERE_GREATER_GENERATION);
+ SQLiteQueryBuilder qbDeletedMedia = createDeletedMediaQueryBuilder();
+ qbDeletedMedia.appendWhereStandalone(WHERE_GREATER_GENERATION);
+
+ try (Cursor mediaCursor = query(qbMedia, db, PROJECTION_MEDIA_INFO, selectionArgs);
+ Cursor deletedMediaCursor = query(qbDeletedMedia, db,
+ PROJECTION_MEDIA_INFO, selectionArgs)) {
+ final int mediaGenerationIndex = mediaCursor.getColumnIndexOrThrow(
+ MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION);
+ final int deletedMediaGenerationIndex =
+ deletedMediaCursor.getColumnIndexOrThrow(
+ MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION);
+
+ long mediaGeneration = 0;
+ if (mediaCursor.moveToFirst()) {
+ mediaGeneration = mediaCursor.getLong(mediaGenerationIndex);
+ }
+
+ long deletedMediaGeneration = 0;
+ if (deletedMediaCursor.moveToFirst()) {
+ deletedMediaGeneration = deletedMediaCursor.getLong(
+ deletedMediaGenerationIndex);
+ }
+
+ long maxGeneration = Math.max(mediaGeneration, deletedMediaGeneration);
+ MatrixCursor result = new MatrixCursor(projection);
+ result.addRow(new Long[] { maxGeneration });
+
+ return result;
+ }
+ });
+ }
+
+ public Bundle getMediaCollectionInfo(long generation) {
+ final Bundle bundle = new Bundle();
+ try (Cursor cursor = getMediaCollectionInfoCursor(generation)) {
+ if (cursor.moveToFirst()) {
+ int generationIndex = cursor.getColumnIndexOrThrow(
+ MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION);
+
+ bundle.putString(MediaCollectionInfo.MEDIA_COLLECTION_ID, getMediaCollectionId());
+ bundle.putLong(MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION,
+ cursor.getLong(generationIndex));
+ }
+ }
+ return bundle;
+ }
+
+ /**
+ * Returns the media item categories from the files table.
+ * 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) {
+ 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));
+
+ return qb.query(db, PROJECTION_ALBUM_DB, /* selection */ null,
+ selectionArgs.toArray(new String[selectionArgs.size()]), /* groupBy */ null,
+ /* having */ null, /* orderBy */ null);
+ });
+
+ if (cursor == null || !cursor.moveToFirst()) {
+ continue;
+ }
+
+ long count = getCursorLong(cursor, CloudMediaProviderContract.AlbumColumns.MEDIA_COUNT);
+ if (count == 0) {
+ continue;
+ }
+
+ final String[] projectionValue = new String[] {
+ /* albumId */ albumId,
+ getCursorString(cursor, AlbumColumns.DATE_TAKEN_MILLIS),
+ /* displayName */ albumId,
+ getCursorString(cursor, AlbumColumns.MEDIA_COVER_ID),
+ String.valueOf(count),
+ PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY
+ };
+
+ c.addRow(projectionValue);
+ }
+
+ return c;
+ }
+
+ private static Cursor query(SQLiteQueryBuilder qb, SQLiteDatabase db, String[] projection,
+ String[] selectionArgs) {
+ return qb.query(db, projection, /* select */ null, selectionArgs,
+ /* groupBy */ null, /* having */ null, /* orderBy */ null);
+ }
+
+ private static List<String> appendWhere(SQLiteQueryBuilder qb, String albumId,
+ String mimeType) {
+ final List<String> selectionArgs = new ArrayList<>();
+
+ if (mimeType != null) {
+ qb.appendWhereStandalone(WHERE_MIME_TYPE);
+ selectionArgs.add(replaceMatchAnyChar(mimeType));
+ }
+
+ if (albumId == null) {
+ return selectionArgs;
+ }
+
+ switch (albumId) {
+ case ALBUM_ID_CAMERA:
+ qb.appendWhereStandalone(WHERE_RELATIVE_PATH);
+ selectionArgs.add(RELATIVE_PATH_CAMERA);
+ break;
+ case ALBUM_ID_SCREENSHOTS:
+ qb.appendWhereStandalone(WHERE_RELATIVE_PATH);
+ selectionArgs.add(RELATIVE_PATH_SCREENSHOTS);
+ break;
+ case ALBUM_ID_DOWNLOADS:
+ qb.appendWhereStandalone(WHERE_IS_DOWNLOAD);
+ break;
+ default:
+ Log.w(TAG, "No match for album: " + albumId);
+ break;
+ }
+
+ return selectionArgs;
+ }
+
+ private static SQLiteQueryBuilder createDeletedMediaQueryBuilder() {
+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ qb.setTables(TABLE_DELETED_MEDIA);
+
+ return qb;
+ }
+
+ private SQLiteQueryBuilder createMediaQueryBuilder() {
+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ qb.setTables(TABLE_FILES);
+ qb.appendWhereStandalone(WHERE_MEDIA_TYPE);
+ qb.appendWhereStandalone(WHERE_NOT_TRASHED);
+ qb.appendWhereStandalone(WHERE_NOT_PENDING);
+
+ String[] volumes = getVolumeList();
+ if (volumes.length > 0) {
+ qb.appendWhereStandalone(buildWhereVolumeIn(volumes));
+ }
+
+ return qb;
+ }
+
+ private String buildWhereVolumeIn(String[] volumes) {
+ return String.format(WHERE_VOLUME_IN_PREFIX, bindList((Object[]) volumes));
+ }
+
+ private String[] getVolumeList() {
+ String[] volumeNames = mVolumeCache.getExternalVolumeNames().toArray(new String[0]);
+ Arrays.sort(volumeNames);
+
+ return volumeNames;
+ }
+
+ private String getMediaCollectionId() {
+ final String[] volumes = getVolumeList();
+ if (volumes.length == 0) {
+ return MediaStore.getVersion(mContext);
+ }
+
+ return MediaStore.getVersion(mContext) + ":" + TextUtils.join(":", getVolumeList());
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/data/ItemsProvider.java b/src/com/android/providers/media/photopicker/data/ItemsProvider.java
new file mode 100644
index 0000000..fb32872
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/data/ItemsProvider.java
@@ -0,0 +1,220 @@
+/*
+ * 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.data;
+
+import static com.android.providers.media.photopicker.util.CursorUtils.getCursorString;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentProvider;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.Cursor;
+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;
+
+/**
+ * Provides image and video items from {@link MediaStore} collection to the Photo Picker.
+ */
+public class ItemsProvider {
+
+ private static final String TAG = ItemsProvider.class.getSimpleName();
+
+ private final Context mContext;
+
+ public ItemsProvider(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * 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}.
+ *
+ * <p>
+ * By default the returned {@link Cursor} sorts by latest date taken.
+ *
+ * @param category the category of items to return. May be cloud, local or merged albums like
+ * favorites or videos.
+ * @param offset the offset after which to return items.
+ * @param limit the limit of number of items to return.
+ * @param mimeType the mime type of item. {@code null} returns all images/videos that are
+ * scanned by {@link MediaStore}.
+ * @param userId the {@link UserId} of the user to get items as.
+ * {@code null} defaults to {@link UserId#CURRENT_USER}
+ *
+ * @return {@link Cursor} to images/videos on external storage that are scanned by
+ * {@link MediaStore}. The returned cursor is filtered based on params passed, it {@code null}
+ * if there are no such images/videos. The Cursor for each item contains {@link ItemColumns}
+ */
+ @Nullable
+ public Cursor getItems(Category category, int offset,
+ int limit, @Nullable String mimeType, @Nullable UserId userId) throws
+ IllegalArgumentException {
+ if (userId == null) {
+ userId = UserId.CURRENT_USER;
+ }
+
+ return queryMedia(limit, mimeType, category, userId);
+ }
+
+ /**
+ * Returns a {@link Cursor} to all non-empty categories in which images/videos are categorised.
+ * This includes:
+ * * A constant list of local categories for on-device images/videos: {@link Category}
+ * * Albums provided by selected cloud provider
+ *
+ * @param mimeType the mime type of item. {@code null} returns all images/videos that are
+ * scanned by {@link MediaStore}.
+ * @param userId the {@link UserId} of the user to get categories as.
+ * {@code null} defaults to {@link UserId#CURRENT_USER}.
+ *
+ * @return {@link Cursor} for each category would contain the following columns in
+ * their relative order:
+ * categoryName: {@link CategoryColumns#NAME} The name of the category,
+ * categoryCoverId: {@link CategoryColumns#COVER_ID} The id for the cover of
+ * the category. By default this will be the most recent image/video in that
+ * category,
+ * categoryNumberOfItems: {@link CategoryColumns#NUMBER_OF_ITEMS} number of image/video items
+ * in the category,
+ */
+ @Nullable
+ public Cursor getCategories(@Nullable String mimeType, @Nullable UserId userId) {
+ if (userId == null) {
+ userId = UserId.CURRENT_USER;
+ }
+
+ return queryAlbums(mimeType, userId);
+ }
+
+ private Cursor queryMedia(int limit, @Nullable String mimeType,
+ @NonNull Category category, @NonNull UserId userId)
+ throws IllegalStateException {
+ final Bundle extras = new Bundle();
+ try (ContentProviderClient client = userId.getContentResolver(mContext)
+ .acquireUnstableContentProviderClient(MediaStore.AUTHORITY)) {
+ if (client == null) {
+ Log.e(TAG, "Unable to acquire unstable content provider for "
+ + MediaStore.AUTHORITY);
+ return null;
+ }
+ extras.putInt(MediaStore.QUERY_ARG_LIMIT, limit);
+ extras.putString(MediaStore.QUERY_ARG_MIME_TYPE, mimeType);
+ extras.putString(MediaStore.QUERY_ARG_ALBUM_ID, category.getId());
+ extras.putString(MediaStore.QUERY_ARG_ALBUM_AUTHORITY, category.getAuthority());
+
+ final Uri uri = PickerUriResolver.PICKER_INTERNAL_URI.buildUpon()
+ .appendPath(PickerUriResolver.MEDIA_PATH).build();
+
+ return client.query(uri, /* projection */ null, extras, /* cancellationSignal */ null);
+ } catch (RemoteException | NameNotFoundException ignored) {
+ // Do nothing, return null.
+ Log.e(TAG, "Failed to query merged media with extras: "
+ + extras + ". userId = " + userId, ignored);
+ return null;
+ }
+ }
+
+ @Nullable
+ private Cursor queryAlbums(@Nullable String mimeType, @NonNull UserId userId) {
+ final Bundle extras = new Bundle();
+ try (ContentProviderClient client = userId.getContentResolver(mContext)
+ .acquireUnstableContentProviderClient(MediaStore.AUTHORITY)) {
+ if (client == null) {
+ Log.e(TAG, "Unable to acquire unstable content provider for "
+ + MediaStore.AUTHORITY);
+ return null;
+ }
+ extras.putString(MediaStore.QUERY_ARG_MIME_TYPE, mimeType);
+
+ final Uri uri = PickerUriResolver.PICKER_INTERNAL_URI.buildUpon()
+ .appendPath(PickerUriResolver.ALBUM_PATH).build();
+
+ return client.query(uri, /* projection */ null, extras, /* cancellationSignal */ null);
+ } catch (RemoteException | NameNotFoundException ignored) {
+ // Do nothing, return null.
+ Log.w(TAG, "Failed to query merged albums with extras: "
+ + extras + ". userId = " + userId, ignored);
+ return null;
+ }
+ }
+
+ public static Uri getItemsUri(String id, String authority, UserId userId) {
+ final Uri uri = PickerUriResolver.getMediaUri(authority).buildUpon()
+ .appendPath(id).build();
+
+ if (userId.equals(UserId.CURRENT_USER)) {
+ return uri;
+ }
+
+ return createContentUriForUser(uri, userId.getUserHandle());
+ }
+
+ private static Uri createContentUriForUser(Uri uri, UserHandle userHandle) {
+ if (SdkLevel.isAtLeastS()) {
+ return ContentProvider.createContentUriForUser(uri, userHandle);
+ }
+
+ return createContentUriForUserImpl(uri, userHandle);
+ }
+
+ /**
+ * This method is a copy of {@link ContentProvider#createContentUriForUser(Uri, UserHandle)}
+ * which is a System API added in Android S.
+ */
+ private static Uri createContentUriForUserImpl(Uri uri, UserHandle userHandle) {
+ if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
+ throw new IllegalArgumentException(String.format(
+ "Given URI [%s] is not a content URI: ", uri));
+ }
+
+ int userId = userHandle.getIdentifier();
+ if (uriHasUserId(uri)) {
+ if (String.valueOf(userId).equals(uri.getUserInfo())) {
+ return uri;
+ }
+ throw new IllegalArgumentException(String.format(
+ "Given URI [%s] already has a user ID, different from given user handle [%s]",
+ uri,
+ userId));
+ }
+
+ Uri.Builder builder = uri.buildUpon();
+ builder.encodedAuthority(
+ "" + userHandle.getIdentifier() + "@" + uri.getEncodedAuthority());
+ return builder.build();
+ }
+
+ private static boolean uriHasUserId(Uri uri) {
+ if (uri == null) return false;
+ return !TextUtils.isEmpty(uri.getUserInfo());
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/data/MuteStatus.java b/src/com/android/providers/media/photopicker/data/MuteStatus.java
new file mode 100644
index 0000000..d433117
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/data/MuteStatus.java
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+package com.android.providers.media.photopicker.data;
+
+/**
+ * Tracks the status of volume mute request from the user.
+ */
+public final class MuteStatus {
+ /**
+ * Always start video preview with volume off
+ */
+ private boolean mIsVolumeMuted = true;
+
+ public MuteStatus() {};
+
+ /**
+ * Sets the volume to mute/unmute
+ * @param isVolumeMuted - {@code true} if the volume state should be set to mute.
+ * {@code false} otherwise.
+ */
+ public void setVolumeMuted(boolean isVolumeMuted) {
+ mIsVolumeMuted = isVolumeMuted;
+ }
+
+ /**
+ * @return {@code isVolumeMuted}
+ */
+ public boolean isVolumeMuted() {
+ return mIsVolumeMuted;
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/data/PickerDatabaseHelper.java b/src/com/android/providers/media/photopicker/data/PickerDatabaseHelper.java
new file mode 100644
index 0000000..eca0ba6
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/data/PickerDatabaseHelper.java
@@ -0,0 +1,169 @@
+/*
+ * 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.data;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.providers.media.photopicker.PickerSyncController;
+
+/**
+ * Wrapper class for the photo picker database. Can open the actual database
+ * on demand, create and upgrade the schema, etc.
+ *
+ * @see DatabaseHelper
+ */
+public class PickerDatabaseHelper extends SQLiteOpenHelper {
+ private static final String TAG = "PickerDatabaseHelper";
+ @VisibleForTesting
+ static final String PICKER_DATABASE_NAME = "picker.db";
+
+ private static final int VERSION_T = 7;
+ private static final int VERSION_LATEST = VERSION_T;
+
+ final Context mContext;
+ final String mName;
+ final int mVersion;
+
+ public PickerDatabaseHelper(Context context) {
+ this(context, PICKER_DATABASE_NAME, VERSION_LATEST);
+ }
+
+ public PickerDatabaseHelper(Context context, String name, int version) {
+ super(context, name, null, version);
+ mContext = context;
+ mName = name;
+ mVersion = version;
+
+ setWriteAheadLoggingEnabled(true);
+ }
+
+ @Override
+ public void onCreate(final SQLiteDatabase db) {
+ Log.v(TAG, "onCreate() for " + mName);
+
+ resetData(db);
+ }
+
+ @Override
+ public void onUpgrade(final SQLiteDatabase db, final int oldV, final int newV) {
+ Log.v(TAG, "onUpgrade() for " + mName + " from " + oldV + " to " + newV);
+
+ resetData(db);
+ }
+
+ @Override
+ public void onDowngrade(final SQLiteDatabase db, final int oldV, final int newV) {
+ Log.v(TAG, "onDowngrade() for " + mName + " from " + oldV + " to " + newV);
+
+ resetData(db);
+ }
+
+ private void resetData(SQLiteDatabase db) {
+ clearPickerPrefs(mContext);
+ createLatestSchema(db);
+ createLatestIndexes(db);
+ }
+
+ @VisibleForTesting
+ static void makePristineSchema(SQLiteDatabase db) {
+ // drop all tables
+ Cursor c = db.query("sqlite_master", new String[] {"name"}, "type is 'table'", null, null,
+ null, null);
+ while (c.moveToNext()) {
+ if (c.getString(0).startsWith("sqlite_")) continue;
+ db.execSQL("DROP TABLE IF EXISTS " + c.getString(0));
+ }
+ c.close();
+ }
+
+ @VisibleForTesting
+ static void makePristineIndexes(SQLiteDatabase db) {
+ // drop all indexes
+ Cursor c = db.query("sqlite_master", new String[] {"name"}, "type is 'index'",
+ null, null, null, null);
+ while (c.moveToNext()) {
+ if (c.getString(0).startsWith("sqlite_")) continue;
+ db.execSQL("DROP INDEX IF EXISTS " + c.getString(0));
+ }
+ c.close();
+ }
+
+ private static void createLatestSchema(SQLiteDatabase db) {
+ makePristineSchema(db);
+
+ db.execSQL("CREATE TABLE media (_id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + "local_id TEXT,"
+ + "cloud_id TEXT UNIQUE,"
+ + "is_visible INTEGER CHECK(is_visible == 1),"
+ + "date_taken_ms INTEGER NOT NULL CHECK(date_taken_ms >= 0),"
+ + "sync_generation INTEGER NOT NULL CHECK(sync_generation >= 0),"
+ + "size_bytes INTEGER NOT NULL CHECK(size_bytes > 0),"
+ + "duration_ms INTEGER CHECK(duration_ms >= 0),"
+ + "mime_type TEXT NOT NULL,"
+ + "standard_mime_type_extension INTEGER,"
+ + "is_favorite INTEGER,"
+ + "CHECK(local_id IS NOT NULL OR cloud_id IS NOT NULL),"
+ + "UNIQUE(local_id, is_visible))");
+
+ db.execSQL("CREATE TABLE album_media (_id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + "local_id TEXT,"
+ + "cloud_id TEXT,"
+ + "album_id TEXT,"
+ + "date_taken_ms INTEGER NOT NULL CHECK(date_taken_ms >= 0),"
+ + "sync_generation INTEGER NOT NULL CHECK(sync_generation >= 0),"
+ + "size_bytes INTEGER NOT NULL CHECK(size_bytes > 0),"
+ + "duration_ms INTEGER CHECK(duration_ms >= 0),"
+ + "mime_type TEXT NOT NULL,"
+ + "standard_mime_type_extension INTEGER,"
+ + "CHECK((local_id IS NULL AND cloud_id IS NOT NULL) "
+ + "OR (local_id IS NOT NULL AND cloud_id IS NULL)),"
+ + "UNIQUE(local_id, album_id),"
+ + "UNIQUE(cloud_id, album_id))");
+ }
+
+ private static void createLatestIndexes(SQLiteDatabase db) {
+ makePristineIndexes(db);
+
+ db.execSQL("CREATE INDEX local_id_index on media(local_id)");
+ db.execSQL("CREATE INDEX cloud_id_index on media(cloud_id)");
+ db.execSQL("CREATE INDEX is_visible_index on media(is_visible)");
+ db.execSQL("CREATE INDEX date_taken_index on media(date_taken_ms)");
+ db.execSQL("CREATE INDEX size_index on media(size_bytes)");
+ db.execSQL("CREATE INDEX mime_type_index on media(mime_type)");
+ db.execSQL("CREATE INDEX is_favorite_index on media(is_favorite)");
+
+ db.execSQL("CREATE INDEX local_id_album_index on album_media(local_id)");
+ db.execSQL("CREATE INDEX cloud_id_album_index on album_media(cloud_id)");
+ db.execSQL("CREATE INDEX date_taken_album_index on album_media(date_taken_ms)");
+ db.execSQL("CREATE INDEX size_album_index on album_media(size_bytes)");
+ db.execSQL("CREATE INDEX mime_type_album_index on album_media(mime_type)");
+ }
+
+ private static void clearPickerPrefs(Context context) {
+ final SharedPreferences prefs = context.getSharedPreferences(
+ PickerSyncController.PICKER_SYNC_PREFS_FILE_NAME, Context.MODE_PRIVATE);
+ final SharedPreferences.Editor editor = prefs.edit();
+ editor.clear();
+ editor.commit();
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/data/PickerDbFacade.java b/src/com/android/providers/media/photopicker/data/PickerDbFacade.java
new file mode 100644
index 0000000..3585a94
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/data/PickerDbFacade.java
@@ -0,0 +1,1149 @@
+/*
+ * 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.data;
+
+import static android.provider.CloudMediaProviderContract.AlbumColumns;
+import static android.provider.CloudMediaProviderContract.AlbumColumns.ALBUM_ID_FAVORITES;
+import static android.provider.CloudMediaProviderContract.AlbumColumns.ALBUM_ID_VIDEOS;
+import static android.provider.CloudMediaProviderContract.MediaColumns;
+import static android.provider.MediaStore.PickerMediaColumns;
+
+import static com.android.providers.media.PickerUriResolver.getMediaUri;
+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.replaceMatchAnyChar;
+import static com.android.providers.media.util.SyntheticPathUtils.getPickerRelativePath;
+
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.sqlite.SQLiteConstraintException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.net.Uri;
+import android.os.SystemProperties;
+import android.provider.DeviceConfig;
+import android.provider.CloudMediaProviderContract;
+import android.provider.MediaStore;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.providers.media.photopicker.PickerSyncController;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * This is a facade that hides the complexities of executing some SQL statements on the picker db.
+ * It does not do any caller permission checks and is only intended for internal use within the
+ * MediaProvider for the Photo Picker.
+ */
+public class PickerDbFacade {
+ private static final String VIDEO_MIME_TYPES = "video/%";
+
+ private final Object mLock = new Object();
+ private final Context mContext;
+ private final SQLiteDatabase mDatabase;
+ private final String mLocalProvider;
+ private String mCloudProvider;
+
+ public PickerDbFacade(Context context) {
+ this(context, PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY);
+ }
+
+ @VisibleForTesting
+ public PickerDbFacade(Context context, String localProvider) {
+ this(context, localProvider, new PickerDatabaseHelper(context));
+ }
+
+ @VisibleForTesting
+ public PickerDbFacade(Context context, String localProvider, PickerDatabaseHelper dbHelper) {
+ mContext = context;
+ mLocalProvider = localProvider;
+ mDatabase = dbHelper.getWritableDatabase();
+ }
+
+ private static final String TAG = "PickerDbFacade";
+
+ private static final int RETRY = 0;
+ private static final int SUCCESS = 1;
+ private static final int FAIL = -1;
+
+ private static final String TABLE_MEDIA = "media";
+ // Intentionally use /sdcard path so that the receiving app resolves it to it's per-user
+ // external storage path, e.g. /storage/emulated/<userid>. That way FUSE cross-user access is
+ // not required for picker paths sent across users
+ private static final String PICKER_PATH = "/sdcard/" + getPickerRelativePath();
+ private static final String TABLE_ALBUM_MEDIA = "album_media";
+
+ @VisibleForTesting
+ public static final String KEY_ID = "_id";
+ @VisibleForTesting
+ public static final String KEY_LOCAL_ID = "local_id";
+ @VisibleForTesting
+ public static final String KEY_CLOUD_ID = "cloud_id";
+ @VisibleForTesting
+ public static final String KEY_IS_VISIBLE = "is_visible";
+ @VisibleForTesting
+ public static final String KEY_DATE_TAKEN_MS = "date_taken_ms";
+ @VisibleForTesting
+ public static final String KEY_SYNC_GENERATION = "sync_generation";
+ @VisibleForTesting
+ public static final String KEY_SIZE_BYTES = "size_bytes";
+ @VisibleForTesting
+ public static final String KEY_DURATION_MS = "duration_ms";
+ @VisibleForTesting
+ public static final String KEY_MIME_TYPE = "mime_type";
+ @VisibleForTesting
+ public static final String KEY_STANDARD_MIME_TYPE_EXTENSION = "standard_mime_type_extension";
+ @VisibleForTesting
+ public static final String KEY_IS_FAVORITE = "is_favorite";
+ @VisibleForTesting
+ public static final String KEY_ALBUM_ID = "album_id";
+
+ @VisibleForTesting
+ public static final String IMAGE_FILE_EXTENSION = ".jpg";
+ @VisibleForTesting
+ public static final String VIDEO_FILE_EXTENSION = ".mp4";
+
+ private static final String WHERE_ID = KEY_ID + " = ?";
+ private static final String WHERE_LOCAL_ID = KEY_LOCAL_ID + " = ?";
+ private static final String WHERE_CLOUD_ID = KEY_CLOUD_ID + " = ?";
+ private static final String WHERE_NULL_CLOUD_ID = KEY_CLOUD_ID + " IS NULL";
+ private static final String WHERE_NOT_NULL_CLOUD_ID = KEY_CLOUD_ID + " IS NOT NULL";
+ private static final String WHERE_NOT_NULL_LOCAL_ID = KEY_LOCAL_ID + " IS NOT NULL";
+ private static final String WHERE_IS_VISIBLE = KEY_IS_VISIBLE + " = 1";
+ private static final String WHERE_MIME_TYPE = KEY_MIME_TYPE + " LIKE ? ";
+ private static final String WHERE_IS_FAVORITE = KEY_IS_FAVORITE + " = 1";
+ private static final String WHERE_SIZE_BYTES = KEY_SIZE_BYTES + " <= ?";
+ private static final String WHERE_DATE_TAKEN_MS_AFTER =
+ String.format("%s > ? OR (%s = ? AND %s > ?)",
+ KEY_DATE_TAKEN_MS, KEY_DATE_TAKEN_MS, KEY_ID);
+ private static final String WHERE_DATE_TAKEN_MS_BEFORE =
+ String.format("%s < ? OR (%s = ? AND %s < ?)",
+ KEY_DATE_TAKEN_MS, KEY_DATE_TAKEN_MS, KEY_ID);
+ private static final String WHERE_ALBUM_ID = KEY_ALBUM_ID + " = ?";
+
+ private static final String[] PROJECTION_ALBUM_DB = new String[] {
+ "COUNT(" + KEY_ID + ") AS " + CloudMediaProviderContract.AlbumColumns.MEDIA_COUNT,
+ "MAX(" + KEY_DATE_TAKEN_MS + ") AS "
+ + CloudMediaProviderContract.AlbumColumns.DATE_TAKEN_MILLIS,
+ String.format("IFNULL(%s, %s) AS %s", KEY_CLOUD_ID,
+ KEY_LOCAL_ID, CloudMediaProviderContract.AlbumColumns.MEDIA_COVER_ID)
+ };
+
+ // Matches all media including cloud+local, cloud-only and local-only
+ private static final SQLiteQueryBuilder QB_MATCH_ALL = createMediaQueryBuilder();
+ // Matches media with id
+ private static final SQLiteQueryBuilder QB_MATCH_ID = createIdMediaQueryBuilder();
+ // Matches media with local_id including cloud+local and local-only
+ private static final SQLiteQueryBuilder QB_MATCH_LOCAL = createLocalMediaQueryBuilder();
+ // Matches cloud media including cloud+local and cloud-only
+ private static final SQLiteQueryBuilder QB_MATCH_CLOUD = createCloudMediaQueryBuilder();
+ // Matches all visible media including cloud+local, cloud-only and local-only
+ private static final SQLiteQueryBuilder QB_MATCH_VISIBLE = createVisibleMediaQueryBuilder();
+ // Matches visible media with local_id including cloud+local and local-only
+ private static final SQLiteQueryBuilder QB_MATCH_VISIBLE_LOCAL =
+ createVisibleLocalMediaQueryBuilder();
+ // Matches stricly local-only media
+ private static final SQLiteQueryBuilder QB_MATCH_LOCAL_ONLY =
+ createLocalOnlyMediaQueryBuilder();
+
+ private static final ContentValues CONTENT_VALUE_VISIBLE = new ContentValues();
+ private static final ContentValues CONTENT_VALUE_HIDDEN = new ContentValues();
+
+ static {
+ CONTENT_VALUE_VISIBLE.put(KEY_IS_VISIBLE, 1);
+ CONTENT_VALUE_HIDDEN.putNull(KEY_IS_VISIBLE);
+ }
+
+ /**
+ * Sets the cloud provider to be returned after querying the picker db
+ * If null, cloud media will be excluded from all queries.
+ */
+ public void setCloudProvider(String authority) {
+ synchronized (mLock) {
+ mCloudProvider = authority;
+ }
+ }
+
+ /**
+ * Returns the cloud provider that will be returned after querying the picker db
+ */
+ @VisibleForTesting
+ public String getCloudProvider() {
+ synchronized (mLock) {
+ return mCloudProvider;
+ }
+ }
+
+ public String getLocalProvider() {
+ return mLocalProvider;
+ }
+
+ /**
+ * Returns {@link DbWriteOperation} to add media belonging to {@code authority} into the picker
+ * db.
+ */
+ public DbWriteOperation beginAddMediaOperation(String authority) {
+ return new AddMediaOperation(mDatabase, isLocal(authority));
+ }
+
+ /**
+ * Returns {@link DbWriteOperation} to add album_media belonging to {@code authority}
+ * into the picker db.
+ */
+ public DbWriteOperation beginAddAlbumMediaOperation(String authority, String albumId) {
+ return new AddAlbumMediaOperation(mDatabase, isLocal(authority), albumId);
+ }
+
+ /**
+ * Returns {@link DbWriteOperation} to remove media belonging to {@code authority} from the
+ * picker db.
+ */
+ public DbWriteOperation beginRemoveMediaOperation(String authority) {
+ return new RemoveMediaOperation(mDatabase, isLocal(authority));
+ }
+
+ /**
+ * Returns {@link DbWriteOperation} to clear local media or all cloud media from the picker
+ * db.
+ *
+ * @param authority to determine whether local or cloud media should be cleared
+ */
+ public DbWriteOperation beginResetMediaOperation(String authority) {
+ return new ResetMediaOperation(mDatabase, isLocal(authority));
+ }
+
+ /**
+ * Returns {@link DbWriteOperation} to clear album media for a given albumId from the picker
+ * db.
+ *
+ * @param authority to determine whether local or cloud media should be cleared
+ */
+ public DbWriteOperation beginResetAlbumMediaOperation(String authority, String albumId) {
+ return new ResetAlbumOperation(mDatabase, isLocal(authority), albumId);
+ }
+
+ /**
+ * Represents an atomic write operation to the picker database.
+ *
+ * <p>This class is not thread-safe and is meant to be used within a single thread only.
+ */
+ public static abstract class DbWriteOperation implements AutoCloseable {
+
+ private final SQLiteDatabase mDatabase;
+ private final boolean mIsLocal;
+ private final String mAlbumId;
+
+ private boolean mIsSuccess = false;
+
+ // Needed for Album Media Write operations.
+ private DbWriteOperation(SQLiteDatabase database, boolean isLocal) {
+ this(database, isLocal, "");
+ }
+
+ // Needed for Album Media Write operations.
+ private DbWriteOperation(SQLiteDatabase database, boolean isLocal, String albumId) {
+ mDatabase = database;
+ mIsLocal = isLocal;
+ mAlbumId = albumId;
+ mDatabase.beginTransaction();
+ }
+
+ /*
+ * Execute the write operation.
+ *
+ * @param cursor containing items to add/remove
+ * @return {@link WriteResult} indicating success/failure and the number of {@code cursor}
+ * items that were inserted/updated/deleted in the picker db
+ * @throws {@link IllegalStateException} if no DB transaction is active
+ */
+ public int execute(@Nullable Cursor cursor) {
+ if (!mDatabase.inTransaction()) {
+ throw new IllegalStateException("No ongoing DB transaction.");
+ }
+ return executeInternal(cursor);
+ }
+
+ public void setSuccess() {
+ mIsSuccess = true;
+ }
+
+ @Override
+ public void close() {
+ if (mDatabase.inTransaction()) {
+ if (mIsSuccess) {
+ mDatabase.setTransactionSuccessful();
+ } else {
+ Log.w(TAG, "DB write transaction failed.");
+ }
+ mDatabase.endTransaction();
+ } else {
+ throw new IllegalStateException("close() has already been called previously.");
+ }
+ }
+
+ abstract int executeInternal(@Nullable Cursor cursor);
+
+ SQLiteDatabase getDatabase() {
+ return mDatabase;
+ }
+
+ boolean isLocal() {
+ return mIsLocal;
+ }
+
+ String albumId() {
+ return mAlbumId;
+ }
+
+ int updateMedia(SQLiteQueryBuilder qb, ContentValues values,
+ String[] selectionArgs) {
+ try {
+ if (qb.update(mDatabase, values, /* selection */ null, selectionArgs) > 0) {
+ return SUCCESS;
+ } else {
+ Log.d(TAG, "Failed to update picker db media. ContentValues: " + values);
+ return FAIL;
+ }
+ } catch (SQLiteConstraintException e) {
+ Log.d(TAG, "Failed to update picker db media. ContentValues: " + values, e);
+ return RETRY;
+ }
+ }
+
+ String querySingleMedia(SQLiteQueryBuilder qb, String[] projection,
+ String[] selectionArgs, int columnIndex) {
+ try (Cursor cursor = qb.query(mDatabase, projection, /* selection */ null,
+ selectionArgs, /* groupBy */ null, /* having */ null,
+ /* orderBy */ null)) {
+ if (cursor.moveToFirst()) {
+ return cursor.getString(columnIndex);
+ }
+ }
+
+ return null;
+ }
+ }
+
+ private static final class AddMediaOperation extends DbWriteOperation {
+
+ private AddMediaOperation(SQLiteDatabase database, boolean isLocal) {
+ super(database, isLocal);
+ }
+
+ @Override
+ int executeInternal(@Nullable Cursor cursor) {
+ final boolean isLocal = isLocal();
+ final SQLiteQueryBuilder qb = isLocal ? QB_MATCH_LOCAL_ONLY : QB_MATCH_CLOUD;
+ int counter = 0;
+
+ while (cursor.moveToNext()) {
+ ContentValues values = cursorToContentValue(cursor, isLocal);
+
+ String[] upsertArgs = {values.getAsString(isLocal ?
+ KEY_LOCAL_ID : KEY_CLOUD_ID)};
+ if (upsertMedia(qb, values, upsertArgs) == SUCCESS) {
+ counter++;
+ continue;
+ }
+
+ // Because we want to prioritize visible local media over visible cloud media,
+ // we do the following if the upsert above failed
+ if (isLocal) {
+ // For local syncs, we attempt hiding the visible cloud media
+ String cloudId = getVisibleCloudIdFromDb(values.getAsString(KEY_LOCAL_ID));
+ demoteCloudMediaToHidden(cloudId);
+ } else {
+ // For cloud syncs, we prepare an upsert as hidden cloud media
+ values.putNull(KEY_IS_VISIBLE);
+ }
+
+ // Now attempt upsert again, this should succeed
+ if (upsertMedia(qb, values, upsertArgs) == SUCCESS) {
+ counter++;
+ }
+ }
+ return counter;
+ }
+
+ private int insertMedia(ContentValues values) {
+ try {
+ if (QB_MATCH_ALL.insert(getDatabase(), values) > 0) {
+ return SUCCESS;
+ } else {
+ Log.d(TAG, "Failed to insert picker db media. ContentValues: " + values);
+ return FAIL;
+ }
+ } catch (SQLiteConstraintException e) {
+ Log.d(TAG, "Failed to insert picker db media. ContentValues: " + values, e);
+ return RETRY;
+ }
+ }
+
+ private int upsertMedia(SQLiteQueryBuilder qb,
+ ContentValues values, String[] selectionArgs) {
+ int res = insertMedia(values);
+ if (res == RETRY) {
+ // Attempt equivalent of CONFLICT_REPLACE resolution
+ Log.d(TAG, "Retrying failed insert as update. ContentValues: " + values);
+ res = updateMedia(qb, values, selectionArgs);
+ }
+
+ return res;
+ }
+
+ private void demoteCloudMediaToHidden(@Nullable String cloudId) {
+ if (cloudId == null) {
+ return;
+ }
+
+ final String[] updateArgs = new String[] {cloudId};
+ if (updateMedia(QB_MATCH_CLOUD, CONTENT_VALUE_HIDDEN, updateArgs) == SUCCESS) {
+ Log.d(TAG, "Demoted picker db media item to hidden. CloudId: " + cloudId);
+ }
+ }
+
+ private String getVisibleCloudIdFromDb(String localId) {
+ final String[] cloudIdProjection = new String[] {KEY_CLOUD_ID};
+ final String[] queryArgs = new String[] {localId};
+ return querySingleMedia(QB_MATCH_VISIBLE_LOCAL, cloudIdProjection, queryArgs,
+ /* columnIndex */ 0);
+ }
+ }
+
+ private static final class RemoveMediaOperation extends DbWriteOperation {
+
+ private RemoveMediaOperation(SQLiteDatabase database, boolean isLocal) {
+ super(database, isLocal);
+ }
+
+ @Override
+ int executeInternal(@Nullable Cursor cursor) {
+ final boolean isLocal = isLocal();
+ final SQLiteQueryBuilder qb = isLocal ? QB_MATCH_LOCAL_ONLY : QB_MATCH_CLOUD;
+
+ int counter = 0;
+
+ while (cursor.moveToNext()) {
+ // Need to fetch the local_id before delete because for cloud items
+ // we need a db query to fetch the local_id matching the id received from
+ // cursor (cloud_id).
+ final String localId = getLocalIdFromCursorOrDb(cursor, isLocal);
+
+ // Delete cloud/local row
+ final int idIndex = cursor.getColumnIndex(
+ CloudMediaProviderContract.MediaColumns.ID);
+ final String[] deleteArgs = {cursor.getString(idIndex)};
+ if (qb.delete(getDatabase(), /* selection */ null, deleteArgs) > 0) {
+ counter++;
+ }
+
+ promoteCloudMediaToVisible(localId);
+ }
+
+ return counter;
+ }
+
+ private void promoteCloudMediaToVisible(@Nullable String localId) {
+ if (localId == null) {
+ return;
+ }
+
+ final String[] idProjection = new String[] {KEY_ID};
+ final String[] queryArgs = {localId};
+ // First query for an exact row id matching the criteria for promotion so that we don't
+ // attempt promoting multiple hidden cloud rows matching the |localId|
+ final String id = querySingleMedia(QB_MATCH_LOCAL, idProjection, queryArgs,
+ /* columnIndex */ 0);
+ if (id == null) {
+ Log.w(TAG, "Unable to promote cloud media with localId: " + localId);
+ return;
+ }
+
+ final String[] updateArgs = {id};
+ if (updateMedia(QB_MATCH_ID, CONTENT_VALUE_VISIBLE, updateArgs) == SUCCESS) {
+ Log.d(TAG, "Promoted picker db media item to visible. LocalId: " + localId);
+ }
+ }
+
+ private String getLocalIdFromCursorOrDb(Cursor cursor, boolean isLocal) {
+ final String id = cursor.getString(0);
+
+ if (isLocal) {
+ // For local, id in cursor is already local_id
+ return id;
+ } else {
+ // For cloud, we need to query db with cloud_id from cursor to fetch local_id
+ final String[] localIdProjection = new String[] {KEY_LOCAL_ID};
+ final String[] queryArgs = new String[] {id};
+ return querySingleMedia(QB_MATCH_CLOUD, localIdProjection, queryArgs,
+ /* columnIndex */ 0);
+ }
+ }
+ }
+
+ private static final class ResetMediaOperation extends DbWriteOperation {
+
+ private ResetMediaOperation(SQLiteDatabase database, boolean isLocal) {
+ super(database, isLocal);
+ }
+
+ @Override
+ int executeInternal(@Nullable Cursor unused) {
+ final boolean isLocal = isLocal();
+ final SQLiteQueryBuilder qb = createMediaQueryBuilder();
+
+ if (isLocal) {
+ qb.appendWhereStandalone(WHERE_NULL_CLOUD_ID);
+ } else {
+ qb.appendWhereStandalone(WHERE_NOT_NULL_CLOUD_ID);
+ }
+
+ SQLiteDatabase database = getDatabase();
+ int counter = qb.delete(database, /* selection */ null, /* selectionArgs */ null);
+
+ if (isLocal) {
+ // If we reset local media, we need to promote cloud media items
+ // Ignore conflicts in case we have multiple cloud_ids mapped to the
+ // same local_id. Promoting either is fine.
+ database.updateWithOnConflict(TABLE_MEDIA, CONTENT_VALUE_VISIBLE, /* where */ null,
+ /* whereClause */ null, SQLiteDatabase.CONFLICT_IGNORE);
+ }
+
+ return counter;
+ }
+ }
+
+ /** Filter for {@link #queryMedia} to modify returned results */
+ public static class QueryFilter {
+ private final int mLimit;
+ private final long mDateTakenBeforeMs;
+ private final long mDateTakenAfterMs;
+ private final long mId;
+ private final String mAlbumId;
+ private final long mSizeBytes;
+ private final String mMimeType;
+ 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,
+ boolean isVideo) {
+ this.mLimit = limit;
+ this.mDateTakenBeforeMs = dateTakenBeforeMs;
+ this.mDateTakenAfterMs = dateTakenAfterMs;
+ this.mId = id;
+ this.mAlbumId = albumId;
+ this.mSizeBytes = sizeBytes;
+ this.mMimeType = mimeType;
+ this.mIsFavorite = isFavorite;
+ this.mIsVideo = isVideo;
+ }
+ }
+
+ /** Builder for {@link Query} filter. */
+ public static class QueryFilterBuilder {
+ public static final long LONG_DEFAULT = -1;
+ public static final String STRING_DEFAULT = null;
+ public static final boolean BOOLEAN_DEFAULT = false;
+
+ public static final int LIMIT_DEFAULT = 1000;
+
+ private final int limit;
+ private long dateTakenBeforeMs = LONG_DEFAULT;
+ private long dateTakenAfterMs = LONG_DEFAULT;
+ private long id = LONG_DEFAULT;
+ private String albumId = STRING_DEFAULT;
+ private long sizeBytes = LONG_DEFAULT;
+ private String mimeType = STRING_DEFAULT;
+ private boolean isFavorite = BOOLEAN_DEFAULT;
+ private boolean mIsVideo = BOOLEAN_DEFAULT;
+
+ public QueryFilterBuilder(int limit) {
+ this.limit = limit;
+ }
+
+ public QueryFilterBuilder setDateTakenBeforeMs(long dateTakenBeforeMs) {
+ this.dateTakenBeforeMs = dateTakenBeforeMs;
+ return this;
+ }
+
+ public QueryFilterBuilder setDateTakenAfterMs(long dateTakenAfterMs) {
+ this.dateTakenAfterMs = dateTakenAfterMs;
+ return this;
+ }
+
+ /**
+ * The {@code id} helps break ties with db rows having the same {@code dateTakenAfterMs} or
+ * {@code dateTakenBeforeMs}.
+ *
+ * If {@code dateTakenAfterMs} is specified, all returned items are either strictly more
+ * recent than {@code dateTakenAfterMs} or have a picker db id strictly greater than
+ * {@code id} for ties.
+ *
+ * If {@code dateTakenBeforeMs} is specified, all returned items are either strictly older
+ * than {@code dateTakenBeforeMs} or have a picker db id strictly less than {@code id}
+ * for ties.
+ */
+ public QueryFilterBuilder setId(long id) {
+ this.id = id;
+ return this;
+ }
+ public QueryFilterBuilder setAlbumId(String albumId) {
+ this.albumId = albumId;
+ return this;
+ }
+
+ public QueryFilterBuilder setSizeBytes(long sizeBytes) {
+ this.sizeBytes = sizeBytes;
+ return this;
+ }
+
+ public QueryFilterBuilder setMimeType(String mimeType) {
+ this.mimeType = mimeType;
+ return this;
+ }
+
+ /**
+ * If {@code isFavorite} is {@code true}, the {@link QueryFilter} returns only
+ * favorited items, however, if it is {@code false}, it returns all items including
+ * favorited and non-favorited items.
+ */
+ public QueryFilterBuilder setIsFavorite(boolean isFavorite) {
+ this.isFavorite = isFavorite;
+ return this;
+ }
+
+ /**
+ * If {@code isVideo} is {@code true}, the {@link QueryFilter} returns only
+ * video items, however, if it is {@code false}, it returns all items including
+ * video and non-video items.
+ */
+ public QueryFilterBuilder setIsVideo(boolean isVideo) {
+ this.mIsVideo = isVideo;
+ return this;
+ }
+
+ public QueryFilter build() {
+ return new QueryFilter(limit, dateTakenBeforeMs, dateTakenAfterMs, id, albumId,
+ sizeBytes, mimeType, isFavorite, mIsVideo);
+ }
+ }
+
+ /**
+ * Returns sorted and deduped cloud and local media items from the picker db.
+ *
+ * Returns a {@link Cursor} containing picker db media rows with columns as
+ * {@link CloudMediaProviderContract#MediaColumns}.
+ *
+ * The result is sorted in reverse chronological order, i.e. newest first, up to a maximum of
+ * {@code limit}. They can also be filtered with {@code query}.
+ */
+ public Cursor queryMediaForUi(QueryFilter query) {
+ final SQLiteQueryBuilder qb = createVisibleMediaQueryBuilder();
+ final String[] selectionArgs = buildSelectionArgs(qb, query);
+
+ final String cloudProvider;
+ synchronized (mLock) {
+ cloudProvider = mCloudProvider;
+ }
+
+ return queryMediaForUi(qb, selectionArgs, query.mLimit, TABLE_MEDIA, cloudProvider);
+ }
+
+ /**
+ * Returns sorted cloud or local media items from the picker db for a given album (either cloud
+ * or local).
+ *
+ * Returns a {@link Cursor} containing picker db media rows with columns as
+ * {@link CloudMediaProviderContract#MediaColumns} except for is_favorites column because that
+ * column is only used for fetching the Favorites album.
+ *
+ * The result is sorted in reverse chronological order, i.e. newest first, up to a maximum of
+ * {@code limit}. They can also be filtered with {@code query}.
+ */
+ public Cursor queryAlbumMediaForUi(QueryFilter query, String authority) {
+ final SQLiteQueryBuilder qb = createAlbumMediaQueryBuilder(isLocal(authority));
+ final String[] selectionArgs = buildSelectionArgs(qb, query);
+
+ return queryMediaForUi(qb, selectionArgs, query.mLimit, TABLE_ALBUM_MEDIA, authority);
+ }
+
+ /**
+ * Returns an individual cloud or local item from the picker db matching {@code authority} and
+ * {@code mediaId}.
+ *
+ * Returns a {@link Cursor} containing picker db media rows with columns as {@code projection},
+ * a subset of {@link PickerMediaColumns}.
+ */
+ public Cursor queryMediaIdForApps(String authority, String mediaId,
+ @NonNull String[] projection) {
+ final String[] selectionArgs = new String[] { mediaId };
+ final SQLiteQueryBuilder qb = createVisibleMediaQueryBuilder();
+ if (isLocal(authority)) {
+ qb.appendWhereStandalone(WHERE_LOCAL_ID);
+ } else {
+ qb.appendWhereStandalone(WHERE_CLOUD_ID);
+ }
+
+ synchronized (mLock) {
+ if (authority.equals(mLocalProvider) || authority.equals(mCloudProvider)) {
+ return qb.query(mDatabase, getMediaStoreProjectionLocked(authority, mediaId,
+ projection),
+ /* selection */ null, selectionArgs, /* groupBy */ null, /* having */ null,
+ /* orderBy */ null, /* limitStr */ null);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns empty {@link Cursor} if there are no items matching merged album constraints {@code
+ * query}
+ */
+ public Cursor getMergedAlbums(QueryFilter query) {
+ final MatrixCursor c = new MatrixCursor(AlbumColumns.ALL_PROJECTION);
+ List<String> mergedAlbums = List.of(ALBUM_ID_FAVORITES, ALBUM_ID_VIDEOS);
+ for (String albumId : mergedAlbums) {
+ List<String> selectionArgs = new ArrayList<>();
+ final SQLiteQueryBuilder qb = createVisibleMediaQueryBuilder();
+ if (albumId.equals(ALBUM_ID_FAVORITES)) {
+ qb.appendWhereStandalone(WHERE_IS_FAVORITE);
+ } else if (albumId.equals(ALBUM_ID_VIDEOS)) {
+ qb.appendWhereStandalone(WHERE_MIME_TYPE);
+ selectionArgs.add("video/%");
+ }
+ if (query.mMimeType != null) {
+ qb.appendWhereStandalone(WHERE_MIME_TYPE);
+ selectionArgs.add(query.mMimeType.replace('*', '%'));
+ }
+
+ Cursor cursor = qb.query(mDatabase, PROJECTION_ALBUM_DB, /* selection */ null,
+ selectionArgs.toArray(new String[0]), /* groupBy */ null, /* having */ null,
+ /* orderBy */ null, /* limit */ null);
+
+ if (cursor == null || !cursor.moveToFirst()) {
+ continue;
+ }
+
+ long count = getCursorLong(cursor, CloudMediaProviderContract.AlbumColumns.MEDIA_COUNT);
+ if (count == 0) {
+ continue;
+ }
+
+ final String[] projectionValue = new String[]{
+ /* albumId */ albumId,
+ getCursorString(cursor, AlbumColumns.DATE_TAKEN_MILLIS),
+ /* displayName */ albumId,
+ getCursorString(cursor, AlbumColumns.MEDIA_COVER_ID),
+ String.valueOf(count),
+ mLocalProvider,
+ };
+ c.addRow(projectionValue);
+ }
+ return c;
+ }
+
+ private boolean isLocal(String authority) {
+ return mLocalProvider.equals(authority);
+ }
+
+ private Cursor queryMediaForUi(SQLiteQueryBuilder qb, String[] selectionArgs,
+ int limit, String tableName, String authority) {
+ // Use the <table>.<column> form to order _id to avoid ordering against the projection '_id'
+ final String orderBy = getOrderClause(tableName);
+ final String limitStr = String.valueOf(limit);
+
+ // Hold lock while checking the cloud provider and querying so that cursor extras containing
+ // the cloud provider is consistent with the cursor results and doesn't race with
+ // #setCloudProvider
+ synchronized (mLock) {
+ if (mCloudProvider == null || !Objects.equals(mCloudProvider, authority)) {
+ // If cloud provider is null or has changed from what we received from the UI,
+ // skip all cloud items in the picker db
+ qb.appendWhereStandalone(WHERE_NULL_CLOUD_ID);
+ }
+
+ return qb.query(mDatabase, getCloudMediaProjectionLocked(), /* selection */ null,
+ selectionArgs, /* groupBy */ null, /* having */ null, orderBy, limitStr);
+ }
+ }
+
+ private static String getOrderClause(String tableName) {
+ return "date_taken_ms DESC," + tableName + "._id DESC";
+ }
+
+ private String[] getCloudMediaProjectionLocked() {
+ return new String[] {
+ getProjectionAuthorityLocked(),
+ getProjectionDataLocked(MediaColumns.DATA),
+ getProjectionId(MediaColumns.ID),
+ getProjectionSimple(KEY_DATE_TAKEN_MS, MediaColumns.DATE_TAKEN_MILLIS),
+ getProjectionSimple(KEY_SYNC_GENERATION, MediaColumns.SYNC_GENERATION),
+ getProjectionSimple(KEY_SIZE_BYTES, MediaColumns.SIZE_BYTES),
+ getProjectionSimple(KEY_DURATION_MS, MediaColumns.DURATION_MILLIS),
+ getProjectionSimple(KEY_MIME_TYPE, MediaColumns.MIME_TYPE),
+ getProjectionSimple(KEY_STANDARD_MIME_TYPE_EXTENSION,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION),
+ };
+ }
+
+ private String[] getMediaStoreProjectionLocked(String authority, String mediaId,
+ String[] columns) {
+ final String[] projection = new String[columns.length];
+
+ for (int i = 0; i < projection.length; i++) {
+ switch (columns[i]) {
+ case PickerMediaColumns.DATA:
+ projection[i] = getProjectionDataLocked(PickerMediaColumns.DATA);
+ break;
+ case PickerMediaColumns.DISPLAY_NAME:
+ projection[i] = getProjectionSimple(getDisplayNameSql(),
+ PickerMediaColumns.DISPLAY_NAME);
+ break;
+ case PickerMediaColumns.MIME_TYPE:
+ projection[i] = getProjectionSimple(KEY_MIME_TYPE,
+ PickerMediaColumns.MIME_TYPE);
+ break;
+ case PickerMediaColumns.DATE_TAKEN:
+ projection[i] = getProjectionSimple(KEY_DATE_TAKEN_MS,
+ PickerMediaColumns.DATE_TAKEN);
+ break;
+ case PickerMediaColumns.SIZE:
+ projection[i] = getProjectionSimple(KEY_SIZE_BYTES, PickerMediaColumns.SIZE);
+ break;
+ case PickerMediaColumns.DURATION_MILLIS:
+ projection[i] = getProjectionSimple(KEY_DURATION_MS,
+ PickerMediaColumns.DURATION_MILLIS);
+ break;
+ default:
+ Uri uri = getMediaUri(authority).buildUpon().appendPath(mediaId).build();
+ throw new IllegalArgumentException("Unexpected picker URI projection. Uri:"
+ + uri + ". Column:" + columns[i]);
+ }
+ }
+
+ return projection;
+ }
+
+ private String getProjectionAuthorityLocked() {
+ // Note that we prefer cloud_id over local_id here. It's important to remember that this
+ // logic is for computing the projection and doesn't affect the filtering of results which
+ // has already been done and ensures that only is_visible=true items are returned.
+ // Here, we need to distinguish between cloud+local and local-only items to determine the
+ // correct authority. Checking whether cloud_id IS NULL distinguishes the former from the
+ // latter.
+ return String.format("CASE WHEN %s IS NULL THEN '%s' ELSE '%s' END AS %s",
+ KEY_CLOUD_ID, mLocalProvider, mCloudProvider, MediaColumns.AUTHORITY);
+ }
+
+ private String getProjectionDataLocked(String asColumn) {
+ // _data format:
+ // /sdcard/.transforms/synthetic/picker/<user-id>/<authority>/media/<display-name>
+ // See PickerUriResolver#getMediaUri
+ final String authority = String.format("CASE WHEN %s IS NULL THEN '%s' ELSE '%s' END",
+ KEY_CLOUD_ID, mLocalProvider, mCloudProvider);
+ final String fullPath = "'" + PICKER_PATH + "/'"
+ + "||" + "'" + MediaStore.MY_USER_ID + "/'"
+ + "||" + authority
+ + "||" + "'/" + CloudMediaProviderContract.URI_PATH_MEDIA + "/'"
+ + "||" + getDisplayNameSql();
+ return String.format("%s AS %s", fullPath, asColumn);
+ }
+
+ private String getProjectionId(String asColumn) {
+ // We prefer cloud_id first and it only matters for cloud+local items. For those, the row
+ // will already be associated with a cloud authority, see #getProjectionAuthorityLocked.
+ // Note that hidden cloud+local items will not be returned in the query, so there's no
+ // concern of preferring the cloud_id in a cloud+local item over the local_id in a
+ // local-only item.
+ return String.format("IFNULL(%s, %s) AS %s", KEY_CLOUD_ID, KEY_LOCAL_ID, asColumn);
+ }
+
+ private static String getProjectionSimple(String dbColumn, String column) {
+ return String.format("%s AS %s", dbColumn, column);
+ }
+
+ private String getDisplayNameSql() {
+ // _display_name format:
+ // <media-id>.<file-extension>
+ // See comment in #getProjectionAuthorityLocked for why cloud_id is preferred over local_id
+ final String mediaId = String.format("IFNULL(%s, %s)", KEY_CLOUD_ID, KEY_LOCAL_ID);
+ // TODO(b/195009139): Add .gif fileextension support
+ final String fileExtension =
+ String.format("CASE WHEN %s LIKE 'image/%%' THEN '%s' ELSE '%s' END",
+ KEY_MIME_TYPE, IMAGE_FILE_EXTENSION, VIDEO_FILE_EXTENSION);
+
+ return mediaId + "||" + fileExtension;
+ }
+
+ private static ContentValues cursorToContentValue(Cursor cursor, boolean isLocal) {
+ return cursorToContentValue(cursor, isLocal, "");
+ }
+
+ private static ContentValues cursorToContentValue(Cursor cursor, boolean isLocal,
+ String albumId) {
+ final ContentValues values = new ContentValues();
+ if(TextUtils.isEmpty(albumId)) {
+ values.put(KEY_IS_VISIBLE, 1);
+ }
+ else {
+ values.put(KEY_ALBUM_ID, albumId);
+ }
+
+ final int count = cursor.getColumnCount();
+ for (int index = 0; index < count; index++) {
+ String key = cursor.getColumnName(index);
+ switch (key) {
+ case CloudMediaProviderContract.MediaColumns.ID:
+ if (isLocal) {
+ values.put(KEY_LOCAL_ID, cursor.getString(index));
+ } else {
+ values.put(KEY_CLOUD_ID, cursor.getString(index));
+ }
+ break;
+ case CloudMediaProviderContract.MediaColumns.MEDIA_STORE_URI:
+ String uriString = cursor.getString(index);
+ if (uriString != null) {
+ Uri uri = Uri.parse(uriString);
+ values.put(KEY_LOCAL_ID, ContentUris.parseId(uri));
+ }
+ break;
+ case CloudMediaProviderContract.MediaColumns.DATE_TAKEN_MILLIS:
+ values.put(KEY_DATE_TAKEN_MS, cursor.getLong(index));
+ break;
+ case CloudMediaProviderContract.MediaColumns.SYNC_GENERATION:
+ values.put(KEY_SYNC_GENERATION, cursor.getLong(index));
+ break;
+ case CloudMediaProviderContract.MediaColumns.SIZE_BYTES:
+ values.put(KEY_SIZE_BYTES, cursor.getLong(index));
+ break;
+ case CloudMediaProviderContract.MediaColumns.MIME_TYPE:
+ values.put(KEY_MIME_TYPE, cursor.getString(index));
+ break;
+ case CloudMediaProviderContract.MediaColumns.STANDARD_MIME_TYPE_EXTENSION:
+ int standardMimeTypeExtension = cursor.getInt(index);
+ if (isValidStandardMimeTypeExtension(standardMimeTypeExtension)) {
+ values.put(KEY_STANDARD_MIME_TYPE_EXTENSION, standardMimeTypeExtension);
+ } else {
+ throw new IllegalArgumentException("Invalid standard mime type extension");
+ }
+ break;
+ case CloudMediaProviderContract.MediaColumns.DURATION_MILLIS:
+ values.put(KEY_DURATION_MS, cursor.getLong(index));
+ break;
+ case CloudMediaProviderContract.MediaColumns.IS_FAVORITE:
+ if(TextUtils.isEmpty(albumId)) {
+ values.put(KEY_IS_FAVORITE, cursor.getInt(index));
+ }
+ break;
+ default:
+ Log.w(TAG, "Unexpected cursor key: " + key);
+ }
+ }
+
+ return values;
+ }
+
+ private static boolean isValidStandardMimeTypeExtension(int standardMimeTypeExtension) {
+ switch (standardMimeTypeExtension) {
+ case CloudMediaProviderContract.MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE:
+ case CloudMediaProviderContract.MediaColumns.STANDARD_MIME_TYPE_EXTENSION_GIF:
+ case CloudMediaProviderContract.MediaColumns.STANDARD_MIME_TYPE_EXTENSION_MOTION_PHOTO:
+ case CloudMediaProviderContract.MediaColumns.STANDARD_MIME_TYPE_EXTENSION_ANIMATED_WEBP:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private static String[] buildSelectionArgs(SQLiteQueryBuilder qb, QueryFilter query) {
+ List<String> selectArgs = new ArrayList<>();
+
+ if (query.mId >= 0) {
+ if (query.mDateTakenAfterMs >= 0) {
+ qb.appendWhereStandalone(WHERE_DATE_TAKEN_MS_AFTER);
+ // Add date args twice because the sql statement evaluates date twice
+ selectArgs.add(String.valueOf(query.mDateTakenAfterMs));
+ selectArgs.add(String.valueOf(query.mDateTakenAfterMs));
+ } else {
+ qb.appendWhereStandalone(WHERE_DATE_TAKEN_MS_BEFORE);
+ // Add date args twice because the sql statement evaluates date twice
+ selectArgs.add(String.valueOf(query.mDateTakenBeforeMs));
+ selectArgs.add(String.valueOf(query.mDateTakenBeforeMs));
+ }
+ selectArgs.add(String.valueOf(query.mId));
+ }
+
+ if (query.mSizeBytes >= 0) {
+ qb.appendWhereStandalone(WHERE_SIZE_BYTES);
+ selectArgs.add(String.valueOf(query.mSizeBytes));
+ }
+
+ if (query.mMimeType != null) {
+ qb.appendWhereStandalone(WHERE_MIME_TYPE);
+ selectArgs.add(replaceMatchAnyChar(query.mMimeType));
+ }
+ if (query.mIsVideo) {
+ qb.appendWhereStandalone(WHERE_MIME_TYPE);
+ selectArgs.add(VIDEO_MIME_TYPES);
+ } else if (query.mIsFavorite) {
+ qb.appendWhereStandalone(WHERE_IS_FAVORITE);
+ } else if (!TextUtils.isEmpty(query.mAlbumId)) {
+ qb.appendWhereStandalone(WHERE_ALBUM_ID);
+ selectArgs.add(query.mAlbumId);
+ }
+
+ if (selectArgs.isEmpty()) {
+ return null;
+ }
+
+ return selectArgs.toArray(new String[selectArgs.size()]);
+ }
+
+ private static SQLiteQueryBuilder createMediaQueryBuilder() {
+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ qb.setTables(TABLE_MEDIA);
+
+ return qb;
+ }
+
+ private static SQLiteQueryBuilder createAlbumMediaQueryBuilder(boolean isLocal) {
+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ qb.setTables(TABLE_ALBUM_MEDIA);
+
+ if (isLocal) {
+ qb.appendWhereStandalone(WHERE_NOT_NULL_LOCAL_ID);
+ } else {
+ qb.appendWhereStandalone(WHERE_NOT_NULL_CLOUD_ID);
+ }
+
+ return qb;
+ }
+
+ private static SQLiteQueryBuilder createLocalOnlyMediaQueryBuilder() {
+ SQLiteQueryBuilder qb = createLocalMediaQueryBuilder();
+ qb.appendWhereStandalone(WHERE_NULL_CLOUD_ID);
+
+ return qb;
+ }
+
+ private static SQLiteQueryBuilder createLocalMediaQueryBuilder() {
+ SQLiteQueryBuilder qb = createMediaQueryBuilder();
+ qb.appendWhereStandalone(WHERE_LOCAL_ID);
+
+ return qb;
+ }
+
+ private static SQLiteQueryBuilder createCloudMediaQueryBuilder() {
+ SQLiteQueryBuilder qb = createMediaQueryBuilder();
+ qb.appendWhereStandalone(WHERE_CLOUD_ID);
+
+ return qb;
+ }
+
+ private static SQLiteQueryBuilder createIdMediaQueryBuilder() {
+ SQLiteQueryBuilder qb = createMediaQueryBuilder();
+ qb.appendWhereStandalone(WHERE_ID);
+
+ return qb;
+ }
+
+ private static SQLiteQueryBuilder createVisibleMediaQueryBuilder() {
+ SQLiteQueryBuilder qb = createMediaQueryBuilder();
+ qb.appendWhereStandalone(WHERE_IS_VISIBLE);
+
+ return qb;
+ }
+
+ private static SQLiteQueryBuilder createVisibleLocalMediaQueryBuilder() {
+ SQLiteQueryBuilder qb = createVisibleMediaQueryBuilder();
+ qb.appendWhereStandalone(WHERE_LOCAL_ID);
+
+ return qb;
+ }
+
+ private static final class ResetAlbumOperation extends DbWriteOperation {
+ /**
+ * Resets the given cloud or local album_media identified by {@code isLocal} and
+ * {@code albumId}. If {@code albumId} is null, resets all the respective cloud or
+ * local albums.
+ */
+ private ResetAlbumOperation(SQLiteDatabase database, boolean isLocal, String albumId) {
+ super(database, isLocal, albumId);
+ }
+
+ @Override
+ int executeInternal(@Nullable Cursor unused) {
+ final String albumId = albumId();
+ final boolean isLocal = isLocal();
+
+ final SQLiteQueryBuilder qb = createAlbumMediaQueryBuilder(isLocal);
+
+ String[] selectionArgs = null;
+ if(!TextUtils.isEmpty(albumId)) {
+ qb.appendWhereStandalone(WHERE_ALBUM_ID);
+ selectionArgs = new String[]{albumId};
+ }
+
+ return qb.delete(getDatabase(), /* selection */ null, /* selectionArgs */
+ selectionArgs);
+ }
+ }
+
+ private static final class AddAlbumMediaOperation extends DbWriteOperation {
+ private AddAlbumMediaOperation(SQLiteDatabase database, boolean isLocal, String albumId) {
+ super(database, isLocal, albumId);
+ if(TextUtils.isEmpty(albumId)) {
+ throw new IllegalArgumentException("Missing albumId.");
+ }
+ }
+
+ @Override
+ int executeInternal(@Nullable Cursor cursor) {
+ final boolean isLocal = isLocal();
+ final String albumId = albumId();
+ final SQLiteQueryBuilder qb = createAlbumMediaQueryBuilder(isLocal);
+ int counter = 0;
+
+ while (cursor.moveToNext()) {
+ ContentValues values = cursorToContentValue(cursor, isLocal, albumId);
+ try {
+ if (qb.insert(getDatabase(), values) > 0) {
+ counter++;
+ } else {
+ Log.d(TAG, "Failed to insert album_media. ContentValues: " + values);
+ }
+ } catch (SQLiteConstraintException e) {
+ Log.d(TAG, "Failed to insert album_media. ContentValues: " + values, e);
+ }
+ }
+
+ return counter;
+ }
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/data/PickerResult.java b/src/com/android/providers/media/photopicker/data/PickerResult.java
new file mode 100644
index 0000000..5207bad
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/data/PickerResult.java
@@ -0,0 +1,95 @@
+/*
+ * 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.data;
+
+import android.content.ClipData;
+import android.content.Intent;
+import android.net.Uri;
+
+import androidx.annotation.NonNull;
+
+import com.android.providers.media.PickerUriResolver;
+import com.android.providers.media.photopicker.data.model.Item;
+import com.android.providers.media.photopicker.data.model.UserId;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is responsible for returning result to the caller of the PhotoPicker.
+ */
+public class PickerResult {
+
+ /**
+ * @return {@code Intent} which contains Uri that has been granted access on.
+ */
+ @NonNull
+ public static Intent getPickerResponseIntent(boolean canSelectMultiple,
+ @NonNull List<Item> selectedItems) {
+ // 1. Get Picker Uris corresponding to the selected items
+ List<Uri> selectedUris = getPickerUrisForItems(selectedItems);
+
+ // 2. Grant read access to picker Uris and return
+ Intent intent = new Intent();
+ final int size = selectedUris.size();
+ if (size < 1) {
+ // TODO (b/168783994): check if this is ever possible. If yes, handle properly,
+ // if not, remove this if block.
+ return intent;
+ }
+ if (!canSelectMultiple) {
+ intent.setData(selectedUris.get(0));
+ }
+ // TODO (b/169737761): use correct mime types
+ String[] mimeTypes = new String[]{"image/*", "video/*"};
+ final ClipData clipData = new ClipData(null /* label */, mimeTypes,
+ new ClipData.Item(selectedUris.get(0)));
+ for (int i = 1; i < size; i++) {
+ clipData.addItem(new ClipData.Item(selectedUris.get(i)));
+ }
+ intent.setClipData(clipData);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
+
+ return intent;
+ }
+
+ @VisibleForTesting
+ static Uri getPickerUri(Uri uri) {
+ final String userInfo = uri.getUserInfo();
+ final String userId = userInfo == null ? UserId.CURRENT_USER.toString() : userInfo;
+ return PickerUriResolver.wrapProviderUri(uri, Integer.parseInt(userId));
+ }
+
+ /**
+ * Returns list of PhotoPicker Uris corresponding to each {@link Item}
+ *
+ * @param ItemList list of Item for which we return uri list.
+ */
+ @NonNull
+ private static List<Uri> getPickerUrisForItems(@NonNull List<Item> ItemList) {
+ List<Uri> uris = new ArrayList<>();
+ for (Item item : ItemList) {
+ uris.add(getPickerUri(item.getContentUri()));
+ }
+
+ return uris;
+ }
+
+}
diff --git a/src/com/android/providers/media/photopicker/data/Selection.java b/src/com/android/providers/media/photopicker/data/Selection.java
new file mode 100644
index 0000000..79692ba
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/data/Selection.java
@@ -0,0 +1,211 @@
+/*
+ * 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.data;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.MediaStore;
+
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+
+import com.android.providers.media.photopicker.data.model.Item;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A class that tracks Selection
+ */
+public class Selection {
+ // The list of selected items.
+ private Map<Uri, Item> mSelectedItems = new HashMap<>();
+ private MutableLiveData<Integer> mSelectedItemSize = new MutableLiveData<>();
+ // The list of selected items for preview. This needs to be saved separately so that if activity
+ // gets killed, we will still have deselected items for preview.
+ private List<Item> mSelectedItemsForPreview = new ArrayList<>();
+ private boolean mSelectMultiple = false;
+ private int mMaxSelectionLimit = 1;
+ // This is set to false when max selection limit is reached.
+ private boolean mIsSelectionAllowed = true;
+
+ /**
+ * @return {@link #mSelectedItems} - A {@link List} of selected {@link Item}
+ */
+ public List<Item> getSelectedItems() {
+ return Collections.unmodifiableList(new ArrayList<>(mSelectedItems.values()));
+ }
+
+ /**
+ * @return {@link LiveData} of count of selected items in {@link #mSelectedItems}
+ */
+ public LiveData<Integer> getSelectedItemCount() {
+ if (mSelectedItemSize.getValue() == null) {
+ mSelectedItemSize.setValue(mSelectedItems.size());
+ }
+ return mSelectedItemSize;
+ }
+
+ /**
+ * Add the selected {@code item} into {@link #mSelectedItems}.
+ */
+ public void addSelectedItem(Item item) {
+ mSelectedItems.put(item.getContentUri(), item);
+ mSelectedItemSize.postValue(mSelectedItems.size());
+ updateSelectionAllowed();
+ }
+
+ /**
+ * Clears {@link #mSelectedItems} and sets the selected item as given {@code item}
+ */
+ public void setSelectedItem(Item item) {
+ mSelectedItems.clear();
+ mSelectedItems.put(item.getContentUri(), item);
+ mSelectedItemSize.postValue(mSelectedItems.size());
+ updateSelectionAllowed();
+ }
+
+ /**
+ * Remove the {@code item} from the selected item list {@link #mSelectedItems}.
+ *
+ * @param item the item to be removed from the selected item list
+ */
+ public void removeSelectedItem(Item item) {
+ mSelectedItems.remove(item.getContentUri());
+ mSelectedItemSize.postValue(mSelectedItems.size());
+ updateSelectionAllowed();
+ }
+
+ /**
+ * Clear all selected items
+ */
+ public void clearSelectedItems() {
+ mSelectedItems.clear();
+ mSelectedItemSize.postValue(mSelectedItems.size());
+ updateSelectionAllowed();
+ }
+
+ /**
+ * @return {@code true} if give {@code item} is present in selected items
+ * {@link #mSelectedItems}, {@code false} otherwise
+ */
+ public boolean isItemSelected(Item item) {
+ return mSelectedItems.containsKey(item.getContentUri());
+ }
+
+ private void updateSelectionAllowed() {
+ final int size = mSelectedItems.size();
+ if (size >= mMaxSelectionLimit) {
+ if (mIsSelectionAllowed) {
+ mIsSelectionAllowed = false;
+ }
+ } else {
+ // size < mMaxSelectionLimit
+ if (!mIsSelectionAllowed) {
+ mIsSelectionAllowed = true;
+ }
+ }
+ }
+
+ /**
+ * @return returns whether more items can be selected or not. {@code true} if the number of
+ * selected items is lower than or equal to {@code mMaxLimit}, {@code false} otherwise.
+ */
+ public boolean isSelectionAllowed() {
+ return mIsSelectionAllowed;
+ }
+
+ /**
+ * Prepares current selected items for previewing all selected items in multi-select preview.
+ * The method also sorts the selected items by {@link Item#compareTo} method which sorts based
+ * on dateTaken values.
+ */
+ public void prepareSelectedItemsForPreviewAll() {
+ mSelectedItemsForPreview = new ArrayList<>(mSelectedItems.values());
+ mSelectedItemsForPreview.sort(Collections.reverseOrder(Item::compareTo));
+ }
+
+ /**
+ * Sets the given {@code item} as the item for previewing. This method will be used while
+ * previewing on long press.
+ */
+ public void prepareItemForPreviewOnLongPress(Item item) {
+ mSelectedItemsForPreview = Collections.singletonList(item);
+ }
+
+ /**
+ * @return {@link #mSelectedItemsForPreview} - selected items for preview.
+ */
+ public List<Item> getSelectedItemsForPreview() {
+ return Collections.unmodifiableList(mSelectedItemsForPreview);
+ }
+
+ /**
+ * Parse values from {@code intent} and set corresponding fields
+ */
+ public void parseSelectionValuesFromIntent(Intent intent) {
+ final Bundle extras = intent.getExtras();
+ final boolean isExtraPickImagesMaxSet =
+ extras != null && extras.containsKey(MediaStore.EXTRA_PICK_IMAGES_MAX);
+
+ // Support Intent.EXTRA_ALLOW_MULTIPLE flag only for ACTION_GET_CONTENT
+ if (intent.getAction() != null && intent.getAction().equals(
+ Intent.ACTION_GET_CONTENT)) {
+ if (isExtraPickImagesMaxSet) {
+ throw new IllegalArgumentException("EXTRA_PICK_IMAGES_MAX is not supported for "
+ + "ACTION_GET_CONTENT");
+ }
+
+ mSelectMultiple = intent.getBooleanExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);
+ if (mSelectMultiple) {
+ mMaxSelectionLimit = MediaStore.getPickImagesMaxLimit();
+ }
+ return;
+ }
+
+ // Check EXTRA_PICK_IMAGES_MAX value only if the flag is set.
+ if (isExtraPickImagesMaxSet) {
+ final int extraMax = intent.getIntExtra(MediaStore.EXTRA_PICK_IMAGES_MAX,
+ /* defaultValue */ -1);
+ // Multi selection max limit should always be greater than 1 and less than or equal
+ // to PICK_IMAGES_MAX_LIMIT.
+ if (extraMax <= 1 || extraMax > MediaStore.getPickImagesMaxLimit()) {
+ throw new IllegalArgumentException("Invalid EXTRA_PICK_IMAGES_MAX value");
+ }
+ mSelectMultiple = true;
+ mMaxSelectionLimit = extraMax;
+ }
+ }
+
+ /**
+ * Return whether supports multiple select {@link #mSelectMultiple} or not
+ */
+ public boolean canSelectMultiple() {
+ return mSelectMultiple;
+ }
+
+ /**
+ * Return maximum limit of items that can be selected
+ */
+ public int getMaxSelectionLimit() {
+ return mMaxSelectionLimit;
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/providers/media/photopicker/data/UnreliableVolumeDatabaseHelper.java b/src/com/android/providers/media/photopicker/data/UnreliableVolumeDatabaseHelper.java
new file mode 100644
index 0000000..9679954
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/data/UnreliableVolumeDatabaseHelper.java
@@ -0,0 +1,117 @@
+/*
+ * 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.data;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+import static com.android.providers.media.DatabaseHelper.VERSION_LATEST;
+
+import androidx.annotation.VisibleForTesting;
+
+public class UnreliableVolumeDatabaseHelper extends SQLiteOpenHelper implements AutoCloseable {
+ final Context mContext;
+ final String mName;
+ final int mVersion;
+
+ private static final String UNRELIABLE_VOLUME_DATABASE_NAME = "pickerUnreliableVolume.db";
+ private static final String TAG = "PickerUnreliableVolumeHelper";
+
+ public static final class MediaColumns {
+ private MediaColumns() {}
+ public static final String _ID = "_id";
+ public static final String DISPLAY_NAME = "display_name";
+ public static final String _DATA = "_data";
+ public static final String DATE_MODIFIED = "date_modified";
+ public static final String SIZE_BYTES = "size_bytes";
+ public static final String MIME_TYPE = "mime_type";
+ }
+
+ public UnreliableVolumeDatabaseHelper(Context context) {
+ this(context, UNRELIABLE_VOLUME_DATABASE_NAME, VERSION_LATEST);
+ }
+
+ public UnreliableVolumeDatabaseHelper(Context context, String name, int version) {
+ super(context, name, null, version);
+ mContext = context;
+ mName = name;
+ mVersion = version;
+
+ setWriteAheadLoggingEnabled(true);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ Log.v(TAG, "onCreate() for " + mName);
+
+ createSchema(db);
+ createIndexes(db);
+ }
+
+ @Override
+ public void onUpgrade(final SQLiteDatabase db, final int oldV, final int newV) {
+ Log.v(TAG, "onUpgrade() for " + mName + " from " + oldV + " to " + newV);
+ }
+
+ @VisibleForTesting
+ static void makePristineSchema(SQLiteDatabase db) {
+ // drop all tables
+ Cursor c = db.query("sqlite_master", new String[] {"name"}, "type is 'table'", null, null,
+ null, null);
+ while (c.moveToNext()) {
+ if (c.getString(0).startsWith("sqlite_")) continue;
+ db.execSQL("DROP TABLE IF EXISTS " + c.getString(0));
+ }
+ c.close();
+ }
+
+ @VisibleForTesting
+ static void makePristineIndexes(SQLiteDatabase db) {
+ // drop all indexes
+ Cursor c = db.query("sqlite_master", new String[] {"name"}, "type is 'index'", null,
+ null, null, null);
+ while (c.moveToNext()) {
+ if (c.getString(0).startsWith("sqlite_")) continue;
+ db.execSQL("DROP INDEX IF EXISTS " + c.getString(0));
+ }
+ c.close();
+ }
+
+ private void createSchema(SQLiteDatabase db) {
+ makePristineSchema(db);
+
+ db.execSQL("CREATE TABLE media (_id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + "date_modified INTEGER NOT NULL CHECK(date_modified >= 0),"
+ + "size_bytes INTEGER NOT NULL CHECK(size_bytes > 0),"
+ + "display_name TEXT NOT NULL,"
+ + "_data TEXT NOT NULL UNIQUE COLLATE NOCASE,"
+ + "mime_type TEXT NOT NULL)");
+ }
+
+ private void createIndexes(SQLiteDatabase db) {
+ makePristineIndexes(db);
+
+ db.execSQL("CREATE INDEX path_index on media(_data)");
+ db.execSQL("CREATE INDEX display_name_index on media(display_name)");
+ db.execSQL("CREATE INDEX date_modified_index on media(date_modified)");
+ db.execSQL("CREATE INDEX size_index on media(size_bytes)");
+ db.execSQL("CREATE INDEX mime_type_index on media(mime_type)");
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/data/UserIdManager.java b/src/com/android/providers/media/photopicker/data/UserIdManager.java
new file mode 100644
index 0000000..07d8c5c
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/data/UserIdManager.java
@@ -0,0 +1,459 @@
+/*
+ * 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.data;
+
+import static androidx.core.util.Preconditions.checkNotNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+import androidx.lifecycle.MutableLiveData;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.providers.media.photopicker.data.model.UserId;
+import com.android.providers.media.photopicker.util.CrossProfileUtils;
+
+import java.util.List;
+
+/**
+ * Interface to query user ids {@link UserId}
+ */
+public interface UserIdManager {
+
+ /**
+ * Whether there are more than 1 user profiles associated with the current user.
+ * @return
+ */
+ boolean isMultiUserProfiles();
+
+ /**
+ * Returns the personal user profile id iff there are at least 2 user profiles for current
+ * user. Otherwise, returns null.
+ */
+ @Nullable
+ UserId getPersonalUserId();
+
+ /**
+ * Returns the managed user profile id iff there are at least 2 user profiles for current user.
+ * Otherwise, returns null.
+ */
+ @Nullable
+ UserId getManagedUserId();
+
+ /**
+ * Returns the current user profile id. This can be managed user profile id, personal user
+ * profile id. If the user does not have a corresponding managed profile, then this always
+ * returns the current user.
+ */
+ @Nullable
+ UserId getCurrentUserProfileId();
+
+ /**
+ * Set Managed User as current user profile
+ */
+ void setManagedAsCurrentUserProfile();
+
+ /**
+ * Set Personal User as current user profile
+ */
+ void setPersonalAsCurrentUserProfile();
+
+ /**
+ * @return true iff current user is the current user profile selected
+ */
+ boolean isCurrentUserSelected();
+
+ /**
+ * @return true iff managed user is the current user profile selected
+ */
+ boolean isManagedUserSelected();
+
+ /**
+ * Whether the current user is the personal user profile iff there are at least 2 user
+ * profiles for current user. Otherwise, returns false.
+ */
+ boolean isPersonalUserId();
+
+ /**
+ * Whether the current user is the managed user profile iff there are at least 2 user
+ * profiles for current user. Otherwise, returns false.
+ */
+ boolean isManagedUserId();
+
+ /**
+ * Whether the current user is allowed to access other profile data.
+ */
+ boolean isCrossProfileAllowed();
+
+ /**
+ * Whether cross profile access is blocked by admin for the current user.
+ */
+ boolean isBlockedByAdmin();
+
+ /**
+ * Whether the work profile corresponding to the current user is turned off.
+ */
+ boolean isWorkProfileOff();
+
+ /**
+ * Set intent to check for device admin policy.
+ */
+ void setIntentAndCheckRestrictions(Intent intent);
+
+ /**
+ * Waits for Media Provider of the work profile to be available.
+ */
+ void waitForMediaProviderToBeAvailable();
+
+ /**
+ * Checks if work profile is switched off and updates the data.
+ */
+ void updateWorkProfileOffValue();
+
+ /**
+ * Resets the user ids. This is usually called as a result of receiving broadcast that
+ * managed profile has been added or removed.
+ */
+ void resetUserIds();
+
+ /**
+ * @return {@link MutableLiveData} to check if cross profile interaction allowed or not
+ */
+ @NonNull
+ MutableLiveData<Boolean> getCrossProfileAllowed();
+
+ /**
+ * @return {@link MutableLiveData} to check if there are multiple user profiles or not
+ */
+ @NonNull
+ MutableLiveData<Boolean> getIsMultiUserProfiles();
+
+ /**
+ * Creates an implementation of {@link UserIdManager}.
+ */
+ static UserIdManager create(Context context) {
+ return new RuntimeUserIdManager(context);
+ }
+
+ /**
+ * Implementation of {@link UserIdManager}. The class assumes that all its public methods are
+ * called from main thread only.
+ */
+ final class RuntimeUserIdManager implements UserIdManager {
+
+ private static final String TAG = "UserIdManager";
+
+ // These values are copied from DocumentsUI
+ private static final int PROVIDER_AVAILABILITY_MAX_RETRIES = 10;
+ private static final long PROVIDER_AVAILABILITY_CHECK_DELAY = 4000;
+
+ private final Context mContext;
+ private final UserId mCurrentUser;
+ private final Handler mHandler;
+
+ private Runnable mIsProviderAvailableRunnable;
+
+ private UserId mPersonalUser = null;
+ private UserId mManagedUser = null;
+
+ private UserId mCurrentUserProfile = null;
+
+ // Set default values to negative case, only set as false if checks pass.
+ private boolean mIsBlockedByAdmin = true;
+ private boolean mIsWorkProfileOff = true;
+
+ private final MutableLiveData<Boolean> mIsMultiUserProfiles = new MutableLiveData<>();
+ private final MutableLiveData<Boolean> mIsCrossProfileAllowed = new MutableLiveData<>();
+
+ private RuntimeUserIdManager(Context context) {
+ this(context, UserId.CURRENT_USER);
+ }
+
+ @VisibleForTesting
+ RuntimeUserIdManager(Context context, UserId currentUser) {
+ mContext = context.getApplicationContext();
+ mCurrentUser = checkNotNull(currentUser);
+ mCurrentUserProfile = mCurrentUser;
+ mHandler = new Handler(Looper.getMainLooper());
+ setUserIds();
+ }
+
+ @Override
+ public MutableLiveData<Boolean> getCrossProfileAllowed() {
+ return mIsCrossProfileAllowed;
+ }
+
+ @Override
+ public MutableLiveData<Boolean> getIsMultiUserProfiles() {
+ return mIsMultiUserProfiles;
+ }
+
+ @Override
+ public void resetUserIds() {
+ assertMainThread();
+ setUserIds();
+ }
+
+ @Override
+ public boolean isMultiUserProfiles() {
+ assertMainThread();
+ return mPersonalUser != null;
+ }
+
+ @Override
+ public UserId getPersonalUserId() {
+ assertMainThread();
+ return mPersonalUser;
+ }
+
+ @Override
+ public UserId getManagedUserId() {
+ assertMainThread();
+ return mManagedUser;
+ }
+
+ @Override
+ public UserId getCurrentUserProfileId() {
+ assertMainThread();
+ return mCurrentUserProfile;
+ }
+
+ @Override
+ public void setManagedAsCurrentUserProfile() {
+ assertMainThread();
+ setCurrentUserProfileId(getManagedUserId());
+ }
+
+ @Override
+ public void setPersonalAsCurrentUserProfile() {
+ assertMainThread();
+ setCurrentUserProfileId(getPersonalUserId());
+ }
+
+ @Override
+ public void setIntentAndCheckRestrictions(Intent intent) {
+ assertMainThread();
+ if (isMultiUserProfiles()) {
+ updateCrossProfileValues(intent);
+ }
+ }
+
+ public boolean isCurrentUserSelected() {
+ assertMainThread();
+ return mCurrentUserProfile.equals(UserId.CURRENT_USER);
+ }
+
+ public boolean isManagedUserSelected() {
+ assertMainThread();
+ return mCurrentUserProfile.equals(getManagedUserId());
+ }
+
+ @Override
+ public boolean isPersonalUserId() {
+ assertMainThread();
+ return mCurrentUser.equals(getPersonalUserId());
+ }
+
+ @Override
+ public boolean isManagedUserId() {
+ assertMainThread();
+ return mCurrentUser.equals(getManagedUserId());
+ }
+
+ private void setCurrentUserProfileId(UserId userId) {
+ mCurrentUserProfile = userId;
+ }
+
+ private void setUserIds() {
+ setUserIdsInternal();
+ mIsMultiUserProfiles.postValue(isMultiUserProfiles());
+ }
+
+ private void setUserIdsInternal() {
+ mPersonalUser = null;
+ mManagedUser = null;
+ UserManager userManager = mContext.getSystemService(UserManager.class);
+ if (userManager == null) {
+ Log.e(TAG, "Cannot obtain user manager");
+ return;
+ }
+
+ final List<UserHandle> userProfiles = userManager.getUserProfiles();
+ if (userProfiles.size() < 2) {
+ Log.d(TAG, "Only 1 user profile found");
+ return;
+ }
+
+ if (mCurrentUser.isManagedProfile(userManager)) {
+ final UserId managedUser = mCurrentUser;
+ final UserHandle parentUser =
+ userManager.getProfileParent(managedUser.getUserHandle());
+ if (parentUser != null) {
+ mPersonalUser = UserId.of(parentUser);
+ mManagedUser = managedUser;
+ }
+
+ } else {
+ final UserId personalUser = mCurrentUser;
+ // Check if this personal profile is a parent of any other managed profile.
+ for (UserHandle userHandle : userProfiles) {
+ if (userManager.isManagedProfile(userHandle.getIdentifier())) {
+ final UserHandle parentUser =
+ userManager.getProfileParent(userHandle);
+ if (parentUser != null &&
+ parentUser.equals(personalUser.getUserHandle())) {
+ mPersonalUser = personalUser;
+ mManagedUser = UserId.of(userHandle);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean isCrossProfileAllowed() {
+ assertMainThread();
+ return (!isWorkProfileOff() && !isBlockedByAdmin());
+ }
+
+ @Override
+ public boolean isWorkProfileOff() {
+ assertMainThread();
+ return mIsWorkProfileOff;
+ }
+
+ @Override
+ public boolean isBlockedByAdmin() {
+ assertMainThread();
+ return mIsBlockedByAdmin;
+ }
+
+ @Override
+ public void updateWorkProfileOffValue() {
+ assertMainThread();
+ mIsWorkProfileOff = isWorkProfileOffInternal(getManagedUserId());
+ mIsCrossProfileAllowed.postValue(isCrossProfileAllowed());
+ }
+
+ @Override
+ public void waitForMediaProviderToBeAvailable() {
+ assertMainThread();
+ final UserId managedUserProfileId = getManagedUserId();
+ if (CrossProfileUtils.isMediaProviderAvailable(managedUserProfileId, mContext)) {
+ mIsWorkProfileOff = false;
+ mIsCrossProfileAllowed.postValue(isCrossProfileAllowed());
+ stopWaitingForProviderToBeAvailable();
+ return;
+ }
+ waitForProviderToBeAvailable(managedUserProfileId, /* numOfTries */ 1);
+ }
+
+ private void updateCrossProfileValues(Intent intent) {
+ setCrossProfileValues(intent);
+ mIsCrossProfileAllowed.postValue(isCrossProfileAllowed());
+ }
+
+ private void setCrossProfileValues(Intent intent) {
+ // 1. Check if PICK_IMAGES intent is allowed by admin to show cross user content
+ setBlockedByAdminValue(intent);
+
+ // 2. Check if work profile is off
+ updateWorkProfileOffValue();
+
+ // 3. For first initial setup, wait for MediaProvider to be on.
+ // (This is not blocking)
+ if (mIsWorkProfileOff) {
+ waitForMediaProviderToBeAvailable();
+ }
+ }
+
+ private void setBlockedByAdminValue(Intent intent) {
+ if (intent == null) {
+ Log.e(TAG, "No intent specified to check if cross profile forwarding is"
+ + " allowed.");
+ return;
+ }
+ final PackageManager packageManager = mContext.getPackageManager();
+ if (!CrossProfileUtils.isIntentAllowedCrossProfileAccess(intent, packageManager)) {
+ mIsBlockedByAdmin = true;
+ return;
+ }
+ mIsBlockedByAdmin = false;
+ }
+
+ private boolean isWorkProfileOffInternal(UserId managedUserProfileId) {
+ return CrossProfileUtils.isQuietModeEnabled(managedUserProfileId, mContext) ||
+ !CrossProfileUtils.isMediaProviderAvailable(managedUserProfileId, mContext);
+ }
+
+ private void waitForProviderToBeAvailable(UserId userId, int numOfTries) {
+ // The runnable should make sure to post update on the live data if it is changed.
+ mIsProviderAvailableRunnable = () -> {
+ // We stop the recursive check when
+ // 1. the provider is available
+ // 2. the profile is in quiet mode, i.e. provider will not be available
+ // 3. after maximum retries
+ if (CrossProfileUtils.isMediaProviderAvailable(userId, mContext)) {
+ mIsWorkProfileOff = false;
+ mIsCrossProfileAllowed.postValue(isCrossProfileAllowed());
+ return;
+ }
+
+ if (CrossProfileUtils.isQuietModeEnabled(userId, mContext)) {
+ return;
+ }
+
+ if (numOfTries <= PROVIDER_AVAILABILITY_MAX_RETRIES) {
+ Log.d(TAG, "MediaProvider is not available. Retry after " +
+ PROVIDER_AVAILABILITY_CHECK_DELAY);
+ waitForProviderToBeAvailable(userId, numOfTries + 1);
+ return;
+ }
+
+ Log.w(TAG, "Failed waiting for MediaProvider for user:" + userId +
+ " to be available");
+ };
+
+ mHandler.postDelayed(mIsProviderAvailableRunnable, PROVIDER_AVAILABILITY_CHECK_DELAY);
+ }
+
+ private void stopWaitingForProviderToBeAvailable() {
+ if (mIsProviderAvailableRunnable == null) {
+ return;
+ }
+ mHandler.removeCallbacks(mIsProviderAvailableRunnable);
+ mIsProviderAvailableRunnable = null;
+ }
+
+ private void assertMainThread() {
+ if (Looper.getMainLooper().isCurrentThread()) return;
+
+ throw new IllegalStateException("UserIdManager methods are expected to be called from "
+ + "main thread. " + (Looper.myLooper() == null ? "" : "Current thread "
+ + Looper.myLooper().getThread() + ", Main thread "
+ + Looper.getMainLooper().getThread()));
+ }
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/data/glide/PickerGlideModule.java b/src/com/android/providers/media/photopicker/data/glide/PickerGlideModule.java
new file mode 100644
index 0000000..9ad40bb
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/data/glide/PickerGlideModule.java
@@ -0,0 +1,39 @@
+/*
+ * 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.data.glide;
+
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.content.Context;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.Registry;
+import com.bumptech.glide.annotation.GlideModule;
+import com.bumptech.glide.module.AppGlideModule;
+
+/**
+ * Custom glide module to enable the loading of thumbnails from cloud media provider.
+ */
+@GlideModule
+public class PickerGlideModule extends AppGlideModule {
+
+ @Override
+ public void registerComponents(Context context, Glide glide, Registry registry) {
+ registry.prepend(Uri.class, ParcelFileDescriptor.class,
+ new PickerModelLoaderFactory(context));
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/data/glide/PickerModelLoader.java b/src/com/android/providers/media/photopicker/data/glide/PickerModelLoader.java
new file mode 100644
index 0000000..2477caf
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/data/glide/PickerModelLoader.java
@@ -0,0 +1,59 @@
+/*
+ * 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.data.glide;
+
+import static com.android.providers.media.photopicker.ui.ImageLoader.THUMBNAIL_REQUEST;
+
+import android.content.Context;
+import android.content.UriMatcher;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.provider.CloudMediaProviderContract;
+
+import com.bumptech.glide.load.Options;
+import com.bumptech.glide.load.model.ModelLoader;
+import com.bumptech.glide.signature.ObjectKey;
+
+/**
+ * Custom {@link ModelLoader} to load thumbnails from cloud media provider.
+ */
+public final class PickerModelLoader implements ModelLoader<Uri, ParcelFileDescriptor> {
+ private final Context mContext;
+
+ PickerModelLoader(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public LoadData<ParcelFileDescriptor> buildLoadData(Uri model, int width, int height,
+ Options options) {
+ final boolean isThumbRequest = Boolean.TRUE.equals(options.get(THUMBNAIL_REQUEST));
+ return new LoadData<>(new ObjectKey(model),
+ new PickerThumbnailFetcher(mContext, model, width, height, isThumbRequest));
+ }
+
+ @Override
+ public boolean handles(Uri model) {
+ final int pickerId = 1;
+ final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
+ matcher.addURI(model.getAuthority(),
+ CloudMediaProviderContract.URI_PATH_MEDIA + "/*", pickerId);
+
+ // Matches picker URIs of the form content://<authority>/media
+ return matcher.match(model) == pickerId;
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/data/glide/PickerModelLoaderFactory.java b/src/com/android/providers/media/photopicker/data/glide/PickerModelLoaderFactory.java
new file mode 100644
index 0000000..1a53e75
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/data/glide/PickerModelLoaderFactory.java
@@ -0,0 +1,48 @@
+/*
+ * 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.data.glide;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+
+import com.bumptech.glide.load.model.ModelLoader;
+import com.bumptech.glide.load.model.ModelLoaderFactory;
+import com.bumptech.glide.load.model.MultiModelLoaderFactory;
+
+/**
+ * Custom {@link ModelLoaderFactory} which provides a {@link ModelLoader} for loading thumbnails
+ * from cloud media provider.
+ */
+public class PickerModelLoaderFactory implements ModelLoaderFactory<Uri, ParcelFileDescriptor> {
+
+ private final Context mContext;
+
+ public PickerModelLoaderFactory(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public ModelLoader<Uri, ParcelFileDescriptor> build(MultiModelLoaderFactory unused) {
+ return new PickerModelLoader(mContext);
+ }
+
+ @Override
+ public void teardown() {
+ // Do nothing.
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/data/glide/PickerThumbnailFetcher.java b/src/com/android/providers/media/photopicker/data/glide/PickerThumbnailFetcher.java
new file mode 100644
index 0000000..9dcb211
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/data/glide/PickerThumbnailFetcher.java
@@ -0,0 +1,100 @@
+/*
+ * 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.data.glide;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.graphics.Point;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.provider.CloudMediaProviderContract;
+
+import com.bumptech.glide.Priority;
+import com.bumptech.glide.load.DataSource;
+import com.bumptech.glide.load.data.DataFetcher;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+/**
+ * Custom {@link DataFetcher} to fetch a {@link ParcelFileDescriptor} for a thumbnail from a cloud
+ * media provider.
+ */
+public class PickerThumbnailFetcher implements DataFetcher<ParcelFileDescriptor> {
+
+ private final Context mContext;
+ private final Uri mModel;
+ private final int mWidth;
+ private final int mHeight;
+ private final boolean mIsThumbRequest;
+
+ PickerThumbnailFetcher(Context context, Uri model, int width, int height,
+ boolean isThumbRequest) {
+ mContext = context;
+ mModel = model;
+ mWidth = width;
+ mHeight = height;
+ mIsThumbRequest = isThumbRequest;
+ }
+
+ @Override
+ public void loadData(Priority priority, DataCallback<? super ParcelFileDescriptor> callback) {
+ ContentResolver contentResolver = mContext.getContentResolver();
+ final Bundle opts = new Bundle();
+ opts.putParcelable(ContentResolver.EXTRA_SIZE, new Point(mWidth, mHeight));
+ opts.putBoolean(CloudMediaProviderContract.EXTRA_PREVIEW_THUMBNAIL, true);
+
+ if (mIsThumbRequest) {
+ opts.putBoolean(CloudMediaProviderContract.EXTRA_MEDIASTORE_THUMB, true);
+ }
+
+ try (AssetFileDescriptor afd = contentResolver.openTypedAssetFileDescriptor(mModel,
+ /* mimeType */ "image/*", opts, /* cancellationSignal */ null)) {
+ if (afd == null) {
+ final String err = "Failed to load data for " + mModel;
+ callback.onLoadFailed(new FileNotFoundException(err));
+ return;
+ }
+ callback.onDataReady(afd.getParcelFileDescriptor());
+ } catch (IOException e) {
+ callback.onLoadFailed(e);
+ }
+ }
+
+ @Override
+ public void cleanup() {
+ // Intentionally empty only because we're not opening an InputStream or another I/O
+ // resource.
+ }
+
+ @Override
+ public void cancel() {
+ // Intentionally empty.
+ }
+
+ @Override
+ public Class<ParcelFileDescriptor> getDataClass() {
+ return ParcelFileDescriptor.class;
+ }
+
+ @Override
+ public DataSource getDataSource() {
+ return DataSource.LOCAL;
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/data/model/Category.java b/src/com/android/providers/media/photopicker/data/model/Category.java
new file mode 100644
index 0000000..6166d4a
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/data/model/Category.java
@@ -0,0 +1,185 @@
+/*
+ * 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.data.model;
+
+import static android.provider.CloudMediaProviderContract.AlbumColumns;
+import static android.provider.CloudMediaProviderContract.AlbumColumns.ALBUM_ID_VIDEOS;
+import static android.provider.CloudMediaProviderContract.AlbumColumns.ALBUM_ID_SCREENSHOTS;
+import static android.provider.CloudMediaProviderContract.AlbumColumns.ALBUM_ID_CAMERA;
+import static android.provider.CloudMediaProviderContract.AlbumColumns.ALBUM_ID_DOWNLOADS;
+import static android.provider.CloudMediaProviderContract.AlbumColumns.ALBUM_ID_FAVORITES;
+import static com.android.providers.media.photopicker.util.CursorUtils.getCursorInt;
+import static com.android.providers.media.photopicker.util.CursorUtils.getCursorString;
+
+import android.annotation.StringDef;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.provider.CloudMediaProviderContract;
+import android.provider.MediaStore;
+import android.provider.MediaStore.Files.FileColumns;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.providers.media.R;
+import com.android.providers.media.photopicker.data.ItemsProvider;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Defines each category (which is group of items) for the photo picker.
+ */
+public class Category {
+ public static final Category DEFAULT = new Category();
+
+ private final String mId;
+ private final String mAuthority;
+ private final String mDisplayName;
+ private final boolean mIsLocal;
+ private final Uri mCoverUri;
+ private final int mItemCount;
+
+ private Category() {
+ this(null, null, null, null, 0, false);
+ }
+
+ @VisibleForTesting
+ public Category(String id, String authority, String displayName, Uri coverUri, int itemCount,
+ boolean isLocal) {
+ mId = id;
+ mAuthority = authority;
+ mDisplayName = displayName;
+ mIsLocal = isLocal;
+ mCoverUri = coverUri;
+ mItemCount = itemCount;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(Locale.ROOT, "Category: {mId: %s, mAuthority: %s, mDisplayName: %s, " +
+ "mCoverUri: %s, mItemCount: %d, mIsLocal: %b",
+ mId, mAuthority, mDisplayName, mCoverUri, mItemCount, mIsLocal);
+ }
+
+ public String getId() {
+ return mId;
+ }
+
+ public String getAuthority() {
+ return mAuthority;
+ }
+
+ public String getDisplayName(Context context) {
+ if (mIsLocal) {
+ return getLocalizedDisplayName(context, mId);
+ }
+ return mDisplayName;
+ }
+
+ public boolean isLocal() {
+ return mIsLocal;
+ }
+
+ public Uri getCoverUri() {
+ return mCoverUri;
+ }
+
+ public int getItemCount() {
+ return mItemCount;
+ }
+
+ public boolean isDefault() {
+ return TextUtils.isEmpty(mId);
+ }
+
+ /**
+ * Write the {@link Category} to the given {@code bundle}.
+ */
+ public void toBundle(@NonNull Bundle bundle) {
+ bundle.putString(AlbumColumns.ID, mId);
+ bundle.putString(AlbumColumns.AUTHORITY, mAuthority);
+ bundle.putString(AlbumColumns.DISPLAY_NAME, mDisplayName);
+ // Re-using the 'media_cover_id' to store the media_cover_uri for lack of
+ // a different constant
+ bundle.putParcelable(AlbumColumns.MEDIA_COVER_ID, mCoverUri);
+ bundle.putInt(AlbumColumns.MEDIA_COUNT, mItemCount);
+ bundle.putBoolean(AlbumColumns.IS_LOCAL, mIsLocal);
+ }
+
+ /**
+ * Create a {@link Category} from the {@code bundle}.
+ */
+ public static Category fromBundle(@NonNull Bundle bundle) {
+ return new Category(bundle.getString(AlbumColumns.ID),
+ bundle.getString(AlbumColumns.AUTHORITY),
+ bundle.getString(AlbumColumns.DISPLAY_NAME),
+ bundle.getParcelable(AlbumColumns.MEDIA_COVER_ID),
+ bundle.getInt(AlbumColumns.MEDIA_COUNT),
+ bundle.getBoolean(AlbumColumns.IS_LOCAL));
+ }
+
+ /**
+ * Create a {@link Category} from the {@code cursor}.
+ */
+ public static Category fromCursor(@NonNull Cursor cursor, @NonNull UserId userId) {
+ final boolean isLocal;
+ String authority = getCursorString(cursor, AlbumColumns.AUTHORITY);
+ if (authority != null) {
+ isLocal = true;
+ } else {
+ isLocal = false;
+ authority = cursor.getExtras().getString(MediaStore.EXTRA_CLOUD_PROVIDER);
+ }
+ final Uri coverUri = ItemsProvider.getItemsUri(
+ getCursorString(cursor, AlbumColumns.MEDIA_COVER_ID), authority, userId);
+
+ return new Category(getCursorString(cursor, AlbumColumns.ID),
+ authority,
+ getCursorString(cursor, AlbumColumns.DISPLAY_NAME),
+ coverUri,
+ getCursorInt(cursor, AlbumColumns.MEDIA_COUNT),
+ isLocal);
+ }
+
+ private static String getLocalizedDisplayName(Context context, String albumId) {
+ switch (albumId) {
+ case ALBUM_ID_VIDEOS:
+ return context.getString(R.string.picker_category_videos);
+ case ALBUM_ID_CAMERA:
+ return context.getString(R.string.picker_category_camera);
+ case ALBUM_ID_SCREENSHOTS:
+ return context.getString(R.string.picker_category_screenshots);
+ case ALBUM_ID_DOWNLOADS:
+ return context.getString(R.string.picker_category_downloads);
+ case ALBUM_ID_FAVORITES:
+ return context.getString(R.string.picker_category_favorites);
+ default:
+ return albumId;
+ }
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/data/model/Item.java b/src/com/android/providers/media/photopicker/data/model/Item.java
new file mode 100644
index 0000000..7983555
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/data/model/Item.java
@@ -0,0 +1,222 @@
+/*
+ * 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.data.model;
+
+import static android.provider.CloudMediaProviderContract.MediaColumns;
+import static android.provider.MediaStore.Files.FileColumns._SPECIAL_FORMAT_ANIMATED_WEBP;
+import static android.provider.MediaStore.Files.FileColumns._SPECIAL_FORMAT_GIF;
+import static android.provider.MediaStore.Files.FileColumns._SPECIAL_FORMAT_MOTION_PHOTO;
+
+import static com.android.providers.media.photopicker.util.CursorUtils.getCursorInt;
+import static com.android.providers.media.photopicker.util.CursorUtils.getCursorLong;
+import static com.android.providers.media.photopicker.util.CursorUtils.getCursorString;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.CloudMediaProviderContract;
+import android.provider.MediaStore;
+import android.text.format.DateUtils;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.providers.media.R;
+import com.android.providers.media.photopicker.data.ItemsProvider;
+import com.android.providers.media.photopicker.util.DateTimeUtils;
+import com.android.providers.media.util.MimeUtils;
+
+/**
+ * Base class representing one single entity/item in the PhotoPicker.
+ */
+public class Item {
+ private String mId;
+ private long mDateTaken;
+ private long mGenerationModified;
+ private long mDuration;
+ private String mMimeType;
+ private Uri mUri;
+ private boolean mIsImage;
+ private boolean mIsVideo;
+ private int mSpecialFormat;
+ private boolean mIsDate;
+
+ private Item() {}
+
+ public Item(@NonNull Cursor cursor, @NonNull UserId userId) {
+ updateFromCursor(cursor, userId);
+ }
+
+ @VisibleForTesting
+ public Item(String id, String mimeType, long dateTaken, long generationModified, long duration,
+ Uri uri, int specialFormat) {
+ mId = id;
+ mMimeType = mimeType;
+ mDateTaken = dateTaken;
+ mGenerationModified = generationModified;
+ mDuration = duration;
+ mUri = uri;
+ mSpecialFormat = specialFormat;
+ parseMimeType();
+ }
+
+ public String getId() {
+ return mId;
+ }
+
+ public boolean isImage() {
+ return mIsImage;
+ }
+
+ public boolean isVideo() {
+ return mIsVideo;
+ }
+
+ public boolean isGifOrAnimatedWebp() {
+ return isGif() || isAnimatedWebp();
+ }
+
+ public boolean isGif() {
+ return mSpecialFormat == _SPECIAL_FORMAT_GIF;
+ }
+
+ public boolean isAnimatedWebp() {
+ return mSpecialFormat == _SPECIAL_FORMAT_ANIMATED_WEBP;
+ }
+
+ public boolean isMotionPhoto() {
+ return mSpecialFormat == _SPECIAL_FORMAT_MOTION_PHOTO;
+ }
+
+ public boolean isDate() {
+ return mIsDate;
+ }
+
+ public Uri getContentUri() {
+ return mUri;
+ }
+
+ public long getDuration() {
+ return mDuration;
+ }
+
+ public String getMimeType() {
+ return mMimeType;
+ }
+
+ public long getDateTaken() {
+ return mDateTaken;
+ }
+
+ public long getGenerationModified() {
+ return mGenerationModified;
+ }
+
+ @VisibleForTesting
+ public int getSpecialFormat() {
+ return mSpecialFormat;
+ }
+
+ public static Item fromCursor(Cursor cursor, UserId userId) {
+ assert(cursor != null);
+ final Item item = new Item(cursor, userId);
+ return item;
+ }
+
+ /**
+ * Return the date item. If dateTaken is 0, it is a recent item.
+ * @param dateTaken the time of date taken. The unit is in milliseconds
+ * since January 1, 1970 00:00:00.0 UTC.
+ * @return the item with date type
+ */
+ public static Item createDateItem(long dateTaken) {
+ final Item item = new Item();
+ item.mIsDate = true;
+ item.mDateTaken = dateTaken;
+ return item;
+ }
+
+ /**
+ * Update the item based on the cursor
+ *
+ * @param cursor the cursor to update the data
+ * @param userId the user id to create an {@link Item} for
+ */
+ public void updateFromCursor(@NonNull Cursor cursor, @NonNull UserId userId) {
+ final String authority = getCursorString(cursor, MediaColumns.AUTHORITY);
+ mId = getCursorString(cursor, MediaColumns.ID);
+ mMimeType = getCursorString(cursor, MediaColumns.MIME_TYPE);
+ mDateTaken = getCursorLong(cursor, MediaColumns.DATE_TAKEN_MILLIS);
+ mGenerationModified = getCursorLong(cursor, MediaColumns.SYNC_GENERATION);
+ mDuration = getCursorLong(cursor, MediaColumns.DURATION_MILLIS);
+ mSpecialFormat = getCursorInt(cursor, MediaColumns.STANDARD_MIME_TYPE_EXTENSION);
+ mUri = ItemsProvider.getItemsUri(mId, authority, userId);
+
+ parseMimeType();
+ }
+
+ public String getContentDescription(@NonNull Context context) {
+ if (isVideo()) {
+ return context.getString(R.string.picker_video_item_content_desc,
+ DateTimeUtils.getDateTimeStringForContentDesc(getDateTaken()),
+ getDurationText());
+ }
+
+ final String itemType;
+ if (isGif() || isAnimatedWebp()) {
+ itemType = context.getString(R.string.picker_gif);
+ } else if (isMotionPhoto()) {
+ itemType = context.getString(R.string.picker_motion_photo);
+ } else {
+ itemType = context.getString(R.string.picker_photo);
+ }
+
+ return context.getString(R.string.picker_item_content_desc, itemType,
+ DateTimeUtils.getDateTimeStringForContentDesc(getDateTaken()));
+ }
+
+ public String getDurationText() {
+ if (mDuration == -1) {
+ return "";
+ }
+ return DateUtils.formatElapsedTime(mDuration / 1000);
+ }
+
+ private void parseMimeType() {
+ if (MimeUtils.isImageMimeType(mMimeType)) {
+ mIsImage = true;
+ } else if (MimeUtils.isVideoMimeType(mMimeType)) {
+ mIsVideo = true;
+ }
+ }
+
+ /**
+ * Compares this item with given {@code anotherItem} by comparing
+ * {@link Item#getDateTaken()} value. When {@link Item#getDateTaken()} is
+ * same, Items are compared based on {@link Item#getId}.
+ */
+ public int compareTo(Item anotherItem) {
+ if (mDateTaken > anotherItem.getDateTaken()) {
+ return 1;
+ } else if (mDateTaken < anotherItem.getDateTaken()) {
+ return -1;
+ } else {
+ return mId.compareTo(anotherItem.getId());
+ }
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/data/model/UserId.java b/src/com/android/providers/media/photopicker/data/model/UserId.java
new file mode 100644
index 0000000..96dc064
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/data/model/UserId.java
@@ -0,0 +1,108 @@
+/*
+ * 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.data.model;
+
+import static androidx.core.util.Preconditions.checkNotNull;
+
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+/**
+ * Representation of a {@link UserHandle}.
+ */
+public final class UserId {
+ // A current user represents the user of the app's process. It is mainly used for comparison.
+ public static final UserId CURRENT_USER = UserId.of(Process.myUserHandle());
+
+ private static final String TAG = "PhotoPickerUserId";
+
+ private final UserHandle mUserHandle;
+
+ private UserId(UserHandle userHandle) {
+ checkNotNull(userHandle);
+ mUserHandle = userHandle;
+ }
+
+ public UserHandle getUserHandle() {
+ return mUserHandle;
+ }
+
+ /**
+ * Returns a {@link UserId} for a given {@link UserHandle}.
+ */
+ public static UserId of(UserHandle userHandle) {
+ return new UserId(userHandle);
+ }
+
+ /**
+ * Returns the given context if the user is the current user or unspecified. Otherwise, returns
+ * an "android" package context as the user.
+ *
+ * @throws NameNotFoundException if android package of the other user does not exist
+ */
+ Context asContext(Context context) throws NameNotFoundException {
+ if (CURRENT_USER.equals(this)) {
+ return context;
+ }
+ return context.createPackageContextAsUser("android", /* flags= */ 0, mUserHandle);
+ }
+
+ /**
+ * Return a content resolver instance of this user.
+ */
+ public ContentResolver getContentResolver(Context context) throws NameNotFoundException {
+ return asContext(context).getContentResolver();
+ }
+
+ /**
+ * @return {@link UserHandle} of parent user profile. Otherwise returns {@code null}.
+ */
+ public static UserHandle getParentProfile(UserManager userManager, UserHandle userHandle) {
+ return userManager.getProfileParent(userHandle);
+ }
+
+ /**
+ * Returns true if the this user is a managed profile.
+ */
+ public boolean isManagedProfile(UserManager userManager) {
+ return userManager.isManagedProfile(mUserHandle.getIdentifier());
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ try {
+ if (obj != null) {
+ UserId other = (UserId)obj;
+ return mUserHandle == other.mUserHandle;
+ }
+ } catch (ClassCastException e) {
+ Log.e(TAG, "Cannot check equality due to ", e);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(this.mUserHandle.getIdentifier());
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/metrics/PhotoPickerUiEventLogger.java b/src/com/android/providers/media/photopicker/metrics/PhotoPickerUiEventLogger.java
new file mode 100644
index 0000000..53fd459
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/metrics/PhotoPickerUiEventLogger.java
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+package com.android.providers.media.photopicker.metrics;
+
+import com.android.internal.logging.InstanceId;
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+import com.android.providers.media.metrics.MPUiEventLoggerImpl;
+
+public class PhotoPickerUiEventLogger {
+
+ enum PhotoPickerEvent implements UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "Photo picker opened in personal profile")
+ PHOTO_PICKER_OPEN_PERSONAL_PROFILE(942),
+ @UiEvent(doc = "Photo picker opened in work profile")
+ PHOTO_PICKER_OPEN_WORK_PROFILE(943);
+
+ private final int mId;
+
+ PhotoPickerEvent(int id) {
+ mId = id;
+ }
+
+ @Override
+ public int getId() {
+ return mId;
+ }
+ }
+
+ private UiEventLogger logger;
+
+ public PhotoPickerUiEventLogger() {
+ logger = new MPUiEventLoggerImpl();
+ }
+
+ public void logPickerOpenPersonal(InstanceId instanceId,
+ String callingPackage) {
+ logger.logWithInstanceId(
+ PhotoPickerEvent.PHOTO_PICKER_OPEN_PERSONAL_PROFILE,
+ 0,
+ callingPackage,
+ instanceId);
+ }
+
+ public void logPickerOpenWork(InstanceId instanceId,
+ String callingPackage) {
+ logger.logWithInstanceId(
+ PhotoPickerEvent.PHOTO_PICKER_OPEN_WORK_PROFILE,
+ 0,
+ callingPackage,
+ instanceId);
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/AlbumGridHolder.java b/src/com/android/providers/media/photopicker/ui/AlbumGridHolder.java
new file mode 100644
index 0000000..d84395c
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/AlbumGridHolder.java
@@ -0,0 +1,81 @@
+/*
+ * 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.icu.text.MessageFormat;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import com.android.providers.media.R;
+import com.android.providers.media.photopicker.data.model.Category;
+import com.android.providers.media.util.StringUtils;
+
+import java.text.NumberFormat;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * ViewHolder of a album item within a RecyclerView.
+ */
+public class AlbumGridHolder extends BaseViewHolder {
+
+ private final ImageLoader mImageLoader;
+ private final ImageView mIconThumb;
+ private final TextView mAlbumName;
+ private final TextView mItemCount;
+ private final boolean mHasMimeTypeFilter;
+
+ public AlbumGridHolder(@NonNull Context context, @NonNull ViewGroup parent,
+ @NonNull ImageLoader imageLoader, boolean hasMimeTypeFilter) {
+ super(context, parent, R.layout.item_album_grid);
+
+ mIconThumb = itemView.findViewById(R.id.icon_thumbnail);
+ mAlbumName = itemView.findViewById(R.id.album_name);
+ mItemCount = itemView.findViewById(R.id.item_count);
+ mImageLoader = imageLoader;
+ mHasMimeTypeFilter = hasMimeTypeFilter;
+ }
+
+ @Override
+ public void bind() {
+ final Category category = (Category) itemView.getTag();
+ mImageLoader.loadAlbumThumbnail(category, mIconThumb);
+ mAlbumName.setText(category.getDisplayName(itemView.getContext()));
+
+ // Check whether there is a mime type filter or not. If yes, hide the item count. Otherwise,
+ // show the item count and update the count.
+ if (mHasMimeTypeFilter) {
+ mItemCount.setVisibility(View.GONE);
+ } else {
+ mItemCount.setVisibility(View.VISIBLE);
+ final int itemCount = category.getItemCount();
+ final String quantityText =
+ StringUtils.getICUFormatString(
+ itemView.getResources(), itemCount, R.string.picker_album_item_count);
+ final String itemCountString = NumberFormat.getInstance(Locale.getDefault()).format(
+ itemCount);
+ mItemCount.setText(TextUtils.expandTemplate(quantityText, itemCountString));
+ }
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/AlbumsTabAdapter.java b/src/com/android/providers/media/photopicker/ui/AlbumsTabAdapter.java
new file mode 100644
index 0000000..447da5e
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/AlbumsTabAdapter.java
@@ -0,0 +1,86 @@
+/*
+ * 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.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.providers.media.photopicker.data.model.Category;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Adapts from model to something RecyclerView understands.
+ */
+public class AlbumsTabAdapter extends RecyclerView.Adapter<BaseViewHolder> {
+
+ private static final int ITEM_TYPE_CATEGORY = 1;
+
+ public static final int COLUMN_COUNT = 2;
+
+ private final ImageLoader mImageLoader;
+ private final View.OnClickListener mOnClickListener;
+ private final boolean mHasMimeTypeFilter;
+
+ private List<Category> mCategoryList = new ArrayList<>();
+
+
+ public AlbumsTabAdapter(ImageLoader imageLoader, View.OnClickListener listener,
+ boolean hasMimeTypeFilter) {
+ mImageLoader = imageLoader;
+ mOnClickListener = listener;
+ mHasMimeTypeFilter = hasMimeTypeFilter;
+ }
+
+ @NonNull
+ @Override
+ public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
+ return new AlbumGridHolder(viewGroup.getContext(), viewGroup, mImageLoader,
+ mHasMimeTypeFilter);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull BaseViewHolder itemHolder, int position) {
+ final Category category = getCategory(position);
+ itemHolder.itemView.setTag(category);
+ itemHolder.itemView.setOnClickListener(mOnClickListener);
+ itemHolder.bind();
+ }
+
+ @Override
+ public int getItemCount() {
+ return mCategoryList.size();
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return ITEM_TYPE_CATEGORY;
+ }
+
+ public Category getCategory(int position) {
+ return mCategoryList.get(position);
+ }
+
+ public void updateCategoryList(List<Category> categoryList) {
+ mCategoryList = categoryList;
+ notifyDataSetChanged();
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/AlbumsTabFragment.java b/src/com/android/providers/media/photopicker/ui/AlbumsTabFragment.java
new file mode 100644
index 0000000..3f946ca
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/AlbumsTabFragment.java
@@ -0,0 +1,93 @@
+/*
+ * 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 static com.android.providers.media.photopicker.ui.AlbumsTabAdapter.COLUMN_COUNT;
+
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+import androidx.recyclerview.widget.GridLayoutManager;
+
+import com.android.providers.media.R;
+import com.android.providers.media.photopicker.PhotoPickerActivity;
+import com.android.providers.media.photopicker.data.model.Category;
+import com.android.providers.media.photopicker.util.LayoutModeUtils;
+
+/**
+ * Albums tab fragment for showing the albums
+ */
+public class AlbumsTabFragment extends TabFragment {
+
+ private static final int MINIMUM_SPAN_COUNT = 2;
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ // Set the pane title for A11y.
+ view.setAccessibilityPaneTitle(getString(R.string.picker_albums));
+
+ setEmptyMessage(R.string.picker_albums_empty_message);
+
+ final AlbumsTabAdapter adapter = new AlbumsTabAdapter(mImageLoader, this::onItemClick,
+ mPickerViewModel.hasMimeTypeFilter());
+ mPickerViewModel.getCategories().observe(this, categoryList -> {
+ adapter.updateCategoryList(categoryList);
+ // Handle emptyView's visibility
+ updateVisibilityForEmptyView(/* shouldShowEmptyView */ categoryList.size() == 0);
+ });
+ final GridLayoutManager layoutManager = new GridLayoutManager(getContext(), COLUMN_COUNT);
+ final AlbumsTabItemDecoration itemDecoration = new AlbumsTabItemDecoration(
+ view.getContext());
+
+ final int spacing = getResources().getDimensionPixelSize(R.dimen.picker_album_item_spacing);
+ final int albumSize = getResources().getDimensionPixelSize(R.dimen.picker_album_size);
+ mRecyclerView.setColumnWidth(albumSize + spacing);
+ mRecyclerView.setMinimumSpanCount(MINIMUM_SPAN_COUNT);
+
+ mRecyclerView.setLayoutManager(layoutManager);
+ mRecyclerView.setAdapter(adapter);
+ mRecyclerView.addItemDecoration(itemDecoration);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ ((PhotoPickerActivity) getActivity()).updateCommonLayouts(LayoutModeUtils.MODE_ALBUMS_TAB,
+ /* title */ "");
+ }
+
+ private void onItemClick(@NonNull View view) {
+ final Category category = (Category) view.getTag();
+ PhotosTabFragment.show(getActivity().getSupportFragmentManager(), category);
+ }
+
+ /**
+ * Create the albums tab fragment and add it into the FragmentManager
+ *
+ * @param fm The fragment manager
+ */
+ public static void show(FragmentManager fm) {
+ final FragmentTransaction ft = fm.beginTransaction();
+ final AlbumsTabFragment fragment = new AlbumsTabFragment();
+ ft.replace(R.id.fragment_container, fragment);
+ ft.commitAllowingStateLoss();
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/providers/media/photopicker/ui/AlbumsTabItemDecoration.java b/src/com/android/providers/media/photopicker/ui/AlbumsTabItemDecoration.java
new file mode 100644
index 0000000..1ec4cd1
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/AlbumsTabItemDecoration.java
@@ -0,0 +1,73 @@
+/*
+ * 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.graphics.Rect;
+import android.view.View;
+
+import androidx.appcompat.widget.ViewUtils;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.providers.media.R;
+
+/**
+ * The ItemDecoration that allows adding layout offsets to specific item views from the adapter's
+ * data set for the {@link RecyclerView} on Albums tab.
+ */
+public class AlbumsTabItemDecoration extends RecyclerView.ItemDecoration {
+
+ private final int mSpacing;
+ private final int mTopSpacing;
+
+ public AlbumsTabItemDecoration(Context context) {
+ mSpacing = context.getResources().getDimensionPixelSize(R.dimen.picker_album_item_spacing);
+ mTopSpacing = context.getResources().getDimensionPixelSize(
+ R.dimen.picker_album_item_top_spacing);
+ }
+
+ @Override
+ public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
+ RecyclerView.State state) {
+ final GridLayoutManager.LayoutParams lp =
+ (GridLayoutManager.LayoutParams) view.getLayoutParams();
+ final GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
+ final int column = lp.getSpanIndex();
+ final int spanCount = layoutManager.getSpanCount();
+
+ final int adapterPosition = parent.getChildAdapterPosition(view);
+ // the top gap of the album items on the first row is mSpacing
+ if (adapterPosition < spanCount) {
+ outRect.top = mSpacing;
+ } else {
+ outRect.top = mTopSpacing;
+ }
+
+ // spacing - column * ((1f / spanCount) * spacing)
+ final int start = mSpacing - column * mSpacing / spanCount;
+ // (column + 1) * ((1f / spanCount) * spacing)
+ final int end = (column + 1) * mSpacing / spanCount;
+ if (ViewUtils.isLayoutRtl(parent)) {
+ outRect.left = end;
+ outRect.right = start;
+ } else {
+ outRect.left = start;
+ outRect.right = end;
+ }
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/AutoFitRecyclerView.java b/src/com/android/providers/media/photopicker/ui/AutoFitRecyclerView.java
new file mode 100644
index 0000000..3df4357
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/AutoFitRecyclerView.java
@@ -0,0 +1,75 @@
+/*
+ * 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.util.AttributeSet;
+
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * The AutoFitRecyclerView auto fits the column width to decide the span count
+ */
+public class AutoFitRecyclerView extends RecyclerView {
+
+ private int mColumnWidth = -1;
+ private int mMinimumSpanCount = 2;
+ private boolean mIsGridLayout;
+
+ public AutoFitRecyclerView(Context context) {
+ super(context);
+ }
+
+ public AutoFitRecyclerView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public AutoFitRecyclerView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ if (mIsGridLayout && mColumnWidth > 0) {
+ final int spanCount = Math.max(mMinimumSpanCount,
+ Math.round((float) getMeasuredWidth() / mColumnWidth));
+ ((GridLayoutManager) getLayoutManager()).setSpanCount(spanCount);
+ }
+ }
+
+ @Override
+ public void setLayoutManager(@Nullable RecyclerView.LayoutManager layoutManager) {
+ super.setLayoutManager(layoutManager);
+ mIsGridLayout = (layoutManager instanceof GridLayoutManager);
+ }
+
+ public void setColumnWidth(int columnWidth) {
+ mColumnWidth = columnWidth;
+ }
+
+ /**
+ * Set the minimum span count for the recyclerView.
+ * @param minimumSpanCount The default value is 2.
+ */
+ public void setMinimumSpanCount(int minimumSpanCount) {
+ mMinimumSpanCount = minimumSpanCount;
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/BaseViewHolder.java b/src/com/android/providers/media/photopicker/ui/BaseViewHolder.java
new file mode 100644
index 0000000..db8c0c1
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/BaseViewHolder.java
@@ -0,0 +1,47 @@
+/*
+ * 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.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * ViewHolder of a item within a {@link RecyclerView.Adapter}.
+ */
+public abstract class BaseViewHolder extends RecyclerView.ViewHolder {
+
+ public BaseViewHolder(@NonNull Context context, @NonNull ViewGroup parent, int layout) {
+ this(context, inflateLayout(context, parent, layout));
+ }
+
+ public BaseViewHolder(@NonNull Context context, @NonNull View view) {
+ super(view);
+ }
+
+ private static <V extends View> V inflateLayout(@NonNull Context context,
+ @NonNull ViewGroup parent, int layout) {
+ final LayoutInflater inflater = LayoutInflater.from(context);
+ return (V) inflater.inflate(layout, parent, false);
+ }
+
+ public abstract void bind();
+}
diff --git a/src/com/android/providers/media/photopicker/ui/DateHeaderHolder.java b/src/com/android/providers/media/photopicker/ui/DateHeaderHolder.java
new file mode 100644
index 0000000..0c6dc4a
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/DateHeaderHolder.java
@@ -0,0 +1,47 @@
+/*
+ * 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.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import com.android.providers.media.R;
+import com.android.providers.media.photopicker.util.DateTimeUtils;
+import com.android.providers.media.photopicker.data.model.Item;
+
+/**
+ * ViewHolder of a date header within a RecyclerView.
+ */
+public class DateHeaderHolder extends BaseViewHolder {
+ private TextView mTitle;
+ public DateHeaderHolder(@NonNull Context context, @NonNull ViewGroup parent) {
+ super(context, parent, R.layout.item_date_header);
+ mTitle = itemView.findViewById(R.id.date_header_title);
+ }
+
+ @Override
+ public void bind() {
+ final Item item = (Item) itemView.getTag();
+ final long dateTaken = item.getDateTaken();
+ if (dateTaken == 0) {
+ mTitle.setText(R.string.recent);
+ } else {
+ mTitle.setText(DateTimeUtils.getDateHeaderString(dateTaken));
+ }
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/DevicePolicyResources.java b/src/com/android/providers/media/photopicker/ui/DevicePolicyResources.java
new file mode 100644
index 0000000..383a260
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/DevicePolicyResources.java
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+package com.android.providers.media.photopicker.ui;
+
+import android.app.admin.DevicePolicyManager;
+
+/**
+ * Class containing the required identifiers to update device management resources.
+ *
+ * <p>See {@link DevicePolicyManager#getDrawable} and {@link DevicePolicyManager#getString}.
+ */
+public class DevicePolicyResources {
+
+ /**
+ * Class containing the identifiers used to update device management-related system strings.
+ */
+ public static final class Strings {
+ private static final String PREFIX = "MediaProvider.";
+
+ /**
+ * The text shown to switch to the work profile in PhotoPicker.
+ */
+ public static final String SWITCH_TO_WORK_MESSAGE =
+ PREFIX + "SWITCH_TO_WORK_MESSAGE";
+
+ /**
+ * The text shown to switch to the personal profile in PhotoPicker.
+ */
+ public static final String SWITCH_TO_PERSONAL_MESSAGE =
+ PREFIX + "SWITCH_TO_PERSONAL_MESSAGE";
+
+ /**
+ * The title for error dialog in PhotoPicker when the admin blocks cross user
+ * interaction for the intent.
+ */
+ public static final String BLOCKED_BY_ADMIN_TITLE =
+ PREFIX + "BLOCKED_BY_ADMIN_TITLE";
+
+ /**
+ * The message for error dialog in PhotoPicker when the admin blocks cross user
+ * interaction from the personal profile.
+ */
+ public static final String BLOCKED_FROM_PERSONAL_MESSAGE =
+ PREFIX + "BLOCKED_FROM_PERSONAL_MESSAGE";
+
+ /**
+ * The message for error dialog in PhotoPicker when the admin blocks cross user
+ * interaction from the work profile.
+ */
+ public static final String BLOCKED_FROM_WORK_MESSAGE =
+ PREFIX + "BLOCKED_FROM_WORK_MESSAGE";
+
+ /**
+ * The title of the error dialog in PhotoPicker when the user tries to switch to work
+ * content, but work profile is off.
+ */
+ public static final String WORK_PROFILE_PAUSED_TITLE =
+ PREFIX + "WORK_PROFILE_PAUSED_TITLE";
+
+ /**
+ * The message of the error dialog in PhotoPicker when the user tries to switch to work
+ * content, but work profile is off.
+ */
+ public static final String WORK_PROFILE_PAUSED_MESSAGE =
+ PREFIX + "WORK_PROFILE_PAUSED_MESSAGE";
+ }
+
+ /**
+ * Class containing the identifiers used to update device management-related system drawable.
+ */
+ public static final class Drawables {
+ /**
+ * General purpose work profile icon (i.e. generic icon badging).
+ */
+ public static final String WORK_PROFILE_ICON = "WORK_PROFILE_ICON";
+
+ /**
+ * Class containing the style identifiers used to update device management-related system
+ * drawable.
+ */
+ public static final class Style {
+ /**
+ * A style identifier indicating that the updatable drawable is an outline.
+ */
+ public static final String OUTLINE = "OUTLINE";
+ }
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/ExoPlayerWrapper.java b/src/com/android/providers/media/photopicker/ui/ExoPlayerWrapper.java
new file mode 100644
index 0000000..fd891a4
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/ExoPlayerWrapper.java
@@ -0,0 +1,264 @@
+/*
+ * 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.AudioManager;
+import android.net.Uri;
+import android.util.Log;
+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 final Context mContext;
+ private final MuteStatus mMuteStatus;
+ private ExoPlayer mExoPlayer;
+ private boolean mIsPlayerReleased = true;
+ private boolean mShouldShowControlsForNext = true;
+ private boolean mIsAccessibilityEnabled = false;
+
+ 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)).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);
+ final boolean isVolumeMuted = mMuteStatus.isVolumeMuted();
+ if (isVolumeMuted) {
+ // If the previous volume was muted, set the volume status to mute.
+ mExoPlayer.setVolume(0f);
+ }
+ updateMuteButtonState(muteButton, isVolumeMuted);
+
+ // Add click listeners for mute button
+ muteButton.setOnClickListener(v -> {
+ if (mMuteStatus.isVolumeMuted()) {
+ AudioManager audioManager = mContext.getSystemService(AudioManager.class);
+ if (audioManager == null) {
+ Log.e(TAG, "Couldn't find AudioManager while trying to set volume,"
+ + " unable to set volume");
+ return;
+ }
+ mExoPlayer.setVolume(audioManager.getStreamVolume(AudioManager.STREAM_MUSIC));
+ mMuteStatus.setVolumeMuted(false);
+ } else {
+ mExoPlayer.setVolume(0f);
+ mMuteStatus.setVolumeMuted(true);
+ }
+ updateMuteButtonState(muteButton, mMuteStatus.isVolumeMuted());
+ });
+ }
+
+ 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 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();
+ mIsPlayerReleased = true;
+ }
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/ImageLoader.java b/src/com/android/providers/media/photopicker/ui/ImageLoader.java
new file mode 100644
index 0000000..d629471
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/ImageLoader.java
@@ -0,0 +1,156 @@
+/*
+ * 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.graphics.ImageDecoder;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.provider.CloudMediaProviderContract;
+import android.provider.MediaStore;
+import android.util.Log;
+import android.widget.ImageView;
+
+import androidx.annotation.NonNull;
+
+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.load.Option;
+import com.bumptech.glide.request.RequestOptions;
+import com.bumptech.glide.signature.ObjectKey;
+
+/**
+ * A class to assist with loading and managing the Images (i.e. thumbnails and preview) associated
+ * with item.
+ */
+public class ImageLoader {
+
+ public static final Option<Boolean> THUMBNAIL_REQUEST =
+ Option.memory(CloudMediaProviderContract.EXTRA_MEDIASTORE_THUMB, false);
+ private static final String TAG = "ImageLoader";
+ private final Context mContext;
+
+ public ImageLoader(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Load the thumbnail of the {@code category} and set it on the {@code imageView}
+ *
+ * @param category the album
+ * @param imageView the imageView shows the thumbnail
+ */
+ public void loadAlbumThumbnail(@NonNull Category category, @NonNull ImageView imageView) {
+ // 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);
+ }
+
+ /**
+ * Load the thumbnail of the photo item {@code item} and set it on the {@code imageView}
+ *
+ * @param item the photo item
+ * @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);
+ }
+
+ /**
+ * Load the image of the photo item {@code item} and set it on the {@code imageView}
+ *
+ * @param item the photo item
+ * @param imageView the imageView shows the image
+ */
+ 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);
+ return;
+ }
+
+ if (item.isAnimatedWebp()) {
+ loadAnimatedWebpPreview(item, imageView);
+ return;
+ }
+
+ // Preview as bitmap image for all other image types
+ Glide.with(mContext)
+ .asBitmap()
+ .load(item.getContentUri())
+ .signature(getGlideSignature(item, /* prefix */ ""))
+ .into(imageView);
+ }
+
+ private void loadAnimatedWebpPreview(@NonNull Item item, @NonNull ImageView imageView) {
+ final Uri uri = item.getContentUri();
+ final ImageDecoder.Source source = ImageDecoder.createSource(mContext.getContentResolver(),
+ uri);
+ Drawable drawable = null;
+ try {
+ drawable = ImageDecoder.decodeDrawable(source);
+ } catch (Exception e) {
+ Log.d(TAG, "Failed to decode drawable for uri: " + uri, e);
+ }
+
+ // 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);
+ }
+
+ /**
+ * 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);
+ }
+
+ private ObjectKey getGlideSignature(Item item, String prefix) {
+ // TODO(b/224725723): Remove media store version from key once MP ids are stable.
+ return new ObjectKey(
+ MediaStore.getVersion(mContext) + prefix + item.getContentUri().toString() +
+ item.getGenerationModified());
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/PhotoGridHolder.java b/src/com/android/providers/media/photopicker/ui/PhotoGridHolder.java
new file mode 100644
index 0000000..99d06d7
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/PhotoGridHolder.java
@@ -0,0 +1,90 @@
+/*
+ * 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.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import com.android.providers.media.R;
+import com.android.providers.media.photopicker.data.model.Item;
+
+/**
+ * ViewHolder of a photo item within a RecyclerView.
+ */
+public class PhotoGridHolder extends BaseViewHolder {
+
+ private final ImageLoader mImageLoader;
+ private final ImageView mIconThumb;
+ private final ImageView mIconGif;
+ private final ImageView mIconMotionPhoto;
+ private final View mVideoBadgeContainer;
+ private final TextView mVideoDuration;
+ private final View mOverlayGradient;
+ private final boolean mCanSelectMultiple;
+
+ public PhotoGridHolder(@NonNull Context context, @NonNull ViewGroup parent,
+ @NonNull ImageLoader imageLoader, boolean canSelectMultiple) {
+ super(context, parent, R.layout.item_photo_grid);
+
+ mIconThumb = itemView.findViewById(R.id.icon_thumbnail);
+ mIconGif = itemView.findViewById(R.id.icon_gif);
+ mIconMotionPhoto = itemView.findViewById(R.id.icon_motion_photo);
+ mVideoBadgeContainer = itemView.findViewById(R.id.video_container);
+ mVideoDuration = mVideoBadgeContainer.findViewById(R.id.video_duration);
+ mOverlayGradient = itemView.findViewById(R.id.overlay_gradient);
+ mImageLoader = imageLoader;
+ final ImageView iconCheck = itemView.findViewById(R.id.icon_check);
+ mCanSelectMultiple = canSelectMultiple;
+ if (mCanSelectMultiple) {
+ iconCheck.setVisibility(View.VISIBLE);
+ } else {
+ iconCheck.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public void bind() {
+ final Item item = (Item) itemView.getTag();
+ mImageLoader.loadPhotoThumbnail(item, mIconThumb);
+
+ mIconGif.setVisibility(item.isGifOrAnimatedWebp() ? View.VISIBLE : View.GONE);
+ mIconMotionPhoto.setVisibility(item.isMotionPhoto() ? View.VISIBLE : View.GONE);
+
+ if (item.isVideo()) {
+ mVideoBadgeContainer.setVisibility(View.VISIBLE);
+ mVideoDuration.setText(item.getDurationText());
+ } else {
+ mVideoBadgeContainer.setVisibility(View.GONE);
+ }
+
+ if (showShowOverlayGradient(item)) {
+ mOverlayGradient.setVisibility(View.VISIBLE);
+ } else {
+ mOverlayGradient.setVisibility(View.GONE);
+ }
+ }
+
+ private boolean showShowOverlayGradient(@NonNull Item item) {
+ return mCanSelectMultiple || item.isGifOrAnimatedWebp() || item.isVideo() ||
+ item.isMotionPhoto();
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/PhotosTabAdapter.java b/src/com/android/providers/media/photopicker/ui/PhotosTabAdapter.java
new file mode 100644
index 0000000..f1bfdef
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/PhotosTabAdapter.java
@@ -0,0 +1,136 @@
+/*
+ * 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.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.providers.media.R;
+
+import com.android.providers.media.photopicker.data.Selection;
+import com.android.providers.media.photopicker.data.model.Item;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Adapts from model to something RecyclerView understands.
+ */
+public class PhotosTabAdapter extends RecyclerView.Adapter<BaseViewHolder> {
+
+ public static final int ITEM_TYPE_DATE_HEADER = 0;
+ private static final int ITEM_TYPE_PHOTO = 1;
+
+ public static final int COLUMN_COUNT = 3;
+
+ private List<Item> mItemList = new ArrayList<>();
+ private final ImageLoader mImageLoader;
+ private final View.OnClickListener mOnClickListener;
+ private final View.OnLongClickListener mOnLongClickListener;
+ private final Selection mSelection;
+
+ public PhotosTabAdapter(@NonNull Selection selection, @NonNull ImageLoader imageLoader,
+ @NonNull View.OnClickListener onClickListener,
+ @NonNull View.OnLongClickListener onLongClickListener) {
+ mImageLoader = imageLoader;
+ mSelection = selection;
+ mOnClickListener = onClickListener;
+ mOnLongClickListener = onLongClickListener;
+ }
+
+ @NonNull
+ @Override
+ public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
+ if (viewType == ITEM_TYPE_DATE_HEADER) {
+ return new DateHeaderHolder(viewGroup.getContext(), viewGroup);
+ }
+ return new PhotoGridHolder(viewGroup.getContext(), viewGroup, mImageLoader,
+ mSelection.canSelectMultiple());
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull BaseViewHolder itemHolder, int position) {
+ final Item item = getItem(position);
+ itemHolder.itemView.setTag(item);
+
+ if (getItemViewType(position) == ITEM_TYPE_PHOTO) {
+ itemHolder.itemView.setOnClickListener(mOnClickListener);
+ itemHolder.itemView.setOnLongClickListener(mOnLongClickListener);
+
+ final Context context = itemHolder.itemView.getContext();
+ itemHolder.itemView.setContentDescription(item.getContentDescription(context));
+
+ if (mSelection.canSelectMultiple()) {
+ final boolean isSelected = mSelection.isItemSelected(item);
+ itemHolder.itemView.setSelected(isSelected);
+
+ // There is an issue b/223695510 about not selected in Accessibility mode. It only
+ // says selected state, but it doesn't say not selected state. Add the not selected
+ // only to avoid that it says selected twice.
+ itemHolder.itemView.setStateDescription(
+ isSelected ? null : context.getString(R.string.not_selected));
+ }
+ }
+ itemHolder.bind();
+ }
+
+ @Override
+ public int getItemCount() {
+ return mItemList.size();
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (getItem(position).isDate()) {
+ return ITEM_TYPE_DATE_HEADER;
+ }
+ return ITEM_TYPE_PHOTO;
+ }
+
+ @NonNull
+ public Item getItem(int position) {
+ return mItemList.get(position);
+ }
+
+ public void updateItemList(@NonNull List<Item> itemList) {
+ mItemList = itemList;
+ notifyDataSetChanged();
+ }
+
+ @NonNull
+ public GridLayoutManager.SpanSizeLookup createSpanSizeLookup(
+ @NonNull GridLayoutManager layoutManager) {
+ return new GridLayoutManager.SpanSizeLookup() {
+ @Override
+ public int getSpanSize(int position) {
+ final int itemViewType = getItemViewType(position);
+ // For the item view type is ITEM_TYPE_DATE_HEADER, it is full
+ // span, return the span count of the layoutManager.
+ if (itemViewType == ITEM_TYPE_DATE_HEADER ) {
+ return layoutManager.getSpanCount();
+ } else {
+ return 1;
+ }
+ }
+ };
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/PhotosTabFragment.java b/src/com/android/providers/media/photopicker/ui/PhotosTabFragment.java
new file mode 100644
index 0000000..78a37a3
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/PhotosTabFragment.java
@@ -0,0 +1,213 @@
+/*
+ * 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 static com.android.providers.media.photopicker.ui.PhotosTabAdapter.COLUMN_COUNT;
+
+import android.os.Bundle;
+import android.provider.CloudMediaProviderContract;
+import android.text.TextUtils;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+import androidx.recyclerview.widget.GridLayoutManager;
+
+import com.android.providers.media.R;
+
+import com.android.providers.media.photopicker.PhotoPickerActivity;
+import com.android.providers.media.photopicker.data.model.Category;
+import com.android.providers.media.photopicker.data.model.Item;
+import com.android.providers.media.photopicker.util.LayoutModeUtils;
+import com.android.providers.media.util.StringUtils;
+
+import com.google.android.material.snackbar.Snackbar;
+
+import java.text.NumberFormat;
+import java.util.Locale;
+
+/**
+ * Photos tab fragment for showing the photos
+ */
+public class PhotosTabFragment extends TabFragment {
+
+ private static final int MINIMUM_SPAN_COUNT = 3;
+ private static final String FRAGMENT_TAG = "PhotosTabFragment";
+
+ private Category mCategory = Category.DEFAULT;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // After the configuration is changed, if the fragment is now shown, onViewCreated will not
+ // be triggered. We need to restore the savedInstanceState in onCreate.
+ // E.g. Click the albums -> preview one item -> rotate the device
+ if (savedInstanceState != null) {
+ mCategory = Category.fromBundle(savedInstanceState);
+ }
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ final PhotosTabAdapter adapter = new PhotosTabAdapter(mSelection, mImageLoader,
+ this::onItemClick, this::onItemLongClick);
+ setEmptyMessage(R.string.picker_photos_empty_message);
+
+ if (mCategory.isDefault()) {
+ // Set the pane title for A11y
+ view.setAccessibilityPaneTitle(getString(R.string.picker_photos));
+ mPickerViewModel.getItems().observe(this, itemList -> {
+ adapter.updateItemList(itemList);
+ // Handle emptyView's visibility
+ updateVisibilityForEmptyView(/* shouldShowEmptyView */ itemList.size() == 0);
+ });
+ } else {
+ // Set the pane title for A11y
+ view.setAccessibilityPaneTitle(mCategory.getDisplayName(getContext()));
+ mPickerViewModel.getCategoryItems(mCategory).observe(this, itemList -> {
+ // If the item count of the albums is zero, albums are not shown on the Albums tab.
+ // The user can't launch the album items page when the album has zero items. So, we
+ // don't need to show emptyView in the case.
+ updateVisibilityForEmptyView(/* shouldShowEmptyView */ false);
+
+ adapter.updateItemList(itemList);
+ });
+ }
+
+ final GridLayoutManager layoutManager = new GridLayoutManager(getContext(), COLUMN_COUNT);
+ final GridLayoutManager.SpanSizeLookup lookup = adapter.createSpanSizeLookup(layoutManager);
+ layoutManager.setSpanSizeLookup(lookup);
+
+ final PhotosTabItemDecoration itemDecoration = new PhotosTabItemDecoration(
+ view.getContext());
+
+ final int spacing = getResources().getDimensionPixelSize(R.dimen.picker_photo_item_spacing);
+ final int photoSize = getResources().getDimensionPixelSize(R.dimen.picker_photo_size);
+ mRecyclerView.setColumnWidth(photoSize + spacing);
+ mRecyclerView.setMinimumSpanCount(MINIMUM_SPAN_COUNT);
+
+ mRecyclerView.setLayoutManager(layoutManager);
+ mRecyclerView.setAdapter(adapter);
+ mRecyclerView.addItemDecoration(itemDecoration);
+ }
+
+ /**
+ * Called when owning activity is saving state to be used to restore state during creation.
+ *
+ * @param state Bundle to save state
+ */
+ public void onSaveInstanceState(Bundle state) {
+ super.onSaveInstanceState(state);
+ mCategory.toBundle(state);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ if (mCategory.isDefault()) {
+ ((PhotoPickerActivity) getActivity()).updateCommonLayouts(
+ LayoutModeUtils.MODE_PHOTOS_TAB, /* title */ "");
+ hideProfileButton(/* hide */ false);
+ } else {
+ hideProfileButton(/* hide */ true);
+ ((PhotoPickerActivity) getActivity()).updateCommonLayouts(
+ LayoutModeUtils.MODE_ALBUM_PHOTOS_TAB, mCategory.getDisplayName(getContext()));
+ }
+ }
+
+ private void onItemClick(@NonNull View view) {
+ if (mSelection.canSelectMultiple()) {
+ final boolean isSelectedBefore = view.isSelected();
+
+ if (isSelectedBefore) {
+ mSelection.removeSelectedItem((Item) view.getTag());
+ } else {
+ if (!mSelection.isSelectionAllowed()) {
+ final int maxCount = mSelection.getMaxSelectionLimit();
+ final CharSequence quantityText =
+ StringUtils.getICUFormatString(
+ getResources(), maxCount, R.string.select_up_to);
+ final String itemCountString = NumberFormat.getInstance(Locale.getDefault())
+ .format(maxCount);
+ final CharSequence message = TextUtils.expandTemplate(quantityText,
+ itemCountString);
+ Snackbar.make(view, message, Snackbar.LENGTH_SHORT).show();
+ return;
+ } else {
+ mSelection.addSelectedItem((Item) view.getTag());
+ }
+ }
+ view.setSelected(!isSelectedBefore);
+ // There is an issue b/223695510 about not selected in Accessibility mode. It only says
+ // selected state, but it doesn't say not selected state. Add the not selected only to
+ // avoid that it says selected twice.
+ view.setStateDescription(isSelectedBefore ? getString(R.string.not_selected) : null);
+ } else {
+ Item item = (Item) view.getTag();
+ mSelection.setSelectedItem(item);
+ ((PhotoPickerActivity) getActivity()).setResultAndFinishSelf();
+ }
+ }
+
+ private boolean onItemLongClick(@NonNull View view) {
+ Item item = (Item) view.getTag();
+ if (!mSelection.canSelectMultiple()) {
+ // In single select mode, if the item is previewed, we set it as selected item. This is
+ // will assist in "Add" button click to return all selected items.
+ // For multi select, long click only previews the item, and until user selects the item,
+ // it doesn't get added to selected items. Also, there is no "Add" button in the preview
+ // layout that can return selected items.
+ mSelection.setSelectedItem(item);
+ }
+ mSelection.prepareItemForPreviewOnLongPress(item);
+ // Transition to PreviewFragment.
+ PreviewFragment.show(getActivity().getSupportFragmentManager(),
+ PreviewFragment.getArgsForPreviewOnLongPress());
+ return true;
+ }
+
+ /**
+ * Create the fragment with the category and add it into the FragmentManager
+ *
+ * @param fm the fragment manager
+ * @param category the category
+ */
+ public static void show(FragmentManager fm, Category category) {
+ final FragmentTransaction ft = fm.beginTransaction();
+ final PhotosTabFragment fragment = new PhotosTabFragment();
+ fragment.mCategory = category;
+ ft.replace(R.id.fragment_container, fragment, FRAGMENT_TAG);
+ if (!fragment.mCategory.isDefault()) {
+ ft.addToBackStack(FRAGMENT_TAG);
+ }
+ ft.commitAllowingStateLoss();
+ }
+
+ /**
+ * Get the fragment in the FragmentManager
+ *
+ * @param fm The fragment manager
+ */
+ public static Fragment get(FragmentManager fm) {
+ return fm.findFragmentByTag(FRAGMENT_TAG);
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/PhotosTabItemDecoration.java b/src/com/android/providers/media/photopicker/ui/PhotosTabItemDecoration.java
new file mode 100644
index 0000000..4d8e873
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/PhotosTabItemDecoration.java
@@ -0,0 +1,81 @@
+/*
+ * 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 static com.android.providers.media.photopicker.ui.PhotosTabAdapter.ITEM_TYPE_DATE_HEADER;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.view.View;
+
+import androidx.appcompat.widget.ViewUtils;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.providers.media.R;
+
+/**
+ * The ItemDecoration that allows to add layout offsets to specific item views from the adapter's
+ * data set for the {@link RecyclerView} on Photos tab.
+ */
+public class PhotosTabItemDecoration extends RecyclerView.ItemDecoration {
+
+ private final int mSpacing;
+
+ public PhotosTabItemDecoration(Context context) {
+ mSpacing = context.getResources().getDimensionPixelSize(R.dimen.picker_photo_item_spacing);
+ }
+
+ @Override
+ public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
+ RecyclerView.State state) {
+ final GridLayoutManager.LayoutParams lp =
+ (GridLayoutManager.LayoutParams) view.getLayoutParams();
+ final GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
+ final int column = lp.getSpanIndex();
+ final int spanCount = layoutManager.getSpanCount();
+
+ // The date header doesn't have spacing
+ if (lp.getSpanSize() == spanCount) {
+ outRect.set(0, 0, 0, 0);
+ return;
+ }
+
+ final int adapterPosition = parent.getChildAdapterPosition(view);
+ if (adapterPosition > column) {
+ final int itemViewType = parent.getAdapter().getItemViewType(
+ adapterPosition - column - 1);
+ // if the above item is not a date header, add the top spacing
+ if (itemViewType != ITEM_TYPE_DATE_HEADER) {
+ outRect.top = mSpacing;
+ }
+ }
+
+ // column * ((1f / spanCount) * spacing)
+ final int start = column * mSpacing / spanCount;
+ // spacing - (column + 1) * ((1f / spanCount) * spacing)
+ final int end = mSpacing - (column + 1) * mSpacing / spanCount;
+
+ if (ViewUtils.isLayoutRtl(parent)) {
+ outRect.left = end;
+ outRect.right = start;
+ } else {
+ outRect.left = start;
+ outRect.right = end;
+ }
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/PlaybackHandler.java b/src/com/android/providers/media/photopicker/ui/PlaybackHandler.java
new file mode 100644
index 0000000..7d31af9
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/PlaybackHandler.java
@@ -0,0 +1,124 @@
+/*
+ * 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
new file mode 100644
index 0000000..8ce7311
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/PreviewAdapter.java
@@ -0,0 +1,141 @@
+/*
+ * 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.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.providers.media.photopicker.data.MuteStatus;
+import com.android.providers.media.photopicker.data.model.Item;
+import com.android.providers.media.photopicker.ui.remotepreview.RemotePreviewHandler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Adapter for Preview RecyclerView to preview all images and videos.
+ */
+class PreviewAdapter extends RecyclerView.Adapter<BaseViewHolder> {
+
+ private static final int ITEM_TYPE_IMAGE = 1;
+ private static final int ITEM_TYPE_VIDEO = 2;
+
+ 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
+ @Override
+ 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);
+ }
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) {
+ final Item item = getItem(position);
+ holder.itemView.setContentDescription(
+ item.getContentDescription(holder.itemView.getContext()));
+ holder.itemView.setTag(item);
+ holder.bind();
+ }
+
+ @Override
+ public void onViewAttachedToWindow(@NonNull BaseViewHolder holder) {
+ super.onViewAttachedToWindow(holder);
+
+ 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);
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return mItemList.size();
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (mItemList.get(position).isVideo()) {
+ return ITEM_TYPE_VIDEO;
+ }
+ // Everything other than video mimeType are previewed using PreviewImageHolder. This also
+ // includes GIF which uses Glide to load image.
+ return ITEM_TYPE_IMAGE;
+ }
+
+ void onHandlePageSelected(View itemView) {
+ if (mIsRemotePreviewEnabled) {
+ final Item item = (Item) itemView.getTag();
+ mRemotePreviewHandler.onHandlePageSelected(item);
+ return;
+ }
+
+ mPlaybackHandler.handleVideoPlayback(itemView);
+ }
+
+ void onStop() {
+ if (mIsRemotePreviewEnabled) {
+ mRemotePreviewHandler.onStop();
+ return;
+ }
+
+ mPlaybackHandler.releaseResources();
+ }
+
+ void onDestroy() {
+ if (mIsRemotePreviewEnabled) {
+ mRemotePreviewHandler.onDestroy();
+ }
+ }
+
+ Item getItem(int position) {
+ return mItemList.get(position);
+ }
+
+ void updateItemList(List<Item> itemList) {
+ mItemList = itemList;
+ notifyDataSetChanged();
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/PreviewFragment.java b/src/com/android/providers/media/photopicker/ui/PreviewFragment.java
new file mode 100644
index 0000000..58bc781
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/PreviewFragment.java
@@ -0,0 +1,386 @@
+/*
+ * 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.content.res.Configuration;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.viewpager2.widget.ViewPager2;
+
+import com.android.providers.media.R;
+import com.android.providers.media.photopicker.PhotoPickerActivity;
+import com.android.providers.media.photopicker.data.MuteStatus;
+import com.android.providers.media.photopicker.data.Selection;
+import com.android.providers.media.photopicker.data.model.Item;
+import com.android.providers.media.photopicker.util.LayoutModeUtils;
+import com.android.providers.media.photopicker.viewmodel.PickerViewModel;
+
+import java.text.NumberFormat;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Displays a selected items in one up view. Supports deselecting items.
+ */
+public class PreviewFragment extends Fragment {
+ private static String TAG = "PreviewFragment";
+
+ private static final String PREVIEW_TYPE = "preview_type";
+ private static final int PREVIEW_ON_LONG_PRESS = 1;
+ private static final int PREVIEW_ON_VIEW_SELECTED = 2;
+
+ private static final Bundle sPreviewOnLongPressArgs = new Bundle();
+ static {
+ sPreviewOnLongPressArgs.putInt(PREVIEW_TYPE, PREVIEW_ON_LONG_PRESS);
+ }
+ private static final Bundle sPreviewOnViewSelectedArgs = new Bundle();
+ static {
+ sPreviewOnViewSelectedArgs.putInt(PREVIEW_TYPE, PREVIEW_ON_VIEW_SELECTED);
+ }
+
+ private Selection mSelection;
+ private ViewPager2Wrapper mViewPager2Wrapper;
+ private boolean mShouldShowGifBadge;
+ private boolean mShouldShowMotionPhotoBadge;
+ private MuteStatus mMuteStatus;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // Register with the activity to inform the system that the app bar fragment is
+ // participating in the population of the options menu
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
+ inflater.inflate(R.menu.picker_preview_menu, menu);
+ }
+
+ @Override
+ public void onPrepareOptionsMenu(@NonNull Menu menu) {
+ super.onPrepareOptionsMenu(menu);
+ // All logic to hide/show an item in the menu must be in this method
+ final MenuItem gifItem = menu.findItem(R.id.preview_gif);
+ final MenuItem motionPhotoItem = menu.findItem(R.id.preview_motion_photo);
+ gifItem.setVisible(mShouldShowGifBadge);
+ motionPhotoItem.setVisible(mShouldShowMotionPhotoBadge);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup parent,
+ Bundle savedInstanceState) {
+ mSelection = new ViewModelProvider(requireActivity()).get(PickerViewModel.class)
+ .getSelection();
+ mMuteStatus = new ViewModelProvider(requireActivity()).get(PickerViewModel.class)
+ .getMuteStatus();
+ return inflater.inflate(R.layout.fragment_preview, parent, /* attachToRoot */ false);
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ // Set the pane title for A11y.
+ view.setAccessibilityPaneTitle(getString(R.string.picker_preview));
+ final List<Item> selectedItemsList = mSelection.getSelectedItemsForPreview();
+ final int selectedItemsListSize = selectedItemsList.size();
+
+ if (selectedItemsListSize <= 0) {
+ // This can happen if we lost PickerViewModel to optimize memory.
+ Log.e(TAG, "No items to preview. Returning back to photo grid");
+ requireActivity().getSupportFragmentManager().popBackStack();
+ } else if (selectedItemsListSize > 1 && !mSelection.canSelectMultiple()) {
+ // This should never happen
+ throw new IllegalStateException("Found more than one preview items in single select"
+ + " mode. Selected items count: " + selectedItemsListSize);
+ }
+
+ // Initialize ViewPager2 to swipe between multiple pictures/videos in preview
+ final ViewPager2 viewPager = view.findViewById(R.id.preview_viewPager);
+ if (viewPager == null) {
+ throw new IllegalStateException("Expected to find ViewPager2 in " + view
+ + ", but found null");
+ }
+ mViewPager2Wrapper = new ViewPager2Wrapper(viewPager, selectedItemsList, mMuteStatus);
+
+ setUpPreviewLayout(view, getArguments());
+ setupScrimLayerAndBottomBar(view);
+ }
+
+ private void setupScrimLayerAndBottomBar(View fragmentView) {
+ final boolean isLandscape = getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE;
+
+ // Show the scrim layers in Landscape mode. The default visibility is GONE.
+ if (isLandscape) {
+ final View topScrim = fragmentView.findViewById(R.id.preview_top_scrim);
+ topScrim.setVisibility(View.VISIBLE);
+
+ final View bottomScrim = fragmentView.findViewById(R.id.preview_bottom_scrim);
+ bottomScrim.setVisibility(View.VISIBLE);
+ }
+
+ // Set appropriate background color for the bottom bar
+ final int bottomBarColor;
+ if (isLandscape) {
+ bottomBarColor = Color.TRANSPARENT;
+ } else {
+ bottomBarColor = getContext().getColor(R.color.preview_scrim_solid_color);
+ }
+ final View bottomBar = fragmentView.findViewById(R.id.preview_bottom_bar);
+ bottomBar.setBackgroundColor(bottomBarColor);
+ }
+
+ private void setUpPreviewLayout(@NonNull View view, @Nullable Bundle args) {
+ if (args == null) {
+ // We are willing to crash PhotoPickerActivity because this error might only happen
+ // during development.
+ throw new IllegalArgumentException("Can't determine the type of the Preview, arguments"
+ + " is not set");
+ }
+
+ final int previewType = args.getInt(PREVIEW_TYPE, -1);
+ if (previewType == PREVIEW_ON_LONG_PRESS) {
+ setUpPreviewLayoutForLongPress(view);
+ } else if (previewType == PREVIEW_ON_VIEW_SELECTED) {
+ setUpPreviewLayoutForViewSelected(view);
+ } else {
+ // We are willing to crash PhotoPickerActivity because this error might only happen
+ // during development.
+ throw new IllegalArgumentException("No preview type specified");
+ }
+ }
+
+ /**
+ * Adjusts the select/add button layout for preview on LongPress
+ */
+ private void setUpPreviewLayoutForLongPress(@NonNull View view) {
+ final Button addOrSelectButton = view.findViewById(R.id.preview_add_or_select_button);
+
+ // Preview on Long Press will reuse AddOrSelect button as
+ // * Add button - Button with text "Add" - for single select mode
+ // * Select button - Button with text "Select"/"Deselect" based on the selection state of
+ // the item - for multi select mode
+ if (!mSelection.canSelectMultiple()) {
+ // On clicking add button we return the picker result to calling app.
+ // This destroys PickerActivity and all fragments.
+ addOrSelectButton.setOnClickListener(v -> {
+ ((PhotoPickerActivity) getActivity()).setResultAndFinishSelf();
+ });
+ } else {
+ // For preview on long press, we always preview only one item.
+ // Selection#getSelectedItemsForPreview is guaranteed to return only one item. Hence,
+ // we can always use position=0 as current position.
+ updateSelectButtonText(addOrSelectButton,
+ mSelection.isItemSelected(mViewPager2Wrapper.getItemAt(/* position */ 0)));
+ addOrSelectButton.setOnClickListener(v -> onClickSelectButton(addOrSelectButton));
+ }
+
+ // Set the appropriate special format icon based on the item in the preview
+ updateSpecialFormatIcon(mViewPager2Wrapper.getItemAt(/* position */ 0));
+ }
+
+ /**
+ * Adjusts the layout based on Multi select and adds appropriate onClick listeners
+ */
+ private void setUpPreviewLayoutForViewSelected(@NonNull View view) {
+ // Hide addOrSelect button of long press, we have a separate add button for view selected
+ final Button addOrSelectButton = view.findViewById(R.id.preview_add_or_select_button);
+ addOrSelectButton.setVisibility(View.GONE);
+
+ final Button viewSelectedAddButton = view.findViewById(R.id.preview_add_button);
+ viewSelectedAddButton.setVisibility(View.VISIBLE);
+ // On clicking add button we return the picker result to calling app.
+ // This destroys PickerActivity and all fragments.
+ viewSelectedAddButton.setOnClickListener(v -> {
+ ((PhotoPickerActivity) getActivity()).setResultAndFinishSelf();
+ });
+
+ final Button selectedCheckButton = view.findViewById(R.id.preview_selected_check_button);
+ selectedCheckButton.setVisibility(View.VISIBLE);
+ // Update the select icon and text according to the state of selection while swiping
+ // between photos
+ mViewPager2Wrapper.addOnPageChangeCallback(new OnPageChangeCallback(selectedCheckButton));
+
+ // Update add button text to include number of items selected.
+ mSelection.getSelectedItemCount().observe(this, selectedItemCount -> {
+ viewSelectedAddButton.setText(generateAddButtonString(getContext(), selectedItemCount));
+ });
+
+ selectedCheckButton.setOnClickListener(
+ v -> onClickSelectedCheckButton(selectedCheckButton));
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ ((PhotoPickerActivity) getActivity()).updateCommonLayouts(LayoutModeUtils.MODE_PREVIEW,
+ /* title */"");
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+
+ if (mViewPager2Wrapper != null) {
+ mViewPager2Wrapper.onStop();
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ if (mViewPager2Wrapper != null) {
+ mViewPager2Wrapper.onStart();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (mViewPager2Wrapper != null) {
+ mViewPager2Wrapper.onDestroy();
+ }
+ }
+
+ private void onClickSelectButton(@NonNull Button selectButton) {
+ final boolean isSelectedNow = updateSelectionAndGetState();
+ updateSelectButtonText(selectButton, isSelectedNow);
+ }
+
+ private void onClickSelectedCheckButton(@NonNull Button selectedCheckButton) {
+ final boolean isSelectedNow = updateSelectionAndGetState();
+ updateSelectedCheckButtonStateAndText(selectedCheckButton, isSelectedNow);
+ }
+
+ private boolean updateSelectionAndGetState() {
+ final Item currentItem = mViewPager2Wrapper.getCurrentItem();
+ final boolean wasSelectedBefore = mSelection.isItemSelected(currentItem);
+
+ if (wasSelectedBefore) {
+ // If the item is previously selected, current user action is to deselect the item
+ mSelection.removeSelectedItem(currentItem);
+ } else {
+ // If the item is not previously selected, current user action is to select the item
+ mSelection.addSelectedItem(currentItem);
+ }
+
+ // After the user has clicked the button, current state of the button should be opposite of
+ // the previous state.
+ // If the previous state was to "Select" the item, and user clicks "Select" button,
+ // wasSelectedBefore = false. And item will be added to selected items. Now, user can only
+ // deselect the item. Hence, isSelectedNow is opposite of previous state,
+ // i.e., isSelectedNow = true.
+ return !wasSelectedBefore;
+ }
+
+ private class OnPageChangeCallback extends ViewPager2.OnPageChangeCallback {
+ private final Button mSelectedCheckButton;
+
+ public OnPageChangeCallback(@NonNull Button selectedCheckButton) {
+ mSelectedCheckButton = selectedCheckButton;
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ // No action to take as we don't have deselect view here.
+ if (!mSelection.canSelectMultiple()) return;
+
+ final Item item = mViewPager2Wrapper.getItemAt(position);
+ // Set the appropriate select/deselect state for each item in each page based on the
+ // selection list.
+ updateSelectedCheckButtonStateAndText(mSelectedCheckButton,
+ mSelection.isItemSelected(item));
+
+ // Set the appropriate special format icon based on the item in the preview
+ updateSpecialFormatIcon(item);
+ }
+ }
+
+ private static void updateSelectButtonText(@NonNull Button selectButton,
+ boolean isSelected) {
+ selectButton.setText(isSelected ? R.string.deselect : R.string.select);
+ }
+
+ private static void updateSelectedCheckButtonStateAndText(@NonNull Button selectedCheckButton,
+ boolean isSelected) {
+ selectedCheckButton.setText(isSelected ? R.string.selected : R.string.deselected);
+ selectedCheckButton.setSelected(isSelected);
+ }
+
+ private void updateSpecialFormatIcon(Item item) {
+ mShouldShowGifBadge = item.isGifOrAnimatedWebp();
+ mShouldShowMotionPhotoBadge = item.isMotionPhoto();
+ // Invalidating options menu calls onPrepareOptionsMenu() where the logic for
+ // hiding/showing menu items is placed.
+ requireActivity().invalidateOptionsMenu();
+ }
+
+ public static void show(@NonNull FragmentManager fm, @NonNull Bundle args) {
+ if (fm.isStateSaved()) {
+ Log.d(TAG, "Skip show preview fragment because state saved");
+ return;
+ }
+
+ final PreviewFragment fragment = new PreviewFragment();
+ fragment.setArguments(args);
+ fm.beginTransaction()
+ .replace(R.id.fragment_container, fragment, TAG)
+ .addToBackStack(TAG)
+ .commitAllowingStateLoss();
+ }
+
+ /**
+ * Get the fragment in the FragmentManager
+ * @param fm the fragment manager
+ */
+ public static Fragment get(@NonNull FragmentManager fm) {
+ return fm.findFragmentByTag(TAG);
+ }
+
+ public static Bundle getArgsForPreviewOnLongPress() {
+ return sPreviewOnLongPressArgs;
+ }
+
+ public static Bundle getArgsForPreviewOnViewSelected() {
+ return sPreviewOnViewSelectedArgs;
+ }
+
+ // TODO: There is a same method in TabFragment. To find a way to reuse it.
+ private static String generateAddButtonString(@NonNull Context context, int size) {
+ final String sizeString = NumberFormat.getInstance(Locale.getDefault()).format(size);
+ final String template = context.getString(R.string.picker_add_button_multi_select);
+ return TextUtils.expandTemplate(template, sizeString).toString();
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/PreviewImageHolder.java b/src/com/android/providers/media/photopicker/ui/PreviewImageHolder.java
new file mode 100644
index 0000000..76ce74d
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/PreviewImageHolder.java
@@ -0,0 +1,49 @@
+/*
+ * 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.view.ViewGroup;
+import android.widget.ImageView;
+
+import androidx.viewpager2.widget.ViewPager2;
+import androidx.annotation.NonNull;
+
+import com.android.providers.media.R;
+import com.android.providers.media.photopicker.data.model.Item;
+
+/**
+ * ViewHolder of an image item within the {@link ViewPager2}
+ */
+public class PreviewImageHolder extends BaseViewHolder {
+ private final ImageLoader mImageLoader;
+ private final ImageView mImageView;
+
+ public PreviewImageHolder(@NonNull Context context, @NonNull ViewGroup parent,
+ @NonNull ImageLoader imageLoader) {
+ super(context, parent, R.layout.item_image_preview);
+
+ mImageView = itemView.findViewById(R.id.preview_imageView);
+ mImageLoader = imageLoader;
+ }
+
+ @Override
+ public void bind() {
+ final Item item = (Item) itemView.getTag();
+ mImageLoader.loadImagePreview(item, mImageView);
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/PreviewVideoHolder.java b/src/com/android/providers/media/photopicker/ui/PreviewVideoHolder.java
new file mode 100644
index 0000000..dcb1696
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/PreviewVideoHolder.java
@@ -0,0 +1,106 @@
+/*
+ * 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.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+
+import androidx.viewpager2.widget.ViewPager2;
+
+import com.android.providers.media.R;
+import com.android.providers.media.photopicker.data.model.Item;
+
+import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
+
+/**
+ * ViewHolder of a video item within the {@link ViewPager2}
+ */
+public class PreviewVideoHolder extends BaseViewHolder {
+
+ private final ImageLoader mImageLoader;
+ private final ImageView mImageView;
+ private final SurfaceView mSurfaceView;
+ private final AspectRatioFrameLayout mPlayerFrame;
+ private final View mPlayerContainer;
+ private final View mPlayerControlsRoot;
+ 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);
+
+ 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;
+ }
+
+ @Override
+ public void bind() {
+ // Video playback needs granular page state events and hence video playback is initiated by
+ // ViewPagerWrapper and handled by PlaybackHandler#handleVideoPlayback.
+ // Here, we set the ImageView with thumbnail from the video, to improve the
+ // user experience while video player is not yet initialized or being prepared.
+ final Item item = (Item) itemView.getTag();
+ mImageLoader.loadImageFromVideoForPreview(item, mImageView);
+ }
+
+ public ImageView getThumbnailView() {
+ return mImageView;
+ }
+
+ public SurfaceHolder getSurfaceHolder() {
+ return mSurfaceView.getHolder();
+ }
+
+ public AspectRatioFrameLayout getPlayerFrame() {
+ return mPlayerFrame;
+ }
+
+ public View getPlayerContainer() {
+ return mPlayerContainer;
+ }
+
+ public View getPlayerControlsRoot() {
+ return mPlayerControlsRoot;
+ }
+
+ public ImageButton getPlayPauseButton() {
+ return mPlayPauseButton;
+ }
+
+ public ImageButton getMuteButton() {
+ return mMuteButton;
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/ProfileDialogFragment.java b/src/com/android/providers/media/photopicker/ui/ProfileDialogFragment.java
new file mode 100644
index 0000000..8ae8e1a
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/ProfileDialogFragment.java
@@ -0,0 +1,137 @@
+/*
+ * 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 static com.android.providers.media.photopicker.ui.DevicePolicyResources.Drawables.Style.OUTLINE;
+import static com.android.providers.media.photopicker.ui.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
+import static com.android.providers.media.photopicker.ui.DevicePolicyResources.Strings.BLOCKED_BY_ADMIN_TITLE;
+import static com.android.providers.media.photopicker.ui.DevicePolicyResources.Strings.BLOCKED_FROM_PERSONAL_MESSAGE;
+import static com.android.providers.media.photopicker.ui.DevicePolicyResources.Strings.BLOCKED_FROM_WORK_MESSAGE;
+import static com.android.providers.media.photopicker.ui.DevicePolicyResources.Strings.WORK_PROFILE_PAUSED_MESSAGE;
+import static com.android.providers.media.photopicker.ui.DevicePolicyResources.Strings.WORK_PROFILE_PAUSED_TITLE;
+
+import android.app.Dialog;
+import android.app.admin.DevicePolicyManager;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.Log;
+
+import androidx.annotation.RequiresApi;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+import androidx.lifecycle.ViewModelProvider;
+
+import com.android.modules.utils.build.SdkLevel;
+import com.android.providers.media.R;
+import com.android.providers.media.photopicker.data.UserIdManager;
+import com.android.providers.media.photopicker.viewmodel.PickerViewModel;
+
+import com.google.android.material.dialog.MaterialAlertDialogBuilder;
+
+public class ProfileDialogFragment extends DialogFragment {
+
+ private static final String TAG = "ProfileDialog";
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final PickerViewModel pickerViewModel = new ViewModelProvider(requireActivity()).get(
+ PickerViewModel.class);
+ final UserIdManager userIdManager = pickerViewModel.getUserIdManager();
+ final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getActivity());
+ if (userIdManager.isBlockedByAdmin()) {
+ setBlockedByAdminParams(userIdManager.isManagedUserSelected(), builder);
+ } else if (userIdManager.isWorkProfileOff()) {
+ setWorkProfileOffParams(builder);
+ } else {
+ Log.e(TAG, "Unknown error for profile dialog");
+ return null;
+ }
+ return builder.create();
+ }
+
+ private void setBlockedByAdminParams(
+ boolean isManagedUserSelected, MaterialAlertDialogBuilder builder) {
+ String title;
+ String message;
+ if (SdkLevel.isAtLeastT()) {
+ title = getUpdatedEnterpriseString(
+ BLOCKED_BY_ADMIN_TITLE, R.string.picker_profile_admin_title);
+ message = isManagedUserSelected
+ ? getUpdatedEnterpriseString(
+ BLOCKED_FROM_WORK_MESSAGE, R.string.picker_profile_admin_msg_from_work)
+ : getUpdatedEnterpriseString(
+ BLOCKED_FROM_PERSONAL_MESSAGE,
+ R.string.picker_profile_admin_msg_from_personal);
+ } else {
+ title = getString(R.string.picker_profile_admin_title);
+ message = isManagedUserSelected
+ ? getString(R.string.picker_profile_admin_msg_from_work)
+ : getString(R.string.picker_profile_admin_msg_from_personal);
+ }
+ builder.setIcon(R.drawable.ic_lock);
+ builder.setTitle(title);
+ builder.setMessage(message);
+ builder.setPositiveButton(android.R.string.ok, null);
+ }
+
+ private void setWorkProfileOffParams(MaterialAlertDialogBuilder builder) {
+ Drawable icon;
+ String title;
+ String message;
+ if (SdkLevel.isAtLeastT()) {
+ icon = getUpdatedWorkProfileIcon();
+ title = getUpdatedEnterpriseString(
+ WORK_PROFILE_PAUSED_TITLE, R.string.picker_profile_work_paused_title);
+ message = getUpdatedEnterpriseString(
+ WORK_PROFILE_PAUSED_MESSAGE, R.string.picker_profile_work_paused_msg);
+ } else {
+ icon = getContext().getDrawable(R.drawable.ic_work_outline);
+ title = getContext().getString(R.string.picker_profile_work_paused_title);
+ message = getContext().getString(R.string.picker_profile_work_paused_msg);
+ }
+ builder.setIcon(icon);
+ builder.setTitle(title);
+ builder.setMessage(message);
+ // TODO(b/197199728): Add listener to turn on apps. This maybe a bit tricky because
+ // after turning on Work profile, work profile MediaProvider may not be available
+ // immediately.
+ builder.setPositiveButton(android.R.string.ok, null);
+ }
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ private String getUpdatedEnterpriseString(String updatableStringId, int defaultStringId) {
+ final DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class);
+ return dpm.getResources().getString(updatableStringId, () -> getString(defaultStringId));
+ }
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ private Drawable getUpdatedWorkProfileIcon() {
+ final DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class);
+ return dpm.getResources().getDrawable(WORK_PROFILE_ICON, OUTLINE, () ->
+ getContext().getDrawable(R.drawable.ic_work_outline));
+ }
+
+ public static void show(FragmentManager fm) {
+ FragmentTransaction ft = fm.beginTransaction();
+ Fragment f = new ProfileDialogFragment();
+ ft.add(f, TAG);
+ ft.commitAllowingStateLoss();
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/SquareImageView.java b/src/com/android/providers/media/photopicker/ui/SquareImageView.java
new file mode 100644
index 0000000..b3a0af3
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/SquareImageView.java
@@ -0,0 +1,43 @@
+/*
+ * 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.util.AttributeSet;
+import android.widget.ImageView;
+
+/**
+ * Ensures that imageView is always square.
+ */
+public class SquareImageView extends ImageView {
+ public SquareImageView(Context context) {
+ super(context);
+ }
+
+ public SquareImageView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public SquareImageView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, widthMeasureSpec);
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/TabContainerAdapter.java b/src/com/android/providers/media/photopicker/ui/TabContainerAdapter.java
new file mode 100644
index 0000000..bba9500
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/TabContainerAdapter.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+package com.android.providers.media.photopicker.ui;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.viewpager2.adapter.FragmentStateAdapter;
+
+/**
+ * Adapter for {@link TabContainerFragment}'s ViewPager2 to show {@link PhotosTabFragment} and
+ * {@link AlbumsTabFragment}.
+ */
+public class TabContainerAdapter extends FragmentStateAdapter {
+ private final static int TAB_COUNT = 2;
+
+ public TabContainerAdapter(@NonNull Fragment fragment) {
+ super(fragment);
+ }
+
+ @Override
+ public int getItemCount() {
+ return TAB_COUNT;
+ }
+
+ @NonNull
+ @Override
+ public Fragment createFragment(int pos) {
+ if (pos == 0) {
+ return new PhotosTabFragment();
+ }
+ return new AlbumsTabFragment();
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/TabContainerFragment.java b/src/com/android/providers/media/photopicker/ui/TabContainerFragment.java
new file mode 100644
index 0000000..5ec4d65
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/TabContainerFragment.java
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+package com.android.providers.media.photopicker.ui;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.viewpager2.widget.CompositePageTransformer;
+import androidx.viewpager2.widget.ViewPager2;
+
+import com.android.providers.media.R;
+
+import com.google.android.material.bottomsheet.BottomSheetBehavior;
+import com.google.android.material.tabs.TabLayout;
+import com.google.android.material.tabs.TabLayoutMediator;
+
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+
+/**
+ * The tab container fragment
+ */
+public class TabContainerFragment extends Fragment {
+ private static final String TAG = "TabContainerFragment";
+ private static final int PHOTOS_TAB_POSITION = 0;
+ private static final int ALBUMS_TAB_POSITION = 1;
+
+ private TabContainerAdapter mTabContainerAdapter;
+ private TabLayoutMediator mTabLayoutMediator;
+ private ViewPager2 mViewPager;
+
+ @Override
+ @NonNull
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ super.onCreateView(inflater, container, savedInstanceState);
+ return inflater.inflate(R.layout.fragment_picker_tab_container, container, false);
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ mTabContainerAdapter = new TabContainerAdapter(/* fragment */ this);
+ mViewPager = view.findViewById(R.id.picker_tab_viewpager);
+ mViewPager.setAdapter(mTabContainerAdapter);
+
+ // If the ViewPager2 has more than one page with BottomSheetBehavior, the scrolled view
+ // (e.g. RecyclerView) on the second page can't be scrolled. The workaround is to update
+ // nestedScrollingChildRef to the scrolled view on the current page. b/145334244
+ Field fieldNestedScrollingChildRef = null;
+ try {
+ fieldNestedScrollingChildRef = BottomSheetBehavior.class.getDeclaredField(
+ "nestedScrollingChildRef");
+ fieldNestedScrollingChildRef.setAccessible(true);
+ } catch (NoSuchFieldException ex) {
+ Log.d(TAG, "Can't get the field nestedScrollingChildRef from BottomSheetBehavior", ex);
+ }
+
+ final BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(
+ getActivity().findViewById(R.id.bottom_sheet));
+
+ final CompositePageTransformer compositePageTransformer = new CompositePageTransformer();
+ mViewPager.setPageTransformer(compositePageTransformer);
+ compositePageTransformer.addTransformer(new AnimationPageTransformer());
+ compositePageTransformer.addTransformer(
+ new NestedScrollPageTransformer(bottomSheetBehavior, fieldNestedScrollingChildRef));
+
+ // The BottomSheetBehavior looks for the first nested scrolling child to determine how to
+ // handle nested scrolls, it finds the inner recyclerView on ViewPager2 in this case. So, we
+ // need to work around it by setNestedScrollingEnabled false. b/145351873
+ final View firstChild = mViewPager.getChildAt(0);
+ if (firstChild instanceof RecyclerView) {
+ mViewPager.getChildAt(0).setNestedScrollingEnabled(false);
+ }
+
+ final TabLayout tabLayout = getActivity().findViewById(R.id.tab_layout);
+ mTabLayoutMediator = new TabLayoutMediator(tabLayout, mViewPager, (tab, pos) -> {
+ if (pos == PHOTOS_TAB_POSITION) {
+ tab.setText(R.string.picker_photos);
+ } else if (pos == ALBUMS_TAB_POSITION) {
+ tab.setText(R.string.picker_albums);
+ }
+ });
+ mTabLayoutMediator.attach();
+ // TabLayout only supports colorDrawable in xml. And if we set the color in the drawable by
+ // setSelectedTabIndicator method, it doesn't apply the color. So, we set color in xml and
+ // set the drawable for the shape here.
+ tabLayout.setSelectedTabIndicator(R.drawable.picker_tab_indicator);
+ }
+
+ @Override
+ public void onDestroyView() {
+ mTabLayoutMediator.detach();
+ super.onDestroyView();
+ }
+
+ /**
+ * Create the fragment and add it into the FragmentManager
+ *
+ * @param fm the fragment manager
+ */
+ public static void show(FragmentManager fm) {
+ final FragmentTransaction ft = fm.beginTransaction();
+ final TabContainerFragment fragment = new TabContainerFragment();
+ ft.replace(R.id.fragment_container, fragment, TAG);
+ ft.commitAllowingStateLoss();
+ }
+
+ private static class AnimationPageTransformer implements ViewPager2.PageTransformer {
+
+ @Override
+ public void transformPage(@NonNull View view, float pos) {
+ view.setAlpha(1.0f - Math.abs(pos));
+ }
+ }
+
+ private static class NestedScrollPageTransformer implements ViewPager2.PageTransformer {
+ private Field mFieldNestedScrollingChildRef;
+ private BottomSheetBehavior mBottomSheetBehavior;
+
+ public NestedScrollPageTransformer(BottomSheetBehavior bottomSheetBehavior, Field field) {
+ mBottomSheetBehavior = bottomSheetBehavior;
+ mFieldNestedScrollingChildRef = field;
+ }
+
+ @Override
+ public void transformPage(@NonNull View view, float pos) {
+ // If pos != 0, it is not in current page, don't update the nested scrolling child
+ // reference.
+ if (pos != 0 || mFieldNestedScrollingChildRef == null) {
+ return;
+ }
+
+ try {
+ final View childView = view.findViewById(R.id.picker_tab_recyclerview);
+ if (childView != null) {
+ mFieldNestedScrollingChildRef.set(mBottomSheetBehavior,
+ new WeakReference(childView));
+ }
+ } catch (IllegalAccessException ex) {
+ Log.d(TAG, "Set nestedScrollingChildRef to BottomSheetBehavior fail", ex);
+ }
+ }
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/TabFragment.java b/src/com/android/providers/media/photopicker/ui/TabFragment.java
new file mode 100644
index 0000000..d037ba7
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/TabFragment.java
@@ -0,0 +1,406 @@
+/*
+ * 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 static com.android.providers.media.photopicker.ui.DevicePolicyResources.Drawables.Style.OUTLINE;
+import static com.android.providers.media.photopicker.ui.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
+import static com.android.providers.media.photopicker.ui.DevicePolicyResources.Strings.SWITCH_TO_PERSONAL_MESSAGE;
+import static com.android.providers.media.photopicker.ui.DevicePolicyResources.Strings.SWITCH_TO_WORK_MESSAGE;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.fragment.app.Fragment;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.modules.utils.build.SdkLevel;
+import com.android.providers.media.R;
+import com.android.providers.media.photopicker.PhotoPickerActivity;
+import com.android.providers.media.photopicker.data.Selection;
+import com.android.providers.media.photopicker.data.UserIdManager;
+import com.android.providers.media.photopicker.viewmodel.PickerViewModel;
+
+import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton;
+
+import java.text.NumberFormat;
+import java.util.Locale;
+
+/**
+ * The base abstract Tab fragment
+ */
+public abstract class TabFragment extends Fragment {
+
+ protected PickerViewModel mPickerViewModel;
+ protected Selection mSelection;
+ protected ImageLoader mImageLoader;
+ protected AutoFitRecyclerView mRecyclerView;
+
+ private ExtendedFloatingActionButton mProfileButton;
+ private UserIdManager mUserIdManager;
+ private boolean mHideProfileButton;
+ private View mEmptyView;
+ private TextView mEmptyTextView;
+ private boolean mIsAccessibilityEnabled;
+
+ private Button mAddButton;
+ private View mBottomBar;
+ private Animation mSlideUpAnimation;
+ private Animation mSlideDownAnimation;
+
+ @ColorInt
+ private int mButtonIconAndTextColor;
+
+ @ColorInt
+ private int mButtonBackgroundColor;
+
+ @ColorInt
+ private int mButtonDisabledIconAndTextColor;
+
+ @ColorInt
+ private int mButtonDisabledBackgroundColor;
+
+ private int mRecyclerViewBottomPadding;
+
+ private final MutableLiveData<Boolean> mIsBottomBarVisible = new MutableLiveData<>(false);
+ private final MutableLiveData<Boolean> mIsProfileButtonVisible = new MutableLiveData<>(false);
+
+ @Override
+ @NonNull
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ super.onCreateView(inflater, container, savedInstanceState);
+ return inflater.inflate(R.layout.fragment_picker_tab, container, false);
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ final Context context = getContext();
+ mImageLoader = new ImageLoader(context);
+ mRecyclerView = view.findViewById(R.id.picker_tab_recyclerview);
+ mRecyclerView.setHasFixedSize(true);
+ mPickerViewModel = new ViewModelProvider(requireActivity()).get(PickerViewModel.class);
+ mSelection = mPickerViewModel.getSelection();
+ mRecyclerViewBottomPadding = getResources().getDimensionPixelSize(
+ R.dimen.picker_recycler_view_bottom_padding);
+
+ mIsBottomBarVisible.observe(this, val -> updateRecyclerViewBottomPadding());
+ mIsProfileButtonVisible.observe(this, val -> updateRecyclerViewBottomPadding());
+
+ mEmptyView = view.findViewById(android.R.id.empty);
+ mEmptyTextView = mEmptyView.findViewById(R.id.empty_text_view);
+
+ final int[] attrsDisabled =
+ new int[]{R.attr.pickerDisabledProfileButtonColor,
+ R.attr.pickerDisabledProfileButtonTextColor};
+ final TypedArray taDisabled = context.obtainStyledAttributes(attrsDisabled);
+ mButtonDisabledBackgroundColor = taDisabled.getColor(/* index */ 0, /* defValue */ -1);
+ mButtonDisabledIconAndTextColor = taDisabled.getColor(/* index */ 1, /* defValue */ -1);
+ taDisabled.recycle();
+
+ final int[] attrs =
+ new int[]{R.attr.pickerProfileButtonColor, R.attr.pickerProfileButtonTextColor};
+ final TypedArray ta = context.obtainStyledAttributes(attrs);
+ mButtonBackgroundColor = ta.getColor(/* index */ 0, /* defValue */ -1);
+ mButtonIconAndTextColor = ta.getColor(/* index */ 1, /* defValue */ -1);
+ ta.recycle();
+
+ mProfileButton = getActivity().findViewById(R.id.profile_button);
+ mUserIdManager = mPickerViewModel.getUserIdManager();
+
+ final boolean canSelectMultiple = mSelection.canSelectMultiple();
+ if (canSelectMultiple) {
+ mAddButton = getActivity().findViewById(R.id.button_add);
+ mAddButton.setOnClickListener(v -> {
+ ((PhotoPickerActivity) getActivity()).setResultAndFinishSelf();
+ });
+
+ final Button viewSelectedButton = getActivity().findViewById(R.id.button_view_selected);
+ // Transition to PreviewFragment on clicking "View Selected".
+ viewSelectedButton.setOnClickListener(v -> {
+ mSelection.prepareSelectedItemsForPreviewAll();
+ PreviewFragment.show(getActivity().getSupportFragmentManager(),
+ PreviewFragment.getArgsForPreviewOnViewSelected());
+ });
+
+ mBottomBar = getActivity().findViewById(R.id.picker_bottom_bar);
+ mSlideUpAnimation = AnimationUtils.loadAnimation(getContext(), R.anim.slide_up);
+ mSlideDownAnimation = AnimationUtils.loadAnimation(getContext(), R.anim.slide_down);
+
+ mSelection.getSelectedItemCount().observe(this, selectedItemListSize -> {
+ updateProfileButtonVisibility();
+ updateVisibilityAndAnimateBottomBar(selectedItemListSize);
+ });
+ }
+
+ // Initial setup
+ setUpProfileButtonWithListeners(mUserIdManager.isMultiUserProfiles());
+
+ // Observe for cross profile access changes.
+ final LiveData<Boolean> crossProfileAllowed = mUserIdManager.getCrossProfileAllowed();
+ if (crossProfileAllowed != null) {
+ crossProfileAllowed.observe(this, isCrossProfileAllowed -> {
+ setUpProfileButton();
+ });
+ }
+
+ // Observe for multi-user changes.
+ final LiveData<Boolean> isMultiUserProfiles = mUserIdManager.getIsMultiUserProfiles();
+ if (isMultiUserProfiles != null) {
+ isMultiUserProfiles.observe(this, this::setUpProfileButtonWithListeners);
+ }
+
+ final AccessibilityManager accessibilityManager =
+ context.getSystemService(AccessibilityManager.class);
+ mIsAccessibilityEnabled = accessibilityManager.isEnabled();
+ accessibilityManager.addAccessibilityStateChangeListener(enabled -> {
+ mIsAccessibilityEnabled = enabled;
+ updateProfileButtonVisibility();
+ });
+ }
+
+ private void updateRecyclerViewBottomPadding() {
+ final int recyclerViewBottomPadding;
+ if (mIsProfileButtonVisible.getValue() || mIsBottomBarVisible.getValue()) {
+ recyclerViewBottomPadding = mRecyclerViewBottomPadding;
+ } else {
+ recyclerViewBottomPadding = 0;
+ }
+
+ mRecyclerView.setPadding(0, 0, 0, recyclerViewBottomPadding);
+ }
+
+ private void updateVisibilityAndAnimateBottomBar(int selectedItemListSize) {
+ if (!mSelection.canSelectMultiple()) {
+ return;
+ }
+
+ if (selectedItemListSize == 0) {
+ if (mBottomBar.getVisibility() == View.VISIBLE) {
+ mBottomBar.setVisibility(View.GONE);
+ mBottomBar.startAnimation(mSlideDownAnimation);
+ }
+ } else {
+ if (mBottomBar.getVisibility() == View.GONE) {
+ mBottomBar.setVisibility(View.VISIBLE);
+ mBottomBar.startAnimation(mSlideUpAnimation);
+ }
+ mAddButton.setText(generateAddButtonString(getContext(), selectedItemListSize));
+ }
+ mIsBottomBarVisible.setValue(selectedItemListSize > 0);
+ }
+
+ private void setUpListenersForProfileButton() {
+ mProfileButton.setOnClickListener(v -> onClickProfileButton());
+ mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+ super.onScrolled(recyclerView, dx, dy);
+
+ // Do not change profile button visibility on scroll if Accessibility mode is
+ // enabled. This is done to enhance button visibility in Accessibility mode.
+ if (mIsAccessibilityEnabled) {
+ return;
+ }
+
+ if (dy > 0) {
+ mProfileButton.hide();
+ } else {
+ updateProfileButtonVisibility();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (mRecyclerView != null) {
+ mRecyclerView.clearOnScrollListeners();
+ }
+ }
+
+ private void setUpProfileButtonWithListeners(boolean isMultiUserProfile) {
+ if (isMultiUserProfile) {
+ setUpListenersForProfileButton();
+ } else {
+ mRecyclerView.clearOnScrollListeners();
+ }
+ setUpProfileButton();
+ }
+
+ private void setUpProfileButton() {
+ updateProfileButtonVisibility();
+ if (!mUserIdManager.isMultiUserProfiles()) {
+ return;
+ }
+
+ updateProfileButtonContent(mUserIdManager.isManagedUserSelected());
+ updateProfileButtonColor(/* isDisabled */ !mUserIdManager.isCrossProfileAllowed());
+ }
+
+ private boolean shouldShowProfileButton() {
+ return mUserIdManager.isMultiUserProfiles() && !mHideProfileButton &&
+ (!mSelection.canSelectMultiple() ||
+ mSelection.getSelectedItemCount().getValue() == 0);
+ }
+
+ private void onClickProfileButton() {
+ if (!mUserIdManager.isCrossProfileAllowed()) {
+ ProfileDialogFragment.show(getActivity().getSupportFragmentManager());
+ } else {
+ changeProfile();
+ }
+ }
+
+ private void changeProfile() {
+ if (mUserIdManager.isManagedUserSelected()) {
+ // TODO(b/190024747): Add caching for performance before switching data to and fro
+ // work profile
+ mUserIdManager.setPersonalAsCurrentUserProfile();
+
+ } else {
+ // TODO(b/190024747): Add caching for performance before switching data to and fro
+ // work profile
+ mUserIdManager.setManagedAsCurrentUserProfile();
+ }
+
+ updateProfileButtonContent(mUserIdManager.isManagedUserSelected());
+
+ mPickerViewModel.updateItems();
+ mPickerViewModel.updateCategories();
+ }
+
+ private void updateProfileButtonContent(boolean isManagedUserSelected) {
+ final Drawable icon;
+ final String text;
+ if (isManagedUserSelected) {
+ icon = getContext().getDrawable(R.drawable.ic_personal_mode);
+ text = getSwitchToPersonalMessage();
+ } else {
+ icon = getWorkProfileIcon();
+ text = getSwitchToWorkMessage();
+ }
+ mProfileButton.setIcon(icon);
+ mProfileButton.setText(text);
+ }
+
+ private String getSwitchToPersonalMessage() {
+ if (SdkLevel.isAtLeastT()) {
+ return getUpdatedEnterpriseString(
+ SWITCH_TO_PERSONAL_MESSAGE, R.string.picker_personal_profile);
+ } else {
+ return getContext().getString(R.string.picker_personal_profile);
+ }
+ }
+
+ private String getSwitchToWorkMessage() {
+ if (SdkLevel.isAtLeastT()) {
+ return getUpdatedEnterpriseString(
+ SWITCH_TO_WORK_MESSAGE, R.string.picker_work_profile);
+ } else {
+ return getContext().getString(R.string.picker_work_profile);
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ private String getUpdatedEnterpriseString(String updatableStringId, int defaultStringId) {
+ final DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class);
+ return dpm.getResources().getString(updatableStringId, () -> getString(defaultStringId));
+ }
+
+ private Drawable getWorkProfileIcon() {
+ if (SdkLevel.isAtLeastT()) {
+ return getUpdatedWorkProfileIcon();
+ } else {
+ return getContext().getDrawable(R.drawable.ic_work_outline);
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ private Drawable getUpdatedWorkProfileIcon() {
+ DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class);
+ return dpm.getResources().getDrawable(WORK_PROFILE_ICON, OUTLINE, () ->
+ getContext().getDrawable(R.drawable.ic_work_outline));
+ }
+
+ private void updateProfileButtonColor(boolean isDisabled) {
+ final int textAndIconColor =
+ isDisabled ? mButtonDisabledIconAndTextColor : mButtonIconAndTextColor;
+ final int backgroundTintColor =
+ isDisabled ? mButtonDisabledBackgroundColor : mButtonBackgroundColor;
+
+ mProfileButton.setTextColor(ColorStateList.valueOf(textAndIconColor));
+ mProfileButton.setIconTint(ColorStateList.valueOf(textAndIconColor));
+ mProfileButton.setBackgroundTintList(ColorStateList.valueOf(backgroundTintColor));
+ }
+
+ protected void hideProfileButton(boolean hide) {
+ mHideProfileButton = hide;
+ updateProfileButtonVisibility();
+ }
+
+ private void updateProfileButtonVisibility() {
+ final boolean shouldShowProfileButton = shouldShowProfileButton();
+ if (shouldShowProfileButton) {
+ mProfileButton.show();
+ } else {
+ mProfileButton.hide();
+ }
+ mIsProfileButtonVisible.setValue(shouldShowProfileButton);
+ }
+
+ protected void setEmptyMessage(int resId) {
+ mEmptyTextView.setText(resId);
+ }
+
+ /**
+ * If we show the {@link #mEmptyView}, hide the {@link #mRecyclerView}. If we don't hide the
+ * {@link #mEmptyView}, show the {@link #mRecyclerView}
+ */
+ protected void updateVisibilityForEmptyView(boolean shouldShowEmptyView) {
+ mEmptyView.setVisibility(shouldShowEmptyView ? View.VISIBLE : View.GONE);
+ mRecyclerView.setVisibility(shouldShowEmptyView ? View.GONE : View.VISIBLE);
+ }
+
+ private static String generateAddButtonString(Context context, int size) {
+ final String sizeString = NumberFormat.getInstance(Locale.getDefault()).format(size);
+ final String template = context.getString(R.string.picker_add_button_multi_select);
+ return TextUtils.expandTemplate(template, sizeString).toString();
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/ViewPager2Wrapper.java b/src/com/android/providers/media/photopicker/ui/ViewPager2Wrapper.java
new file mode 100644
index 0000000..563f777
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/ViewPager2Wrapper.java
@@ -0,0 +1,110 @@
+/*
+ * 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.view.View;
+
+import androidx.viewpager2.widget.CompositePageTransformer;
+import androidx.viewpager2.widget.MarginPageTransformer;
+import androidx.viewpager2.widget.ViewPager2;
+
+import com.android.providers.media.R;
+import com.android.providers.media.photopicker.data.MuteStatus;
+import com.android.providers.media.photopicker.data.model.Item;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A wrapper class to assist in initializing {@link ViewPager2} and {@link PreviewAdapter}. This
+ * class also supports some of {@link ViewPager2} and {@link PreviewAdapter} methods to avoid
+ * exposing these objects outside this class.
+ * The class also supports registering {@link ViewPager2.OnPageChangeCallback} and unregister the
+ * same onDestroy().
+ */
+class ViewPager2Wrapper {
+ private final ViewPager2 mViewPager;
+ private final PreviewAdapter mAdapter;
+ private final List<ViewPager2.OnPageChangeCallback> mOnPageChangeCallbacks = new ArrayList<>();
+
+ ViewPager2Wrapper(ViewPager2 viewPager, List<Item> selectedItems, MuteStatus muteStatus) {
+ mViewPager = viewPager;
+
+ final Context context = mViewPager.getContext();
+
+ mAdapter = new PreviewAdapter(context, muteStatus);
+ mAdapter.updateItemList(selectedItems);
+ mViewPager.setAdapter(mAdapter);
+
+ CompositePageTransformer compositePageTransformer = new CompositePageTransformer();
+ compositePageTransformer.addTransformer(new MarginPageTransformer(
+ context.getResources().getDimensionPixelSize(R.dimen.preview_viewpager_margin)));
+ compositePageTransformer.addTransformer(new PlayerPageTransformer());
+ mViewPager.setPageTransformer(compositePageTransformer);
+ }
+
+ /**
+ * Registers given {@link ViewPager2.OnPageChangeCallback} to the {@link ViewPager2}. This class
+ * also takes care of unregistering the callback onDestroy()
+ */
+ public void addOnPageChangeCallback(ViewPager2.OnPageChangeCallback onPageChangeCallback) {
+ mOnPageChangeCallbacks.add(onPageChangeCallback);
+ mViewPager.registerOnPageChangeCallback(onPageChangeCallback);
+ }
+
+ public Item getItemAt(int position) {
+ return getItemAtInternal(position);
+ }
+
+ public Item getCurrentItem() {
+ return getItemAtInternal(mViewPager.getCurrentItem());
+ }
+
+ private Item getItemAtInternal(int position) {
+ return mAdapter.getItem(position);
+ }
+
+ public void onStop() {
+ mAdapter.onStop();
+ }
+
+ public void onStart() {
+ // TODO(b/197083539): Restore the playback state here.
+ // This forces PageTransformer#transformPage call and assists in ExoPlayer initialization.
+ mViewPager.requestTransform();
+ }
+
+ public void onDestroy() {
+ for (ViewPager2.OnPageChangeCallback callback : mOnPageChangeCallbacks) {
+ mViewPager.unregisterOnPageChangeCallback(callback);
+ }
+ mOnPageChangeCallbacks.clear();
+ mAdapter.onDestroy();
+ }
+
+ private class PlayerPageTransformer implements ViewPager2.PageTransformer {
+ @Override
+ public void transformPage(View view, float position) {
+ // We are only interested in position == 0.0. Only position=0.0 indicates that the page
+ // is selected.
+ if (position != 0) return;
+
+ mAdapter.onHandlePageSelected(view);
+ }
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/remotepreview/PlayerControlsVisibilityStatus.java b/src/com/android/providers/media/photopicker/ui/remotepreview/PlayerControlsVisibilityStatus.java
new file mode 100644
index 0000000..caa7a38
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/remotepreview/PlayerControlsVisibilityStatus.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package com.android.providers.media.photopicker.ui.remotepreview;
+
+/**
+ * Tracks the visibility status of player controls in remote preview.
+ */
+class PlayerControlsVisibilityStatus {
+
+ /**
+ * Player controls should be visible by default.
+ */
+ private boolean mShouldShowPlayerControlsForNextItem = true;
+
+ boolean shouldShowPlayerControls() {
+ return mShouldShowPlayerControlsForNextItem;
+ }
+
+ void setShouldShowPlayerControlsForNextItem(boolean shouldShowPlayerControlsForNextItem) {
+ mShouldShowPlayerControlsForNextItem = shouldShowPlayerControlsForNextItem;
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/remotepreview/RemotePreviewHandler.java b/src/com/android/providers/media/photopicker/ui/remotepreview/RemotePreviewHandler.java
new file mode 100644
index 0000000..ccf2885
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/remotepreview/RemotePreviewHandler.java
@@ -0,0 +1,322 @@
+/*
+ * 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.
+ */
+
+package com.android.providers.media.photopicker.ui.remotepreview;
+
+import static android.provider.CloudMediaProviderContract.EXTRA_AUTHORITY;
+import static android.provider.CloudMediaProviderContract.EXTRA_LOOPING_PLAYBACK_ENABLED;
+import static android.provider.CloudMediaProviderContract.EXTRA_SURFACE_CONTROLLER;
+import static android.provider.CloudMediaProviderContract.EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED;
+import static android.provider.CloudMediaProviderContract.EXTRA_SURFACE_STATE_CALLBACK;
+import static android.provider.CloudMediaProviderContract.METHOD_CREATE_SURFACE_CONTROLLER;
+
+import static com.android.providers.media.PickerUriResolver.createSurfaceControllerUri;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PlaybackState;
+import android.provider.ICloudMediaSurfaceController;
+import android.provider.ICloudMediaSurfaceStateChangedCallback;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import com.android.providers.media.photopicker.PickerSyncController;
+import com.android.providers.media.photopicker.data.MuteStatus;
+import com.android.providers.media.photopicker.data.model.Item;
+import com.android.providers.media.photopicker.ui.PreviewVideoHolder;
+
+import java.util.Map;
+
+/**
+ * Manages playback of videos on a {@link Surface} with a
+ * {@link android.provider.CloudMediaProvider.CloudMediaSurfaceController} populated remotely.
+ *
+ * <p>This class is not thread-safe and the methods are meant to be always called on the main
+ * thread.
+ */
+public final class RemotePreviewHandler {
+
+ private static final String TAG = "RemotePreviewHandler";
+
+ private final Context mContext;
+ private final MuteStatus mMuteStatus;
+ private final ArrayMap<SurfaceHolder, RemotePreviewSession>
+ mSessionMap = new ArrayMap<>();
+ private final Map<String, SurfaceControllerProxy> mControllers =
+ new ArrayMap<>();
+ private final SurfaceHolder.Callback mSurfaceHolderCallback = new PreviewSurfaceCallback();
+ private final SurfaceStateChangedCallbackWrapper mSurfaceStateChangedCallbackWrapper =
+ new SurfaceStateChangedCallbackWrapper();
+ private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
+ private final ItemPreviewState mCurrentPreviewState = new ItemPreviewState();
+ private final PlayerControlsVisibilityStatus mPlayerControlsVisibilityStatus =
+ new PlayerControlsVisibilityStatus();
+
+ private boolean mIsInBackground = false;
+ private int mSurfaceCounter = 0;
+
+ /**
+ * Returns {@code true} if remote preview is enabled.
+ */
+ public static boolean isRemotePreviewEnabled() {
+ return SystemProperties.getBoolean("sys.photopicker.remote_preview", true);
+ }
+
+ public RemotePreviewHandler(Context context, MuteStatus muteStatus) {
+ mContext = context;
+ mMuteStatus = muteStatus;
+ }
+
+ /**
+ * Prepares the given {@link SurfaceView} for remote preview of the given {@link Item}.
+ *
+ * @param viewHolder {@link PreviewVideoHolder} for the media item under preview
+ * @param item {@link Item} to be previewed
+ */
+ public void onViewAttachedToWindow(PreviewVideoHolder viewHolder, Item item) {
+ final RemotePreviewSession session = createRemotePreviewSession(item, viewHolder);
+ final SurfaceHolder holder = viewHolder.getSurfaceHolder();
+
+ mSessionMap.put(holder, session);
+ // Ensure that we don't add the same callback twice, since we don't remove callbacks
+ // anywhere else.
+ holder.removeCallback(mSurfaceHolderCallback);
+ holder.addCallback(mSurfaceHolderCallback);
+ }
+
+ /**
+ * Handle page selected event for the given {@link Item}.
+ *
+ * <p>This is where we start the playback for the {@link Item}.
+ *
+ * @param item {@link Item} to be played
+ * @return true if the given {@link Item} can be played, else false
+ */
+ public boolean onHandlePageSelected(Item item) {
+ if (!item.isVideo()) {
+ // Clear state of the previous player controls visibility state. Controls visibility
+ // state will only be tracked and used for contiguous videos in the preview.
+ mPlayerControlsVisibilityStatus.setShouldShowPlayerControlsForNextItem(true);
+ return false;
+ }
+
+ Log.i(TAG, "onHandlePageSelected() called, attempting to start playback.");
+ RemotePreviewSession session = getSessionForItem(item);
+ if (session == null) {
+ Log.w(TAG, "No RemotePreviewSession found.");
+ return false;
+ }
+
+ mCurrentPreviewState.item = item;
+ mCurrentPreviewState.viewHolder = session.getPreviewVideoHolder();
+
+ session.requestPlayMedia();
+ return true;
+ }
+
+ /**
+ * Handle onStop called from activity/fragment lifecycle.
+ */
+ public void onStop() {
+ mIsInBackground = true;
+ }
+
+ /**
+ * Handle onDestroy called from activity/fragment lifecycle.
+ *
+ * <p>This is where the surface controllers are destroyed and their references are released.
+ */
+ public void onDestroy() {
+ Log.i(TAG, "onDestroy() called, destroying all surface controllers.");
+ destroyAllSurfaceControllers();
+ }
+
+ private RemotePreviewSession createRemotePreviewSession(Item item,
+ PreviewVideoHolder previewVideoHolder) {
+ String authority = item.getContentUri().getAuthority();
+ SurfaceControllerProxy controller = getSurfaceController(authority, false);
+ if (controller == null) {
+ Log.w(TAG, "Failed to create RemotePreviewSession for " + authority
+ + ". Fallback to openPreview");
+ controller = getSurfaceController(authority, true);
+ }
+
+ return new RemotePreviewSession(mSurfaceCounter++, item.getId(), authority, controller,
+ previewVideoHolder, mMuteStatus, mPlayerControlsVisibilityStatus, mContext);
+ }
+
+ private void restorePreviewState(SurfaceHolder holder) {
+ RemotePreviewSession session = createRemotePreviewSession(mCurrentPreviewState.item,
+ mCurrentPreviewState.viewHolder);
+ if (session == null) {
+ throw new IllegalStateException("Failed to restore preview state.");
+ }
+
+ mSessionMap.put(holder, session);
+ session.surfaceCreated();
+ session.requestPlayMedia();
+ }
+
+ private RemotePreviewSession getSessionForItem(Item item) {
+ String mediaId = item.getId();
+ String authority = item.getContentUri().getAuthority();
+ for (RemotePreviewSession session : mSessionMap.values()) {
+ if (session.getMediaId().equals(mediaId) && session.getAuthority().equals(authority)) {
+ return session;
+ }
+ }
+ return null;
+ }
+
+ private RemotePreviewSession getSessionForSurfaceId(int surfaceId) {
+ for (RemotePreviewSession session : mSessionMap.values()) {
+ if (session.getSurfaceId() == surfaceId) {
+ return session;
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ private SurfaceControllerProxy getSurfaceController(String authority,
+ boolean localControllerFallback) {
+ if (mControllers.containsKey(authority)) {
+ return mControllers.get(authority);
+ }
+
+ SurfaceControllerProxy controller = null;
+ try {
+ controller = createController(authority, localControllerFallback);
+ if (controller != null) {
+ mControllers.put(authority, controller);
+ }
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Could not create SurfaceController.", e);
+ }
+ return controller;
+ }
+
+ private void destroyAllSurfaceControllers() {
+ for (SurfaceControllerProxy controller : mControllers.values()) {
+ try {
+ controller.onDestroy();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to destroy SurfaceController.", e);
+ }
+ }
+ mControllers.clear();
+ }
+
+ private SurfaceControllerProxy createController(String authority,
+ boolean localControllerFallback) {
+ Log.i(TAG, "Creating new SurfaceController for authority: " + authority
+ + ". localControllerFallback: " + localControllerFallback);
+ Bundle extras = new Bundle();
+ extras.putBoolean(EXTRA_LOOPING_PLAYBACK_ENABLED, true);
+ extras.putBoolean(EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED, mMuteStatus.isVolumeMuted());
+ extras.putBinder(EXTRA_SURFACE_STATE_CALLBACK, mSurfaceStateChangedCallbackWrapper);
+
+ if (localControllerFallback) {
+ extras.putString(EXTRA_AUTHORITY, authority);
+ authority = PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY;
+ }
+
+ final Bundle surfaceControllerBundle = mContext.getContentResolver().call(
+ createSurfaceControllerUri(authority),
+ METHOD_CREATE_SURFACE_CONTROLLER, /* arg */ null, extras);
+ IBinder binder = surfaceControllerBundle.getBinder(EXTRA_SURFACE_CONTROLLER);
+ return binder != null ? new SurfaceControllerProxy(
+ ICloudMediaSurfaceController.Stub.asInterface(binder))
+ : null;
+ }
+
+ /**
+ * Wrapper class for {@link android.provider.ICloudMediaSurfaceStateChangedCallback} interface
+ * implementation.
+ */
+ private final class SurfaceStateChangedCallbackWrapper extends
+ ICloudMediaSurfaceStateChangedCallback.Stub {
+
+ @Override
+ public void setPlaybackState(int surfaceId, @PlaybackState int playbackState,
+ @Nullable Bundle playbackStateInfo) {
+ Log.d(TAG, "Received onPlaybackEvent for surfaceId: " + surfaceId +
+ " ; playbackState: " + playbackState + " ; playbackStateInfo: " +
+ playbackStateInfo);
+
+ mMainThreadHandler.post(() -> {
+ final RemotePreviewSession session = getSessionForSurfaceId(surfaceId);
+ if (session == null) {
+ Log.w(TAG, "No RemotePreviewSession found.");
+ return;
+ }
+ session.setPlaybackState(playbackState, playbackStateInfo);
+ });
+ }
+ }
+
+ private final class PreviewSurfaceCallback implements SurfaceHolder.Callback {
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ Log.i(TAG, "Surface created: " + holder);
+
+ if (mIsInBackground) {
+ // This indicates that the app has just come to foreground, and we need to
+ // restore the preview state.
+ restorePreviewState(holder);
+ mIsInBackground = false;
+ return;
+ }
+
+ Surface surface = holder.getSurface();
+ RemotePreviewSession session = mSessionMap.get(holder);
+ session.surfaceCreated();
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ Log.i(TAG, "Surface changed: " + holder + " format: " + format + " width: " + width
+ + " height: " + height);
+
+ RemotePreviewSession session = mSessionMap.get(holder);
+ session.surfaceChanged(format, width, height);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ Log.i(TAG, "Surface destroyed: " + holder);
+
+ RemotePreviewSession session = mSessionMap.get(holder);
+ session.surfaceDestroyed();
+ mSessionMap.remove(holder);
+ }
+ }
+
+ private static final class ItemPreviewState {
+ Item item;
+ PreviewVideoHolder viewHolder;
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/remotepreview/RemotePreviewSession.java b/src/com/android/providers/media/photopicker/ui/remotepreview/RemotePreviewSession.java
new file mode 100644
index 0000000..4cd7e8e
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/remotepreview/RemotePreviewSession.java
@@ -0,0 +1,431 @@
+/*
+ * 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.
+ */
+
+package com.android.providers.media.photopicker.ui.remotepreview;
+
+import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PLAYBACK_STATE_BUFFERING;
+import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PLAYBACK_STATE_COMPLETED;
+import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PLAYBACK_STATE_MEDIA_SIZE_CHANGED;
+import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PLAYBACK_STATE_PAUSED;
+import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PLAYBACK_STATE_READY;
+import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PLAYBACK_STATE_STARTED;
+import static android.provider.CloudMediaProviderContract.EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PlaybackState;
+import android.util.Log;
+import android.view.View;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
+import android.widget.ImageButton;
+
+import com.android.providers.media.R;
+import com.android.providers.media.photopicker.data.MuteStatus;
+import com.android.providers.media.photopicker.ui.PreviewVideoHolder;
+
+/**
+ * Handles preview of a given media on a {@link Surface}.
+ */
+final class RemotePreviewSession {
+
+ private static final String TAG = "RemotePreviewSession";
+ private static final long PLAYER_CONTROL_ON_PLAY_TIMEOUT_MS = 1000;
+
+ private final int mSurfaceId;
+ private final String mMediaId;
+ private final String mAuthority;
+ private final SurfaceControllerProxy mSurfaceController;
+ private final PreviewVideoHolder mPreviewVideoHolder;
+ private final MuteStatus mMuteStatus;
+ private final PlayerControlsVisibilityStatus mPlayerControlsVisibilityStatus;
+ private final AccessibilityManager mAccessibilityManager;
+ private final View.OnClickListener mPlayPauseButtonClickListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mCurrentPlaybackState == PLAYBACK_STATE_STARTED) {
+ pauseMedia();
+ } else {
+ playMedia();
+ }
+ }
+ };
+ private final View.OnClickListener mMuteButtonClickListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ boolean newMutedValue = !mMuteStatus.isVolumeMuted();
+ setAudioMuted(newMutedValue);
+ mMuteStatus.setVolumeMuted(newMutedValue);
+ updateMuteButtonState(mMuteStatus.isVolumeMuted());
+ }
+ };
+ private final View.OnClickListener mPlayerContainerClickListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ boolean playerControlsVisible =
+ mPreviewVideoHolder.getPlayerControlsRoot().getVisibility() == View.VISIBLE;
+ updatePlayerControlsVisibilityState(!playerControlsVisible);
+ }
+ };
+ private final AccessibilityStateChangeListener mAccessibilityStateChangeListener =
+ this::updateAccessibilityState;
+
+ private SurfaceChangeData mSurfaceChangeData;
+ private boolean mIsSurfaceCreated = false;
+ private boolean mIsSurfaceCreationNotified = false;
+ private boolean mIsPlaybackRequested = false;
+ @PlaybackState
+ private int mCurrentPlaybackState = PLAYBACK_STATE_BUFFERING;
+ private boolean mIsAccessibilityEnabled;
+
+ RemotePreviewSession(int surfaceId, @NonNull String mediaId, @NonNull String authority,
+ @NonNull SurfaceControllerProxy surfaceController,
+ @NonNull PreviewVideoHolder previewVideoHolder, @NonNull MuteStatus muteStatus,
+ @NonNull PlayerControlsVisibilityStatus playerControlsVisibilityStatus,
+ @NonNull Context context) {
+ this.mSurfaceId = surfaceId;
+ this.mMediaId = mediaId;
+ this.mAuthority = authority;
+ this.mSurfaceController = surfaceController;
+ this.mPreviewVideoHolder = previewVideoHolder;
+ this.mMuteStatus = muteStatus;
+ this.mPlayerControlsVisibilityStatus = playerControlsVisibilityStatus;
+ this.mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
+
+ initUI();
+ }
+
+ int getSurfaceId() {
+ return mSurfaceId;
+ }
+
+ @NonNull
+ String getMediaId() {
+ return mMediaId;
+ }
+
+ @NonNull
+ String getAuthority() {
+ return mAuthority;
+ }
+
+ @NonNull
+ PreviewVideoHolder getPreviewVideoHolder() {
+ return mPreviewVideoHolder;
+ }
+
+ void surfaceCreated() {
+ if (mIsSurfaceCreated) {
+ throw new IllegalStateException("Surface is already created.");
+ }
+ if (mIsSurfaceCreationNotified) {
+ throw new IllegalStateException(
+ "Surface creation has been already notified to SurfaceController.");
+ }
+
+ mIsSurfaceCreated = true;
+
+ // Notify surface creation only if playback has been already requested, else this will be
+ // done in requestPlayMedia() when playback is explicitly requested.
+ if (mIsPlaybackRequested) {
+ notifySurfaceCreated();
+ }
+ }
+
+ void surfaceChanged(int format, int width, int height) {
+ mSurfaceChangeData = new SurfaceChangeData(format, width, height);
+
+ // Notify surface change only if playback has been already requested, else this will be
+ // done in requestPlayMedia() when playback is explicitly requested.
+ if (mIsPlaybackRequested) {
+ notifySurfaceChanged();
+ }
+ }
+
+ void surfaceDestroyed() {
+ if (!mIsSurfaceCreated) {
+ throw new IllegalStateException("Surface is not created.");
+ }
+
+ mSurfaceChangeData = null;
+
+ tearDownUI();
+
+ if (!mIsSurfaceCreationNotified) {
+ // If we haven't notified surface creation yet, then no need to notify surface
+ // destruction either.
+ return;
+ }
+
+ try {
+ mSurfaceController.onSurfaceDestroyed(mSurfaceId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failure in onSurfaceDestroyed().", e);
+ }
+ }
+
+ void requestPlayMedia() {
+ // When the user is at the first item in ViewPager, swiping further right would trigger the
+ // callback {@link ViewPager2.PageTransformer#transforPage(View, int)}, which would call
+ // into requestPlayMedia again. Hence, we want to check if playback is already requested or
+ // if playback is already happening, before proceeding further.
+ if (mIsPlaybackRequested || (mCurrentPlaybackState == PLAYBACK_STATE_STARTED)) {
+ return;
+ }
+
+ if (mCurrentPlaybackState == PLAYBACK_STATE_READY
+ || mCurrentPlaybackState == PLAYBACK_STATE_MEDIA_SIZE_CHANGED
+ || mCurrentPlaybackState == PLAYBACK_STATE_COMPLETED
+ || mCurrentPlaybackState == PLAYBACK_STATE_PAUSED) {
+ playMedia();
+ return;
+ }
+
+ // Now that playback has been requested, try to notify surface creation and surface change
+ // so that player can be prepared with the surface.
+ if (mIsSurfaceCreated) {
+ notifySurfaceCreated();
+ }
+ if (mSurfaceChangeData != null) {
+ notifySurfaceChanged();
+ }
+
+ mIsPlaybackRequested = true;
+ }
+
+ void setPlaybackState(@PlaybackState int playbackState, @Nullable Bundle playbackStateInfo) {
+ mCurrentPlaybackState = playbackState;
+ switch (mCurrentPlaybackState) {
+ case PLAYBACK_STATE_READY:
+ if (mIsPlaybackRequested) {
+ playMedia();
+ mIsPlaybackRequested = false;
+ }
+ return;
+ case PLAYBACK_STATE_MEDIA_SIZE_CHANGED:
+ Point size = playbackStateInfo.getParcelable(ContentResolver.EXTRA_SIZE);
+ onMediaSizeChanged(size.x, size.y);
+ return;
+ case PLAYBACK_STATE_STARTED:
+ updatePlayPauseButtonState(true /* isPlaying */);
+ if (mIsAccessibilityEnabled
+ || mPlayerControlsVisibilityStatus.shouldShowPlayerControls()) {
+ updatePlayerControlsVisibilityState(true /* visible */);
+ }
+ if (!mIsAccessibilityEnabled) {
+ hidePlayerControlsWithDelay();
+ }
+ return;
+ case PLAYBACK_STATE_PAUSED:
+ updatePlayPauseButtonState(false /* isPlaying */);
+ return;
+ default:
+ }
+ }
+
+ private void notifySurfaceCreated() {
+ if (!mIsSurfaceCreated) {
+ throw new IllegalStateException("Surface is not created.");
+ }
+ if (mIsSurfaceCreationNotified) {
+ throw new IllegalStateException(
+ "Surface creation has already been notified to SurfaceController.");
+ }
+
+ try {
+ mSurfaceController.onSurfaceCreated(mSurfaceId,
+ mPreviewVideoHolder.getSurfaceHolder().getSurface(), mMediaId);
+ mIsSurfaceCreationNotified = true;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failure in notifySurfaceCreated().", e);
+ }
+ }
+
+ private void notifySurfaceChanged() {
+ if (!mIsSurfaceCreated) {
+ throw new IllegalStateException("Surface is not created.");
+ }
+ if (!mIsSurfaceCreationNotified) {
+ throw new IllegalStateException(
+ "Surface creation has not been notified to SurfaceController.");
+ }
+
+ if (mSurfaceChangeData == null) {
+ throw new IllegalStateException("No surface change data present.");
+ }
+
+ try {
+ mSurfaceController.onSurfaceChanged(mSurfaceId, mSurfaceChangeData.getFormat(),
+ mSurfaceChangeData.getWidth(), mSurfaceChangeData.getHeight());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failure in notifySurfaceChanged().", e);
+ }
+ }
+
+ private void playMedia() {
+ if (!mIsSurfaceCreated) {
+ throw new IllegalStateException("Surface is not created.");
+ }
+ if (!mIsSurfaceCreationNotified) {
+ throw new IllegalStateException(
+ "Surface creation has not been notified to SurfaceController.");
+ }
+
+ if (mCurrentPlaybackState == PLAYBACK_STATE_STARTED) {
+ throw new IllegalStateException("Player is already playing.");
+ }
+
+ try {
+ mSurfaceController.onMediaPlay(mSurfaceId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to play media.", e);
+ }
+ }
+
+ private void pauseMedia() {
+ if (!mIsSurfaceCreated) {
+ throw new IllegalStateException("Surface is not created.");
+ }
+ if (!mIsSurfaceCreationNotified) {
+ throw new IllegalStateException(
+ "Surface creation has not been notified to SurfaceController.");
+ }
+
+ if (mCurrentPlaybackState != PLAYBACK_STATE_STARTED) {
+ throw new IllegalStateException("Player is not playing.");
+ }
+
+ try {
+ mSurfaceController.onMediaPause(mSurfaceId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to pause media.", e);
+ }
+ }
+
+ private void setAudioMuted(boolean isMuted) {
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED, isMuted);
+ try {
+ mSurfaceController.onConfigChange(bundle);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to perform config change.", e);
+ }
+ }
+
+ private void onMediaSizeChanged(int width, int height) {
+ float aspectRatio = width / (float) height;
+ mPreviewVideoHolder.getPlayerFrame().setAspectRatio(aspectRatio);
+
+ // We want to show the player view only when we have the correct aspect ratio.
+ mPreviewVideoHolder.getPlayerContainer().setVisibility(View.VISIBLE);
+ mPreviewVideoHolder.getThumbnailView().setVisibility(View.GONE);
+ }
+
+ private void initUI() {
+ // We show the thumbnail view till the player is ready and when we know the
+ // media size, then we hide the thumbnail view.
+ mPreviewVideoHolder.getPlayerContainer().setVisibility(View.INVISIBLE);
+ mPreviewVideoHolder.getThumbnailView().setVisibility(View.VISIBLE);
+ mPreviewVideoHolder.getPlayerControlsRoot().setVisibility(View.GONE);
+
+ updatePlayPauseButtonState(false /* isPlaying */);
+ mPreviewVideoHolder.getPlayPauseButton().setOnClickListener(mPlayPauseButtonClickListener);
+
+ updateMuteButtonState(mMuteStatus.isVolumeMuted());
+ mPreviewVideoHolder.getMuteButton().setOnClickListener(mMuteButtonClickListener);
+
+ updateAccessibilityState(mAccessibilityManager.isEnabled());
+ mAccessibilityManager.addAccessibilityStateChangeListener(
+ mAccessibilityStateChangeListener);
+ }
+
+ private void tearDownUI() {
+ mAccessibilityManager.removeAccessibilityStateChangeListener(
+ mAccessibilityStateChangeListener);
+ mPreviewVideoHolder.getPlayPauseButton().setOnClickListener(null);
+ mPreviewVideoHolder.getMuteButton().setOnClickListener(null);
+ mPreviewVideoHolder.getPlayerContainer().setOnClickListener(null);
+ }
+
+ private void updateAccessibilityState(boolean enabled) {
+ mIsAccessibilityEnabled = enabled;
+ mPreviewVideoHolder.getPlayerContainer().setOnClickListener(
+ mIsAccessibilityEnabled ? null : mPlayerContainerClickListener);
+ updatePlayerControlsVisibilityState(mIsAccessibilityEnabled);
+ }
+
+ private void updatePlayPauseButtonState(boolean isPlaying) {
+ ImageButton playPauseButton = mPreviewVideoHolder.getPlayPauseButton();
+ Context context = playPauseButton.getContext();
+ playPauseButton.setContentDescription(
+ context.getString(
+ isPlaying ? R.string.picker_pause_video : R.string.picker_play_video));
+ playPauseButton.setImageResource(
+ isPlaying ? R.drawable.ic_preview_pause : R.drawable.ic_preview_play);
+ }
+
+ private void updateMuteButtonState(boolean isVolumeMuted) {
+ ImageButton muteButton = mPreviewVideoHolder.getMuteButton();
+ Context context = muteButton.getContext();
+ muteButton.setContentDescription(
+ context.getString(
+ isVolumeMuted ? R.string.picker_unmute_video : R.string.picker_mute_video));
+ muteButton.setImageResource(
+ isVolumeMuted ? R.drawable.ic_volume_off : R.drawable.ic_volume_up);
+ }
+
+ private void hidePlayerControlsWithDelay() {
+ mPreviewVideoHolder.getPlayerControlsRoot().postDelayed(
+ () -> updatePlayerControlsVisibilityState(false /* visible */),
+ PLAYER_CONTROL_ON_PLAY_TIMEOUT_MS);
+ }
+
+ private void updatePlayerControlsVisibilityState(boolean visible) {
+ mPreviewVideoHolder.getPlayerControlsRoot().setVisibility(
+ visible ? View.VISIBLE : View.GONE);
+ mPlayerControlsVisibilityStatus.setShouldShowPlayerControlsForNextItem(visible);
+ }
+
+ private static final class SurfaceChangeData {
+
+ private int mFormat;
+ private int mWidth;
+ private int mHeight;
+
+ SurfaceChangeData(int format, int width, int height) {
+ mFormat = format;
+ mWidth = width;
+ mHeight = height;
+ }
+
+ int getFormat() {
+ return mFormat;
+ }
+
+ int getWidth() {
+ return mWidth;
+ }
+
+ int getHeight() {
+ return mHeight;
+ }
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/remotepreview/RemoteSurfaceController.java b/src/com/android/providers/media/photopicker/ui/remotepreview/RemoteSurfaceController.java
new file mode 100644
index 0000000..37a5921
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/remotepreview/RemoteSurfaceController.java
@@ -0,0 +1,307 @@
+/*
+ * 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.
+ */
+
+package com.android.providers.media.photopicker.ui.remotepreview;
+
+import static android.provider.CloudMediaProvider.CloudMediaSurfaceController;
+import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback;
+import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PLAYBACK_STATE_BUFFERING;
+import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PLAYBACK_STATE_COMPLETED;
+import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PLAYBACK_STATE_MEDIA_SIZE_CHANGED;
+import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PLAYBACK_STATE_PAUSED;
+import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PLAYBACK_STATE_READY;
+import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PLAYBACK_STATE_STARTED;
+import static android.provider.CloudMediaProviderContract.EXTRA_LOOPING_PLAYBACK_ENABLED;
+import static android.provider.CloudMediaProviderContract.EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED;
+
+import android.annotation.DurationMillisLong;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.graphics.Point;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.view.Surface;
+
+import androidx.annotation.NonNull;
+
+import com.android.providers.media.PickerUriResolver;
+
+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.Player.State;
+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.upstream.ContentDataSource;
+import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
+import com.google.android.exoplayer2.util.Clock;
+import com.google.android.exoplayer2.video.VideoSize;
+
+/**
+ * Implements a {@link CloudMediaSurfaceController} for a cloud provider authority and initializes
+ * an ExoPlayer instance to render cloud media to {@link Surface} instances.
+ */
+public class RemoteSurfaceController extends CloudMediaSurfaceController {
+ private static final String TAG = "RemoteSurfaceController";
+
+ // 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 final String mAuthority;
+ private final Context mContext;
+ private final CloudMediaSurfaceStateChangedCallback mCallback;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final Player.Listener mEventListener = new Player.Listener() {
+ @Override
+ public void onPlaybackStateChanged(@State int state) {
+ Log.d(TAG, "Received player event " + state);
+
+ switch (state) {
+ case Player.STATE_READY:
+ mCallback.setPlaybackState(mCurrentSurfaceId, PLAYBACK_STATE_READY,
+ null);
+ return;
+ case Player.STATE_BUFFERING:
+ mCallback.setPlaybackState(mCurrentSurfaceId, PLAYBACK_STATE_BUFFERING,
+ null);
+ return;
+ case Player.STATE_ENDED:
+ mCallback.setPlaybackState(mCurrentSurfaceId, PLAYBACK_STATE_COMPLETED,
+ null);
+ return;
+ default:
+ }
+ }
+
+ @Override
+ public void onIsPlayingChanged(boolean isPlaying) {
+ mCallback.setPlaybackState(mCurrentSurfaceId, isPlaying ? PLAYBACK_STATE_STARTED :
+ PLAYBACK_STATE_PAUSED, null);
+ }
+
+ @Override
+ public void onVideoSizeChanged(VideoSize videoSize) {
+ Point size = new Point(videoSize.width, videoSize.height);
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(ContentResolver.EXTRA_SIZE, size);
+ mCallback.setPlaybackState(mCurrentSurfaceId, PLAYBACK_STATE_MEDIA_SIZE_CHANGED,
+ bundle);
+ }
+ };
+
+ private boolean mEnableLoop;
+ private boolean mMuteAudio;
+ private ExoPlayer mPlayer;
+ private int mCurrentSurfaceId = -1;
+
+ public RemoteSurfaceController(Context context, String authority, boolean enableLoop,
+ boolean muteAudio, CloudMediaSurfaceStateChangedCallback callback) {
+ mAuthority = authority;
+ mCallback = callback;
+ mContext = context;
+ mEnableLoop = enableLoop;
+ mMuteAudio = muteAudio;
+ Log.d(TAG, "Surface controller created.");
+ }
+
+ @Override
+ public void onPlayerCreate() {
+ mHandler.post(() -> {
+ mPlayer = createExoPlayer();
+ mPlayer.addListener(mEventListener);
+ updateLoopingPlaybackStatus();
+ updateAudioMuteStatus();
+ Log.d(TAG, "Player created.");
+ });
+ }
+
+ @Override
+ public void onPlayerRelease() {
+ mHandler.post(() -> {
+ mPlayer.removeListener(mEventListener);
+ mPlayer.release();
+ mPlayer = null;
+ Log.d(TAG, "Player released.");
+ });
+ }
+
+ @Override
+ public void onSurfaceCreated(int surfaceId, @NonNull Surface surface,
+ @NonNull String mediaId) {
+ mHandler.post(() -> {
+ try {
+ // onSurfaceCreated may get called while the player is already rendering on a
+ // different surface. In that case, pause the player before preparing it for
+ // rendering on the new surface.
+ // Unfortunately, Exoplayer#stop doesn't seem to work here. If we call stop(),
+ // as soon as the player becomes ready again, it automatically starts to play
+ // the new media. The reason is that Exoplayer treats play/pause as calls to
+ // the method Exoplayer#setPlayWhenReady(boolean) with true and false
+ // respectively. So, if we don't pause(), then since the previous play() call
+ // had set setPlayWhenReady to true, the player would start the playback as soon
+ // as it gets ready with the new media item.
+ if (mPlayer.isPlaying()) {
+ mPlayer.pause();
+ }
+
+ mCurrentSurfaceId = surfaceId;
+
+ final Uri mediaUri = PickerUriResolver.getMediaUri(mAuthority).buildUpon()
+ .appendPath(mediaId).build();
+ mPlayer.setMediaItem(MediaItem.fromUri(mediaUri));
+ mPlayer.setVideoSurface(surface);
+ mPlayer.prepare();
+
+ Log.d(TAG, "Surface prepared: " + surfaceId + ". Surface: " + surface
+ + ". MediaId: " + mediaId);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Error preparing player with surface.", e);
+ }
+ });
+ }
+
+ @Override
+ public void onSurfaceChanged(int surfaceId, int format, int width, int height) {
+ Log.d(TAG, "Surface changed: " + surfaceId + ". Format: " + format + ". Width: "
+ + width + ". Height: " + height);
+ }
+
+ @Override
+ public void onSurfaceDestroyed(int surfaceId) {
+ mHandler.post(() -> {
+ if (mCurrentSurfaceId != surfaceId) {
+ // This means that the player is already using some other surface, hence
+ // nothing to do.
+ return;
+ }
+ if (mPlayer.isPlaying()) {
+ mPlayer.stop();
+ }
+ mPlayer.clearVideoSurface();
+ mCurrentSurfaceId = -1;
+
+ Log.d(TAG, "Surface released: " + surfaceId);
+ });
+ }
+
+ @Override
+ public void onMediaPlay(int surfaceId) {
+ mHandler.post(() -> {
+ mPlayer.play();
+ Log.d(TAG, "Media played: " + surfaceId);
+ });
+ }
+
+ @Override
+ public void onMediaPause(int surfaceId) {
+ mHandler.post(() -> {
+ if (mPlayer.isPlaying()) {
+ mPlayer.pause();
+ Log.d(TAG, "Media paused: " + surfaceId);
+ }
+ });
+ }
+
+ @Override
+ public void onMediaSeekTo(int surfaceId, @DurationMillisLong long timestampMillis) {
+ mHandler.post(() -> {
+ mPlayer.seekTo((int) timestampMillis);
+ Log.d(TAG, "Media seeked: " + surfaceId + ". Timestamp: " + timestampMillis);
+ });
+ }
+
+ @Override
+ public void onConfigChange(@NonNull Bundle config) {
+ final boolean enableLoop = config.getBoolean(EXTRA_LOOPING_PLAYBACK_ENABLED,
+ mEnableLoop);
+ final boolean muteAudio = config.getBoolean(EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED,
+ mMuteAudio);
+ mHandler.post(() -> {
+ if (mEnableLoop != enableLoop) {
+ mEnableLoop = enableLoop;
+ updateLoopingPlaybackStatus();
+ }
+
+ if (mMuteAudio != muteAudio) {
+ mMuteAudio = muteAudio;
+ updateAudioMuteStatus();
+ }
+ });
+ Log.d(TAG, "Config changed. Updated config params: " + config);
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.d(TAG, "Surface controller destroyed.");
+ }
+
+ 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)).build();
+ }
+
+ private void updateLoopingPlaybackStatus() {
+ mPlayer.setRepeatMode(mEnableLoop ? Player.REPEAT_MODE_ONE : Player.REPEAT_MODE_OFF);
+ }
+
+ private void updateAudioMuteStatus() {
+ if (mMuteAudio) {
+ mPlayer.setVolume(0f);
+ } else {
+ AudioManager audioManager = mContext.getSystemService(AudioManager.class);
+ if (audioManager == null) {
+ Log.e(TAG, "Couldn't find AudioManager while trying to set volume,"
+ + " unable to set volume");
+ return;
+ }
+ mPlayer.setVolume(audioManager.getStreamVolume(AudioManager.STREAM_MUSIC));
+ }
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/remotepreview/SurfaceControllerProxy.java b/src/com/android/providers/media/photopicker/ui/remotepreview/SurfaceControllerProxy.java
new file mode 100644
index 0000000..f1172cc
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/remotepreview/SurfaceControllerProxy.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+package com.android.providers.media.photopicker.ui.remotepreview;
+
+import android.annotation.DurationMillisLong;
+import android.annotation.NonNull;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.provider.ICloudMediaSurfaceController;
+import android.view.Surface;
+
+/**
+ * Wrapper over {@link ICloudMediaSurfaceController} which manages player creation/release based on
+ * presence of active surfaces.
+ *
+ * <p>This class is not thread-safe and the methods are meant to be always called on the main
+ * thread.
+ * TODO(b/216414060): Handle binder call failures and propagate to the caller.
+ */
+final class SurfaceControllerProxy {
+
+ private final ICloudMediaSurfaceController mController;
+
+ private int mNumActiveSurfaces = 0;
+
+ SurfaceControllerProxy(@NonNull ICloudMediaSurfaceController controller) {
+ mController = controller;
+ }
+
+ void onSurfaceCreated(int surfaceId, @NonNull Surface surface,
+ @NonNull String mediaId) throws RemoteException {
+ if (mNumActiveSurfaces == 0) {
+ onPlayerCreate();
+ }
+
+ mNumActiveSurfaces++;
+ mController.onSurfaceCreated(surfaceId, surface, mediaId);
+ }
+
+ void onSurfaceChanged(int surfaceId, int format, int width, int height) throws RemoteException {
+ mController.onSurfaceChanged(surfaceId, format, width, height);
+ }
+
+ void onSurfaceDestroyed(int surfaceId) throws RemoteException {
+ mController.onSurfaceDestroyed(surfaceId);
+ mNumActiveSurfaces--;
+
+ if (mNumActiveSurfaces == 0) {
+ onPlayerRelease();
+ }
+ }
+
+ void onMediaPlay(int surfaceId) throws RemoteException {
+ mController.onMediaPlay(surfaceId);
+ }
+
+ void onMediaPause(int surfaceId) throws RemoteException {
+ mController.onMediaPause(surfaceId);
+ }
+
+ void onMediaSeekTo(int surfaceId, @DurationMillisLong long timestampMillis)
+ throws RemoteException {
+ mController.onMediaSeekTo(surfaceId, timestampMillis);
+ }
+
+ void onConfigChange(@NonNull Bundle bundle) throws RemoteException {
+ mController.onConfigChange(bundle);
+ }
+
+ void onDestroy() throws RemoteException {
+ mController.onDestroy();
+ }
+
+ private void onPlayerCreate() throws RemoteException {
+ mController.onPlayerCreate();
+ }
+
+ private void onPlayerRelease() throws RemoteException {
+ mController.onPlayerRelease();
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/util/CrossProfileUtils.java b/src/com/android/providers/media/photopicker/util/CrossProfileUtils.java
new file mode 100644
index 0000000..301b623
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/util/CrossProfileUtils.java
@@ -0,0 +1,89 @@
+/*
+ * 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.util;
+
+import android.content.ContentProviderClient;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.UserManager;
+import android.provider.MediaStore;
+import android.util.Log;
+
+import com.android.providers.media.MediaProvider;
+import com.android.providers.media.photopicker.data.model.UserId;
+
+/**
+ * A utility class for cross-profile usage.
+ */
+public class CrossProfileUtils {
+ private static final String TAG = "CrossProfileUtils";
+
+ /**
+ * Whether {@link MediaStore#ACTION_PICK_IMAGES} intent is allowed to show cross profile content
+ * for the current user profile. This can be regulated by device admin policies.
+ *
+ * @return {@code true} if the current user profile can access cross profile content via
+ * {@link MediaStore#ACTION_PICK_IMAGES}.
+ *
+ * Note: For simplicity this function assumes that the caller is only checking for
+ * {@link MediaStore#ACTION_PICK_IMAGES} intent, please modify the logic if we want to check
+ * for multiple intents.
+ */
+ public static boolean isIntentAllowedCrossProfileAccess(Intent intent,
+ PackageManager packageManager) {
+ intent.setComponent(null);
+ intent.setPackage(null);
+ for (ResolveInfo info : packageManager.queryIntentActivities(intent,
+ PackageManager.MATCH_DEFAULT_ONLY)) {
+ if (info != null && info.isCrossProfileIntentForwarderActivity()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Whether {@link MediaProvider} for user profile of {@code userId} is available or not
+ *
+ * return {@code false} if managed profile is turned off
+ */
+ public static boolean isMediaProviderAvailable(UserId userId, Context context) {
+ try (ContentProviderClient client = userId.getContentResolver(context)
+ .acquireUnstableContentProviderClient(MediaStore.AUTHORITY)) {
+ if (client != null) {
+ return true;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Unable to get content resolver for the given userId: " + userId, e);
+ }
+ return false;
+ }
+
+ /**
+ * Whether the given profile is in quiet mode or not.
+ * Notes: Quiet mode is only supported for managed profiles.
+ *
+ * @param userId The user id of the profile to be queried.
+ * @return true if the profile is in quiet mode, false otherwise.
+ */
+ public static boolean isQuietModeEnabled(UserId userId, Context context) {
+ final UserManager userManager = context.getSystemService(UserManager.class);
+ return userManager.isQuietModeEnabled(userId.getUserHandle());
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/util/CursorUtils.java b/src/com/android/providers/media/photopicker/util/CursorUtils.java
new file mode 100644
index 0000000..a992f4e
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/util/CursorUtils.java
@@ -0,0 +1,81 @@
+/*
+ * 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.util;
+
+import android.database.Cursor;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Provide the utility methods to handle cursor.
+ */
+public class CursorUtils {
+
+ /**
+ * Get the string from the {@code cursor} with the {@code columnName}.
+ *
+ * @param cursor the cursor to be parsed
+ * @param columnName the column name of the value
+ * @return the string value from the {@code cursor}, or {@code null} when {@code cursor} doesn't
+ * contain {@code columnName}
+ */
+ @Nullable
+ public static String getCursorString(@NonNull Cursor cursor, @NonNull String columnName) {
+ final int index = cursor.getColumnIndex(columnName);
+ return (index != -1) ? cursor.getString(index) : null;
+ }
+
+ /**
+ * Get the long value from the {@code cursor} with the {@code columnName}.
+ *
+ * @param cursor the cursor to be parsed
+ * @param columnName the column name of the value
+ * @return the long value from the {@code cursor}, or -1 when {@code cursor} doesn't contain
+ * {@code columnName}
+ */
+ public static long getCursorLong(@NonNull Cursor cursor, @NonNull String columnName) {
+ final int index = cursor.getColumnIndex(columnName);
+ if (index == -1) {
+ return -1;
+ }
+
+ final String value = cursor.getString(index);
+ if (value == null) {
+ return -1;
+ }
+
+ try {
+ return Long.parseLong(value);
+ } catch (NumberFormatException e) {
+ return -1;
+ }
+ }
+
+ /**
+ * Get the int value from the {@code cursor} with the {@code columnName}.
+ *
+ * @param cursor the cursor to be parsed
+ * @param columnName the column name of the value
+ * @return the int value from the {@code cursor}, or 0 when {@code cursor} doesn't contain
+ * {@code columnName}
+ */
+ public static int getCursorInt(@NonNull Cursor cursor, @NonNull String columnName) {
+ final int index = cursor.getColumnIndex(columnName);
+ return (index != -1) ? cursor.getInt(index) : 0;
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/util/DateTimeUtils.java b/src/com/android/providers/media/photopicker/util/DateTimeUtils.java
new file mode 100644
index 0000000..263256f
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/util/DateTimeUtils.java
@@ -0,0 +1,156 @@
+/*
+ * 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.util;
+
+import static android.icu.text.DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE;
+import static android.icu.text.RelativeDateTimeFormatter.Style.LONG;
+
+import android.icu.text.DateFormat;
+import android.icu.text.DisplayContext;
+import android.icu.text.RelativeDateTimeFormatter;
+import android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit;
+import android.icu.text.RelativeDateTimeFormatter.Direction;
+import android.icu.util.ULocale;
+import android.text.format.DateUtils;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.TextStyle;
+import java.time.temporal.ChronoUnit;
+import java.util.Locale;
+
+/**
+ * Provide the utility methods to handle date time.
+ */
+public class DateTimeUtils {
+
+ private static final String DATE_FORMAT_SKELETON_WITH_YEAR = "EMMMdy";
+ private static final String DATE_FORMAT_SKELETON_WITHOUT_YEAR = "EMMMd";
+ private static final String DATE_FORMAT_SKELETON_WITH_TIME = "MMMdyhmmss";
+
+ /**
+ * Formats a time according to the local conventions for PhotoGrid.
+ *
+ * If the difference of the date between the time and now is zero, show
+ * "Today".
+ * If the difference is 1, show "Yesterday".
+ * If the difference is less than 7, show the weekday. E.g. "Sunday".
+ * Otherwise, show the weekday and the date. E.g. "Sat, Jun 5".
+ * If they have different years, show the weekday, the date and the year.
+ * E.g. "Sat, Jun 5, 2021"
+ *
+ * @param when the time to be formatted. The unit is in milliseconds
+ * since January 1, 1970 00:00:00.0 UTC.
+ * @return the formatted string
+ */
+ public static String getDateHeaderString(long when) {
+ // Get the system time zone
+ final ZoneId zoneId = ZoneId.systemDefault();
+ final LocalDate nowDate = LocalDate.now(zoneId);
+
+ return getDateHeaderString(when, nowDate);
+ }
+
+ /**
+ * Formats a time according to the local conventions for content description.
+ *
+ * The format of the returned string is fixed to {@code DATE_FORMAT_SKELETON_WITH_TIME}.
+ * E.g. "Feb 2, 2022, 2:22:22 PM"
+ *
+ * @param when the time to be formatted. The unit is in milliseconds
+ * since January 1, 1970 00:00:00.0 UTC.
+ * @return the formatted string
+ */
+ public static String getDateTimeStringForContentDesc(long when) {
+ return getDateTimeString(when, DATE_FORMAT_SKELETON_WITH_TIME, Locale.getDefault());
+ }
+
+ @VisibleForTesting
+ static String getDateHeaderString(long when, LocalDate nowDate) {
+ // Get the system time zone
+ final ZoneId zoneId = ZoneId.systemDefault();
+ final LocalDate whenDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(when),
+ zoneId).toLocalDate();
+
+ final long dayDiff = ChronoUnit.DAYS.between(whenDate, nowDate);
+ if (dayDiff == 0) {
+ return getTodayString();
+ } else if (dayDiff == 1) {
+ return getYesterdayString();
+ } else if (dayDiff > 0 && dayDiff < 7) {
+ return whenDate.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.getDefault());
+ } else {
+ final String skeleton;
+ if (whenDate.getYear() == nowDate.getYear()) {
+ skeleton = DATE_FORMAT_SKELETON_WITHOUT_YEAR;
+ } else {
+ skeleton = DATE_FORMAT_SKELETON_WITH_YEAR;
+ }
+
+ return getDateTimeString(when, skeleton, Locale.getDefault());
+ }
+ }
+
+ @VisibleForTesting
+ static String getDateTimeString(long when, String skeleton, Locale locale) {
+ final DateFormat format = DateFormat.getInstanceForSkeleton(skeleton, locale);
+ format.setContext(DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE);
+ return format.format(when);
+ }
+
+ /**
+ * It is borrowed from {@link DateUtils} since it is no official API yet.
+ *
+ * @param oneMillis the first time. The unit is in milliseconds since
+ * January 1, 1970 00:00:00.0 UTC.
+ * @param twoMillis the second time. The unit is in milliseconds since
+ * January 1, 1970 00:00:00.0 UTC.
+ * @return True, the date is the same. Otherwise, return false.
+ */
+ public static boolean isSameDate(long oneMillis, long twoMillis) {
+ // Get the system time zone
+ final ZoneId zoneId = ZoneId.systemDefault();
+
+ final Instant oneInstant = Instant.ofEpochMilli(oneMillis);
+ final LocalDateTime oneLocalDateTime = LocalDateTime.ofInstant(oneInstant, zoneId);
+
+ final Instant twoInstant = Instant.ofEpochMilli(twoMillis);
+ final LocalDateTime twoLocalDateTime = LocalDateTime.ofInstant(twoInstant, zoneId);
+
+ return (oneLocalDateTime.getYear() == twoLocalDateTime.getYear())
+ && (oneLocalDateTime.getMonthValue() == twoLocalDateTime.getMonthValue())
+ && (oneLocalDateTime.getDayOfMonth() == twoLocalDateTime.getDayOfMonth());
+ }
+
+ @VisibleForTesting
+ static String getTodayString() {
+ final RelativeDateTimeFormatter fmt = RelativeDateTimeFormatter.getInstance(
+ ULocale.getDefault(), null, LONG, CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE);
+ return fmt.format(Direction.THIS, AbsoluteUnit.DAY);
+ }
+
+ @VisibleForTesting
+ static String getYesterdayString() {
+ final RelativeDateTimeFormatter fmt = RelativeDateTimeFormatter.getInstance(
+ ULocale.getDefault(), null, LONG, CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE);
+ return fmt.format(Direction.LAST, AbsoluteUnit.DAY);
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/util/LayoutModeUtils.java b/src/com/android/providers/media/photopicker/util/LayoutModeUtils.java
new file mode 100644
index 0000000..8409511
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/util/LayoutModeUtils.java
@@ -0,0 +1,73 @@
+/*
+ * 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.util;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Utility class for various layout modes that PhotoPicker supports.
+ */
+public class LayoutModeUtils {
+
+ public static final Mode MODE_PHOTOS_TAB = Mode.of(Mode.MODE_PHOTOS_TAB);
+ public static final Mode MODE_ALBUMS_TAB = Mode.of(Mode.MODE_ALBUMS_TAB);
+ public static final Mode MODE_ALBUM_PHOTOS_TAB = Mode.of(Mode.MODE_ALBUM_PHOTOS_TAB);
+ public static final Mode MODE_PREVIEW = Mode.of(Mode.MODE_PREVIEW);
+
+
+ public static class Mode {
+ public boolean isPhotosTabOrAlbumsTab;
+ public boolean isPreview;
+ @IntDef(prefix = { "MODE_" }, value = {
+ MODE_PHOTOS_TAB,
+ MODE_ALBUMS_TAB,
+ MODE_ALBUM_PHOTOS_TAB,
+ MODE_PREVIEW,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ModeType {}
+
+ public static final int MODE_PHOTOS_TAB = 1;
+ public static final int MODE_ALBUMS_TAB = 2;
+ public static final int MODE_ALBUM_PHOTOS_TAB = 3;
+ public static final int MODE_PREVIEW = 4;
+
+ public static Mode of(@Mode.ModeType int modeType) {
+ Mode mode = new Mode();
+ switch(modeType) {
+ case MODE_PHOTOS_TAB:
+ case MODE_ALBUMS_TAB:
+ mode.isPhotosTabOrAlbumsTab = true;
+ mode.isPreview = false;
+ break;
+ case MODE_ALBUM_PHOTOS_TAB:
+ mode.isPhotosTabOrAlbumsTab = false;
+ mode.isPreview = false;
+ break;
+ case MODE_PREVIEW:
+ mode.isPhotosTabOrAlbumsTab = false;
+ mode.isPreview = true;
+ break;
+ default:
+ }
+ return mode;
+ }
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/viewmodel/PickerViewModel.java b/src/com/android/providers/media/photopicker/viewmodel/PickerViewModel.java
new file mode 100644
index 0000000..0c5860b
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/viewmodel/PickerViewModel.java
@@ -0,0 +1,363 @@
+/*
+ * 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.viewmodel;
+
+import static com.android.providers.media.util.MimeUtils.isImageMimeType;
+import static com.android.providers.media.util.MimeUtils.isVideoMimeType;
+
+import android.app.Application;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.lifecycle.AndroidViewModel;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+
+import com.android.internal.logging.InstanceId;
+import com.android.internal.logging.InstanceIdSequence;
+import com.android.providers.media.photopicker.data.ItemsProvider;
+import com.android.providers.media.photopicker.data.MuteStatus;
+import com.android.providers.media.photopicker.data.Selection;
+import com.android.providers.media.photopicker.data.UserIdManager;
+import com.android.providers.media.photopicker.data.model.Category;
+import com.android.providers.media.photopicker.data.model.Item;
+import com.android.providers.media.photopicker.data.model.UserId;
+import com.android.providers.media.photopicker.metrics.PhotoPickerUiEventLogger;
+import com.android.providers.media.photopicker.util.DateTimeUtils;
+import com.android.providers.media.util.ForegroundThread;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * PickerViewModel to store and handle data for PhotoPickerActivity.
+ */
+public class PickerViewModel extends AndroidViewModel {
+ public static final String TAG = "PhotoPicker";
+
+ private static final int RECENT_MINIMUM_COUNT = 12;
+
+ private static final int INSTANCE_ID_MAX = 1 << 15;
+
+ private final Selection mSelection;
+ private final MuteStatus mMuteStatus;
+
+ // TODO(b/193857982): We keep these four data sets now, we may need to find a way to reduce the
+ // data set to reduce memories.
+ // The list of Items with all photos and videos
+ private MutableLiveData<List<Item>> mItemList;
+ // The list of Items with all photos and videos in category
+ private MutableLiveData<List<Item>> mCategoryItemList;
+ // The list of categories.
+ private MutableLiveData<List<Category>> mCategoryList;
+
+ private ItemsProvider mItemsProvider;
+ private UserIdManager mUserIdManager;
+
+ private InstanceId mInstanceId;
+ private PhotoPickerUiEventLogger mLogger;
+
+ private String mMimeTypeFilter = null;
+ private int mBottomSheetState;
+
+ private Category mCurrentCategory;
+
+ public PickerViewModel(@NonNull Application application) {
+ super(application);
+ final Context context = application.getApplicationContext();
+ mItemsProvider = new ItemsProvider(context);
+ mSelection = new Selection();
+ mUserIdManager = UserIdManager.create(context);
+ mMuteStatus = new MuteStatus();
+ mInstanceId = new InstanceIdSequence(INSTANCE_ID_MAX).newInstanceId();
+ mLogger = new PhotoPickerUiEventLogger();
+ }
+
+ @VisibleForTesting
+ public void setItemsProvider(@NonNull ItemsProvider itemsProvider) {
+ mItemsProvider = itemsProvider;
+ }
+
+ @VisibleForTesting
+ public void setUserIdManager(@NonNull UserIdManager userIdManager) {
+ mUserIdManager = userIdManager;
+ }
+
+ /**
+ * @return {@link UserIdManager} for this context.
+ */
+ public UserIdManager getUserIdManager() {
+ return mUserIdManager;
+ }
+
+ /**
+ * @return {@code mSelection} that manages the selection
+ */
+ public Selection getSelection() {
+ return mSelection;
+ }
+
+
+ /**
+ * @return {@code mMuteStatus} that tracks the volume mute status of the video preview
+ */
+ public MuteStatus getMuteStatus() {
+ return mMuteStatus;
+ }
+
+ /**
+ * Reset to personal profile mode.
+ */
+ public void resetToPersonalProfile() {
+ // 1. Clear Selected items
+ mSelection.clearSelectedItems();
+ // 2. Change profile to personal user
+ mUserIdManager.setPersonalAsCurrentUserProfile();
+ // 3. Update Item and Category lists
+ updateItems();
+ updateCategories();
+ }
+
+ /**
+ * @return the list of Items with all photos and videos {@link #mItemList} on the device.
+ */
+ public LiveData<List<Item>> getItems() {
+ if (mItemList == null) {
+ updateItems();
+ }
+ return mItemList;
+ }
+
+ private List<Item> loadItems(Category category, UserId userId) {
+ final List<Item> items = new ArrayList<>();
+
+ try (Cursor cursor = mItemsProvider.getItems(category, /* offset */ 0,
+ /* limit */ -1, mMimeTypeFilter, 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");
+ return items;
+ }
+
+ // We only add the RECENT header on the PhotosTabFragment with CATEGORY_DEFAULT. In this
+ // case, we call this method {loadItems} with null category. When the category is not
+ // empty, we don't show the RECENT header.
+ final boolean showRecent = category.isDefault();
+
+ int recentSize = 0;
+ long currentDateTaken = 0;
+
+ if (showRecent) {
+ // add Recent date header
+ items.add(Item.createDateItem(0));
+ }
+ while (cursor.moveToNext()) {
+ // TODO(b/188394433): Return userId in the cursor so that we do not need to pass it
+ // here again.
+ final Item item = Item.fromCursor(cursor, userId);
+ final long dateTaken = item.getDateTaken();
+ // the minimum count of items in recent is not reached
+ if (showRecent && recentSize < RECENT_MINIMUM_COUNT) {
+ recentSize++;
+ currentDateTaken = dateTaken;
+ }
+
+ // The date taken of these two images are not on the
+ // same day, add the new date header.
+ if (!DateTimeUtils.isSameDate(currentDateTaken, dateTaken)) {
+ items.add(Item.createDateItem(dateTaken));
+ currentDateTaken = dateTaken;
+ }
+ items.add(item);
+ }
+ }
+
+ Log.d(TAG, "Loaded " + items.size() + " items in " + category + " for user "
+ + userId.toString());
+ return items;
+ }
+
+ private void loadItemsAsync() {
+ final UserId userId = mUserIdManager.getCurrentUserProfileId();
+ ForegroundThread.getExecutor().execute(() -> {
+ mItemList.postValue(loadItems(Category.DEFAULT, userId));
+ });
+ }
+
+ /**
+ * Update the item List {@link #mItemList}
+ */
+ public void updateItems() {
+ if (mItemList == null) {
+ mItemList = new MutableLiveData<>();
+ }
+ loadItemsAsync();
+ }
+
+ /**
+ * Get the list of all photos and videos with the specific {@code category} on the device.
+ *
+ * In our use case, we only keep the list of current category {@link #mCurrentCategory} in
+ * {@link #mCategoryItemList}. If the {@code category} and {@link #mCurrentCategory} are
+ * different, we will create the new LiveData to {@link #mCategoryItemList}.
+ *
+ * @param category the category we want to be queried
+ * @return the list of all photos and videos with the specific {@code category}
+ * {@link #mCategoryItemList}
+ */
+ public LiveData<List<Item>> getCategoryItems(@NonNull Category category) {
+ if (mCategoryItemList == null || !TextUtils.equals(mCurrentCategory.getId(),
+ category.getId())) {
+ mCategoryItemList = new MutableLiveData<>();
+ mCurrentCategory = category;
+ }
+ updateCategoryItems();
+ return mCategoryItemList;
+ }
+
+ private void loadCategoryItemsAsync() {
+ final UserId userId = mUserIdManager.getCurrentUserProfileId();
+ ForegroundThread.getExecutor().execute(() -> {
+ mCategoryItemList.postValue(loadItems(mCurrentCategory, userId));
+ });
+ }
+
+ /**
+ * Update the item List with the {@link #mCurrentCategory} {@link #mCategoryItemList}
+ *
+ * @throws IllegalStateException category and category items is not initiated before calling
+ * this method
+ */
+ @VisibleForTesting
+ public void updateCategoryItems() {
+ if (mCategoryItemList == null || mCurrentCategory == null) {
+ throw new IllegalStateException("mCurrentCategory and mCategoryItemList are not"
+ + " initiated. Please call getCategoryItems before calling this method");
+ }
+ loadCategoryItemsAsync();
+ }
+
+ /**
+ * @return the list of Categories {@link #mCategoryList}
+ */
+ public LiveData<List<Category>> getCategories() {
+ if (mCategoryList == null) {
+ updateCategories();
+ }
+ return mCategoryList;
+ }
+
+ private List<Category> loadCategories(UserId userId) {
+ final List<Category> categoryList = new ArrayList<>();
+ try (final Cursor cursor = mItemsProvider.getCategories(mMimeTypeFilter, userId)) {
+ if (cursor == null || cursor.getCount() == 0) {
+ Log.d(TAG, "Didn't receive any categories, either cursor is null or"
+ + " cursor count is zero");
+ return categoryList;
+ }
+
+ while (cursor.moveToNext()) {
+ final Category category = Category.fromCursor(cursor, userId);
+ categoryList.add(category);
+ }
+
+ Log.d(TAG,
+ "Loaded " + categoryList.size() + " categories for user " + userId.toString());
+ }
+ return categoryList;
+ }
+
+ private void loadCategoriesAsync() {
+ final UserId userId = mUserIdManager.getCurrentUserProfileId();
+ ForegroundThread.getExecutor().execute(() -> {
+ mCategoryList.postValue(loadCategories(userId));
+ });
+ }
+
+ /**
+ * Update the category List {@link #mCategoryList}
+ */
+ public void updateCategories() {
+ if (mCategoryList == null) {
+ mCategoryList = new MutableLiveData<>();
+ }
+ loadCategoriesAsync();
+ }
+
+ /**
+ * Return whether the {@link #mMimeTypeFilter} is {@code null} or not
+ */
+ public boolean hasMimeTypeFilter() {
+ return !TextUtils.isEmpty(mMimeTypeFilter);
+ }
+
+ /**
+ * Parse values from {@code intent} and set corresponding fields
+ */
+ public void parseValuesFromIntent(Intent intent) throws IllegalArgumentException {
+ mUserIdManager.setIntentAndCheckRestrictions(intent);
+
+ final String mimeType = intent.getType();
+ if (isMimeTypeMedia(mimeType)) {
+ mMimeTypeFilter = mimeType;
+ }
+
+ mSelection.parseSelectionValuesFromIntent(intent);
+ }
+
+ private static boolean isMimeTypeMedia(@Nullable String mimeType) {
+ return isImageMimeType(mimeType) || isVideoMimeType(mimeType);
+ }
+
+ /**
+ * Set BottomSheet state
+ */
+ public void setBottomSheetState(int state) {
+ mBottomSheetState = state;
+ }
+
+ /**
+ * @return BottomSheet state
+ */
+ public int getBottomSheetState() {
+ return mBottomSheetState;
+ }
+
+ public void logPickerOpened(String callingPackage) {
+ if (getUserIdManager().isManagedUserSelected()) {
+ mLogger.logPickerOpenWork(mInstanceId, callingPackage);
+ } else {
+ mLogger.logPickerOpenPersonal(mInstanceId, callingPackage);
+ }
+ }
+
+ public InstanceId getInstanceId() {
+ return mInstanceId;
+ }
+
+ public void setInstanceId(InstanceId parcelable) {
+ mInstanceId = parcelable;
+ }
+}
diff --git a/src/com/android/providers/media/scan/ModernMediaScanner.java b/src/com/android/providers/media/scan/ModernMediaScanner.java
index 8927bfa..41f53d5 100644
--- a/src/com/android/providers/media/scan/ModernMediaScanner.java
+++ b/src/com/android/providers/media/scan/ModernMediaScanner.java
@@ -101,6 +101,7 @@
import com.android.providers.media.util.LongArray;
import com.android.providers.media.util.Metrics;
import com.android.providers.media.util.MimeUtils;
+import com.android.providers.media.util.SpecialFormatDetector;
import com.android.providers.media.util.XmpInterface;
import java.io.File;
@@ -175,14 +176,6 @@
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
static final int MAX_EXCLUDE_DIRS = 450;
- private static final Pattern PATTERN_VISIBLE = Pattern.compile(
- "(?i)^/storage/[^/]+(?:/[0-9]+)?$");
- private static final Pattern PATTERN_INVISIBLE = Pattern.compile(
- "(?i)^/storage/[^/]+(?:/[0-9]+)?/"
- + "(?:(?:Android/(?:data|obb|sandbox)$)|"
- + "(?:\\.transforms$)|"
- + "(?:(?:Movies|Music|Pictures)/.thumbnails$))");
-
private static final Pattern PATTERN_YEAR = Pattern.compile("([1-9][0-9][0-9][0-9])");
private static final Pattern PATTERN_ALBUM_ART = Pattern.compile(
@@ -666,6 +659,8 @@
synchronized (mPendingCleanDirectories) {
if (mIsDirectoryTreeDirty) {
// Directory tree is dirty, continue scanning subtree.
+ } else if (FileUtils.getTopLevelNoMedia(dir.toFile()) == null) {
+ // No nomedia file found, continue scanning.
} else if (FileUtils.isDirectoryDirty(FileUtils.getTopLevelNoMedia(dir.toFile()))) {
// Track the directory dirty status for directory tree in mIsDirectoryDirty.
// This removes additional dirty state check for subdirectories of nomedia
@@ -1389,6 +1384,7 @@
final XmpInterface xmp = XmpInterface.fromContainer(exif);
withXmpValues(op, xmp, mimeType);
+ op.withValue(FileColumns._SPECIAL_FORMAT, SpecialFormatDetector.detect(exif, file));
} catch (Exception e) {
logTroubleScanning(file, e);
}
@@ -1677,14 +1673,14 @@
// Handle well-known paths that should always be visible or invisible,
// regardless of .nomedia presence
- if (PATTERN_VISIBLE.matcher(dir.getAbsolutePath()).matches()) {
+ if (FileUtils.shouldBeVisible(dir.getAbsolutePath())) {
// Well known paths can never be a hidden directory. Delete any non-standard nomedia
// presence in well known path.
nomedia.delete();
return true;
}
- if (PATTERN_INVISIBLE.matcher(dir.getAbsolutePath()).matches()) {
+ if (FileUtils.shouldBeInvisible(dir.getAbsolutePath())) {
// Create the .nomedia file in paths that are not scannable. This is useful when user
// ejects the SD card and brings it to an older device and its media scanner can
// now correctly identify these paths as not scannable.
diff --git a/src/com/android/providers/media/scan/UnreliableVolumeScanner.java b/src/com/android/providers/media/scan/UnreliableVolumeScanner.java
new file mode 100644
index 0000000..58715ad
--- /dev/null
+++ b/src/com/android/providers/media/scan/UnreliableVolumeScanner.java
@@ -0,0 +1,86 @@
+/*
+ * 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.scan;
+
+import android.content.ContentValues;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import static com.android.providers.media.photopicker.data.UnreliableVolumeDatabaseHelper.MediaColumns.DATE_MODIFIED;
+import static com.android.providers.media.photopicker.data.UnreliableVolumeDatabaseHelper.MediaColumns.DISPLAY_NAME;
+import static com.android.providers.media.photopicker.data.UnreliableVolumeDatabaseHelper.MediaColumns.MIME_TYPE;
+import static com.android.providers.media.photopicker.data.UnreliableVolumeDatabaseHelper.MediaColumns.SIZE_BYTES;
+import static com.android.providers.media.photopicker.data.UnreliableVolumeDatabaseHelper.MediaColumns._DATA;
+
+import static com.android.providers.media.util.MimeUtils.isImageMimeType;
+import static com.android.providers.media.util.MimeUtils.isVideoMimeType;
+import static com.android.providers.media.util.MimeUtils.resolveMimeType;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.List;
+
+public class UnreliableVolumeScanner {
+ private final String TAG = "UnreliableVolumeScanner";
+
+ private boolean isAllowedMimeType(String mimeType) {
+ return (isImageMimeType(mimeType) || isVideoMimeType(mimeType));
+ }
+
+ private @NonNull ContentValues addDataFromFile(File file, String mimeType) {
+ ContentValues fileData = new ContentValues();
+ fileData.put(SIZE_BYTES, file.length());
+ fileData.put(_DATA, file.getPath());
+ fileData.put(MIME_TYPE, mimeType);
+ fileData.put(DATE_MODIFIED, file.lastModified());
+ fileData.put(DISPLAY_NAME, file.getName());
+
+ return fileData;
+ }
+
+ /**
+ * @return list of image and video data from the unreliable volume {@code path}
+ */
+ public @NonNull List<ContentValues> scanAndGetUnreliableVolFileInfo(Path path)
+ throws IOException {
+ List<ContentValues> unreliableVolumeData = new ArrayList<>();
+ Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+ String mimeType = resolveMimeType(file.toFile());
+ if (isAllowedMimeType(mimeType)) {
+ unreliableVolumeData.add(addDataFromFile(file.toFile(), mimeType));
+ }
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult visitFileFailed(Path file, IOException exc) {
+ Log.w(TAG, "File read failed for: " + file.toString());
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ return unreliableVolumeData;
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/providers/media/util/BackgroundThread.java b/src/com/android/providers/media/util/BackgroundThread.java
deleted file mode 100644
index 90d10a3..0000000
--- a/src/com/android/providers/media/util/BackgroundThread.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2019 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.util;
-
-import android.os.Handler;
-import android.os.HandlerThread;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Thread for asynchronous event processing. This thread is configured as
- * {@link android.os.Process#THREAD_PRIORITY_BACKGROUND}, which means fewer CPU
- * resources will be dedicated to it, and it will "have less chance of impacting
- * the responsiveness of the user interface."
- * <p>
- * This thread is best suited for tasks that the user is not actively waiting
- * for, or for tasks that the user expects to be executed eventually.
- *
- * @see ForegroundThread
- */
-public final class BackgroundThread extends HandlerThread {
- private static BackgroundThread sInstance;
- private static Handler sHandler;
- private static HandlerExecutor sHandlerExecutor;
-
- private BackgroundThread() {
- super("bg", android.os.Process.THREAD_PRIORITY_BACKGROUND);
- }
-
- private static void ensureThreadLocked() {
- if (sInstance == null) {
- sInstance = new BackgroundThread();
- sInstance.start();
- sHandler = new Handler(sInstance.getLooper());
- sHandlerExecutor = new HandlerExecutor(sHandler);
- }
- }
-
- public static BackgroundThread get() {
- synchronized (BackgroundThread.class) {
- ensureThreadLocked();
- return sInstance;
- }
- }
-
- public static Handler getHandler() {
- synchronized (BackgroundThread.class) {
- ensureThreadLocked();
- return sHandler;
- }
- }
-
- public static Executor getExecutor() {
- synchronized (BackgroundThread.class) {
- ensureThreadLocked();
- return sHandlerExecutor;
- }
- }
-
- public static void waitForIdle() {
- final CountDownLatch latch = new CountDownLatch(1);
- getExecutor().execute(() -> {
- latch.countDown();
- });
- try {
- latch.await(30, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- throw new IllegalStateException(e);
- }
- }
-}
diff --git a/src/com/android/providers/media/util/DatabaseUtils.java b/src/com/android/providers/media/util/DatabaseUtils.java
index 66b7751..55efafc 100644
--- a/src/com/android/providers/media/util/DatabaseUtils.java
+++ b/src/com/android/providers/media/util/DatabaseUtils.java
@@ -535,6 +535,10 @@
return sb.toString();
}
+ public static String replaceMatchAnyChar(@NonNull String arg) {
+ return arg.replace('*', '%');
+ }
+
public static boolean parseBoolean(@Nullable Object value, boolean def) {
if (value instanceof Boolean) {
return (Boolean) value;
diff --git a/src/com/android/providers/media/util/FileUtils.java b/src/com/android/providers/media/util/FileUtils.java
index 72880bd..bbf04de 100644
--- a/src/com/android/providers/media/util/FileUtils.java
+++ b/src/com/android/providers/media/util/FileUtils.java
@@ -734,8 +734,8 @@
extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
}
- if (MimeUtils.equalIgnoreCase(mimeType, mimeTypeFromExt)
- || MimeUtils.equalIgnoreCase(ext, extFromMimeType)) {
+ if (StringUtils.equalIgnoreCase(mimeType, mimeTypeFromExt)
+ || StringUtils.equalIgnoreCase(ext, extFromMimeType)) {
// Extension maps back to requested MIME type; allow it
} else {
// No match; insist that create file matches requested MIME
@@ -994,6 +994,15 @@
private static final Pattern PATTERN_OBB_OR_CHILD_RELATIVE_PATH = Pattern.compile(
"(?i)^Android/obb(?:/.*)?$");
+ private static final Pattern PATTERN_VISIBLE = Pattern.compile(
+ "(?i)^/storage/[^/]+(?:/[0-9]+)?$");
+
+ private static final Pattern PATTERN_INVISIBLE = Pattern.compile(
+ "(?i)^/storage/[^/]+(?:/[0-9]+)?/"
+ + "(?:(?:Android/(?:data|obb|sandbox)$)|"
+ + "(?:\\.transforms$)|"
+ + "(?:(?:Movies|Music|Pictures)/.thumbnails$))");
+
/**
* The recordings directory. This is used for R OS. For S OS or later,
* we use {@link Environment#DIRECTORY_RECORDINGS} directly.
@@ -1101,9 +1110,7 @@
}
public static @Nullable String extractRelativePath(@Nullable String data) {
- data = getCanonicalPath(data);
if (data == null) return null;
-
final Matcher matcher = PATTERN_RELATIVE_PATH.matcher(data);
if (matcher.find()) {
final int lastSlash = data.lastIndexOf('/');
@@ -1176,11 +1183,17 @@
@VisibleForTesting
static boolean isExternalMediaDirectory(@NonNull String path, String crossUserRoot) {
final String relativePath = extractRelativePath(path);
- if (relativePath != null) {
- final String externalMediaDir = (crossUserRoot == null || crossUserRoot.isEmpty())
- ? "Android/media" : crossUserRoot + "/Android/media";
- return relativePath.startsWith(externalMediaDir);
+ if (relativePath == null) {
+ return false;
}
+
+ if (StringUtils.startsWithIgnoreCase(relativePath, "Android/media")) {
+ return true;
+ }
+ if (!TextUtils.isEmpty(crossUserRoot)) {
+ return StringUtils.startsWithIgnoreCase(relativePath, crossUserRoot + "/Android/media");
+ }
+
return false;
}
@@ -1211,6 +1224,18 @@
return m.matches();
}
+ public static boolean shouldBeVisible(@Nullable String path) {
+ if (path == null) return false;
+ final Matcher m = PATTERN_VISIBLE.matcher(path);
+ return m.matches();
+ }
+
+ public static boolean shouldBeInvisible(@Nullable String path) {
+ if (path == null) return false;
+ final Matcher m = PATTERN_INVISIBLE.matcher(path);
+ return m.matches();
+ }
+
/**
* Returns the name of the top level directory, or null if the path doesn't go through the
* external storage directory.
@@ -1335,6 +1360,8 @@
// The relative path for files in the top directory is "/"
if (!"/".equals(values.getAsString(MediaColumns.RELATIVE_PATH))) {
values.put(MediaColumns.BUCKET_DISPLAY_NAME, file.getParentFile().getName());
+ } else {
+ values.putNull(MediaColumns.BUCKET_DISPLAY_NAME);
}
}
}
@@ -1456,6 +1483,48 @@
}
/**
+ * Returns true if the given File should be hidden (if it or any of its parents is hidden).
+ * This can be called before the file is created, to check if it will be hidden once created.
+ */
+ @VisibleForTesting
+ public static boolean shouldFileBeHidden(@NonNull File file) {
+ if (isFileHidden(file)) {
+ return true;
+ }
+
+ File parent = file.getParentFile();
+ while (parent != null) {
+ if (isDirectoryHidden(parent)) {
+ return true;
+ }
+ parent = parent.getParentFile();
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if the given dir should be hidden (if it or any of its parents is hidden).
+ * This can be called before the file is created, to check if it will be hidden once created.
+ */
+ @VisibleForTesting
+ public static boolean shouldDirBeHidden(@NonNull File file) {
+ if (isDirectoryHidden(file)) {
+ return true;
+ }
+
+ File parent = file.getParentFile();
+ while (parent != null) {
+ if (isDirectoryHidden(parent)) {
+ return true;
+ }
+ parent = parent.getParentFile();
+ }
+
+ return false;
+ }
+
+ /**
* Test if this given directory should be considered hidden.
*/
@VisibleForTesting
@@ -1472,6 +1541,11 @@
return false;
}
+ if (shouldBeVisible(dir.getAbsolutePath())) {
+ nomedia.delete();
+ return false;
+ }
+
// Handle top-level default directories. These directories should always be visible,
// regardless of .nomedia presence.
final String[] relativePath = sanitizePath(extractRelativePath(dir.getAbsolutePath()));
@@ -1579,22 +1653,33 @@
}
/**
- * @return {@code true} if {@code dir} is dirty and should be scanned, {@code false} otherwise.
+ * @return {@code true} if {@code dir} has nomedia and it is dirty directory, so it should be
+ * scanned. Returns {@code false} otherwise.
*/
public static boolean isDirectoryDirty(File dir) {
File nomedia = new File(dir, ".nomedia");
- if (nomedia.exists()) {
- try {
- Optional<String> expectedPath = readString(nomedia);
- // Returns true If .nomedia file is empty or content doesn't match |dir|
- // Returns false otherwise
- return !expectedPath.isPresent()
- || !expectedPath.get().equals(dir.getPath());
- } catch (IOException e) {
- Log.w(TAG, "Failed to read directory dirty" + dir);
- }
+
+ // We return false for directories that don't have .nomedia
+ if (!nomedia.exists()) {
+ return false;
}
- return true;
+
+ // We don't write to ".nomedia" dirs, only to ".nomedia" files. If this ".nomedia" is not
+ // a file, then don't try to read it.
+ if (!nomedia.isFile()) {
+ return true;
+ }
+
+ try {
+ Optional<String> expectedPath = readString(nomedia);
+ // Returns true If .nomedia file is empty or content doesn't match |dir|
+ // Returns false otherwise
+ return !expectedPath.isPresent()
+ || !expectedPath.get().equals(dir.getPath());
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to read directory dirty" + dir);
+ return true;
+ }
}
/**
@@ -1603,7 +1688,7 @@
*/
public static void setDirectoryDirty(File dir, boolean isDirty) {
File nomedia = new File(dir, ".nomedia");
- if (nomedia.exists()) {
+ if (nomedia.exists() && nomedia.isFile()) {
try {
writeString(nomedia, isDirty ? Optional.of("") : Optional.of(dir.getPath()));
} catch (IOException e) {
@@ -1659,15 +1744,18 @@
return null;
}
- @Nullable
- private static String getCanonicalPath(@Nullable String path) {
- if (path == null) return null;
+ public static File buildPrimaryVolumeFile(int userId, String... segments) {
+ return buildPath(new File("/storage/emulated/" + userId), segments);
+ }
- try {
- return new File(path).getCanonicalPath();
- } catch (IOException e) {
- Log.d(TAG, "Unable to get canonical path from invalid data path: " + path, e);
- return null;
- }
+ private static final String LOWER_FS_PREFIX = "/storage/";
+ private static final String FUSE_FS_PREFIX = "/mnt/user/" + UserHandle.myUserId() + "/";
+
+ public static File toFuseFile(File file) {
+ return new File(file.getPath().replaceFirst(LOWER_FS_PREFIX, FUSE_FS_PREFIX));
+ }
+
+ public static File fromFuseFile(File file) {
+ return new File(file.getPath().replaceFirst(FUSE_FS_PREFIX, LOWER_FS_PREFIX));
}
}
diff --git a/src/com/android/providers/media/util/ForegroundThread.java b/src/com/android/providers/media/util/ForegroundThread.java
index 436c1b8..0d65a91 100644
--- a/src/com/android/providers/media/util/ForegroundThread.java
+++ b/src/com/android/providers/media/util/ForegroundThread.java
@@ -19,6 +19,8 @@
import android.os.Handler;
import android.os.HandlerThread;
+import com.android.modules.utils.HandlerExecutor;
+
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
diff --git a/src/com/android/providers/media/util/HandlerExecutor.java b/src/com/android/providers/media/util/HandlerExecutor.java
deleted file mode 100644
index 4d1b4ac..0000000
--- a/src/com/android/providers/media/util/HandlerExecutor.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2017 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.util;
-
-import android.os.Handler;
-
-import androidx.annotation.NonNull;
-
-import java.util.Objects;
-import java.util.concurrent.Executor;
-import java.util.concurrent.RejectedExecutionException;
-
-/**
- * An adapter {@link Executor} that posts all executed tasks onto the given
- * {@link Handler}.
- *
- * @hide
- */
-public class HandlerExecutor implements Executor {
- private final Handler mHandler;
-
- public HandlerExecutor(@NonNull Handler handler) {
- mHandler = Objects.requireNonNull(handler);
- }
-
- @Override
- public void execute(Runnable command) {
- if (!mHandler.post(command)) {
- throw new RejectedExecutionException(mHandler + " is shutting down");
- }
- }
-}
diff --git a/src/com/android/providers/media/util/LongArray.java b/src/com/android/providers/media/util/LongArray.java
index 630b41f..7ea750e 100644
--- a/src/com/android/providers/media/util/LongArray.java
+++ b/src/com/android/providers/media/util/LongArray.java
@@ -31,7 +31,7 @@
private LongArray(long[] array, int size) {
mValues = array;
- mSize = checkArgumentInRange(size, 0, array.length, "size");
+ mSize = Preconditions.checkArgumentInRange(size, 0, array.length, "size");
}
/**
@@ -73,7 +73,7 @@
* created from the current content of this LongArray padded with 0s.
*/
public void resize(int newSize) {
- checkArgumentNonnegative(newSize);
+ Preconditions.checkArgumentNonnegative(newSize);
if (newSize <= mValues.length) {
Arrays.fill(mValues, newSize, mValues.length, 0);
} else {
@@ -222,29 +222,6 @@
return true;
}
- public static int checkArgumentNonnegative(final int value) {
- if (value < 0) {
- throw new IllegalArgumentException();
- }
-
- return value;
- }
-
- public static int checkArgumentInRange(int value, int lower, int upper,
- String valueName) {
- if (value < lower) {
- throw new IllegalArgumentException(
- String.format(
- "%s is out of range of [%d, %d] (too low)", valueName, lower, upper));
- } else if (value > upper) {
- throw new IllegalArgumentException(
- String.format(
- "%s is out of range of [%d, %d] (too high)", valueName, lower, upper));
- }
-
- return value;
- }
-
public static void checkBounds(int len, int index) {
if (index < 0 || len <= index) {
throw new ArrayIndexOutOfBoundsException("length=" + len + "; index=" + index);
diff --git a/src/com/android/providers/media/util/Memory.java b/src/com/android/providers/media/util/Memory.java
index 355cabe..f2306e3 100644
--- a/src/com/android/providers/media/util/Memory.java
+++ b/src/com/android/providers/media/util/Memory.java
@@ -46,4 +46,54 @@
dst[offset ] = (byte) ((value >> 24) & 0xff);
}
}
+
+ public static long peekLong(byte[] src, int offset, ByteOrder order) {
+ if (order == ByteOrder.BIG_ENDIAN) {
+ int h = ((src[offset++] & 0xff) << 24) |
+ ((src[offset++] & 0xff) << 16) |
+ ((src[offset++] & 0xff) << 8) |
+ ((src[offset++] & 0xff) << 0);
+ int l = ((src[offset++] & 0xff) << 24) |
+ ((src[offset++] & 0xff) << 16) |
+ ((src[offset++] & 0xff) << 8) |
+ ((src[offset ] & 0xff) << 0);
+ return (((long) h) << 32L) | ((long) l) & 0xffffffffL;
+ } else {
+ int l = ((src[offset++] & 0xff) << 0) |
+ ((src[offset++] & 0xff) << 8) |
+ ((src[offset++] & 0xff) << 16) |
+ ((src[offset++] & 0xff) << 24);
+ int h = ((src[offset++] & 0xff) << 0) |
+ ((src[offset++] & 0xff) << 8) |
+ ((src[offset++] & 0xff) << 16) |
+ ((src[offset ] & 0xff) << 24);
+ return (((long) h) << 32L) | ((long) l) & 0xffffffffL;
+ }
+ }
+
+ public static void pokeLong(byte[] dst, int offset, long value, ByteOrder order) {
+ if (order == ByteOrder.BIG_ENDIAN) {
+ int i = (int) (value >> 32);
+ dst[offset++] = (byte) ((i >> 24) & 0xff);
+ dst[offset++] = (byte) ((i >> 16) & 0xff);
+ dst[offset++] = (byte) ((i >> 8) & 0xff);
+ dst[offset++] = (byte) ((i >> 0) & 0xff);
+ i = (int) value;
+ dst[offset++] = (byte) ((i >> 24) & 0xff);
+ dst[offset++] = (byte) ((i >> 16) & 0xff);
+ dst[offset++] = (byte) ((i >> 8) & 0xff);
+ dst[offset ] = (byte) ((i >> 0) & 0xff);
+ } else {
+ int i = (int) value;
+ dst[offset++] = (byte) ((i >> 0) & 0xff);
+ dst[offset++] = (byte) ((i >> 8) & 0xff);
+ dst[offset++] = (byte) ((i >> 16) & 0xff);
+ dst[offset++] = (byte) ((i >> 24) & 0xff);
+ i = (int) (value >> 32);
+ dst[offset++] = (byte) ((i >> 0) & 0xff);
+ dst[offset++] = (byte) ((i >> 8) & 0xff);
+ dst[offset++] = (byte) ((i >> 16) & 0xff);
+ dst[offset ] = (byte) ((i >> 24) & 0xff);
+ }
+ }
}
diff --git a/src/com/android/providers/media/util/MimeUtils.java b/src/com/android/providers/media/util/MimeUtils.java
index 09417bd..65f3e83 100644
--- a/src/com/android/providers/media/util/MimeUtils.java
+++ b/src/com/android/providers/media/util/MimeUtils.java
@@ -29,23 +29,6 @@
import java.util.Objects;
public class MimeUtils {
- /**
- * Variant of {@link Objects#equal(Object, Object)} but which tests with
- * case-insensitivity.
- */
- public static boolean equalIgnoreCase(@Nullable String a, @Nullable String b) {
- return (a != null) && a.equalsIgnoreCase(b);
- }
-
- /**
- * Variant of {@link String#startsWith(String)} but which tests with
- * case-insensitivity.
- */
- public static boolean startsWithIgnoreCase(@Nullable String target, @Nullable String other) {
- if (target == null || other == null) return false;
- if (other.length() > target.length()) return false;
- return target.regionMatches(true, 0, other, 0, other.length());
- }
/**
* Resolve the MIME type of the given file, returning
@@ -114,17 +97,22 @@
public static boolean isAudioMimeType(@Nullable String mimeType) {
if (mimeType == null) return false;
- return startsWithIgnoreCase(mimeType, "audio/");
+ return StringUtils.startsWithIgnoreCase(mimeType, "audio/");
}
public static boolean isVideoMimeType(@Nullable String mimeType) {
if (mimeType == null) return false;
- return startsWithIgnoreCase(mimeType, "video/");
+ return StringUtils.startsWithIgnoreCase(mimeType, "video/");
}
public static boolean isImageMimeType(@Nullable String mimeType) {
if (mimeType == null) return false;
- return startsWithIgnoreCase(mimeType, "image/");
+ return StringUtils.startsWithIgnoreCase(mimeType, "image/");
+ }
+
+ public static boolean isImageOrVideoMediaType(int mediaType) {
+ return FileColumns.MEDIA_TYPE_IMAGE == mediaType
+ || FileColumns.MEDIA_TYPE_VIDEO == mediaType;
}
public static boolean isPlaylistMimeType(@Nullable String mimeType) {
@@ -165,7 +153,7 @@
public static boolean isDocumentMimeType(@Nullable String mimeType) {
if (mimeType == null) return false;
- if (startsWithIgnoreCase(mimeType, "text/")) return true;
+ if (StringUtils.startsWithIgnoreCase(mimeType, "text/")) return true;
switch (mimeType.toLowerCase(Locale.ROOT)) {
case "application/epub+zip":
diff --git a/src/com/android/providers/media/util/PermissionUtils.java b/src/com/android/providers/media/util/PermissionUtils.java
index 5b3639c..b1d9836 100644
--- a/src/com/android/providers/media/util/PermissionUtils.java
+++ b/src/com/android/providers/media/util/PermissionUtils.java
@@ -23,15 +23,18 @@
import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE;
import static android.Manifest.permission.MANAGE_MEDIA;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.Manifest.permission.READ_MEDIA_AUDIO;
+import static android.Manifest.permission.READ_MEDIA_IMAGES;
+import static android.Manifest.permission.READ_MEDIA_VIDEO;
import static android.Manifest.permission.UPDATE_DEVICE_STATS;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES;
import static android.app.AppOpsManager.OPSTR_LEGACY_STORAGE;
import static android.app.AppOpsManager.OPSTR_NO_ISOLATED_STORAGE;
import static android.app.AppOpsManager.OPSTR_READ_MEDIA_AUDIO;
import static android.app.AppOpsManager.OPSTR_READ_MEDIA_IMAGES;
import static android.app.AppOpsManager.OPSTR_READ_MEDIA_VIDEO;
+import static android.app.AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES;
import static android.app.AppOpsManager.OPSTR_WRITE_MEDIA_AUDIO;
import static android.app.AppOpsManager.OPSTR_WRITE_MEDIA_IMAGES;
import static android.app.AppOpsManager.OPSTR_WRITE_MEDIA_VIDEO;
@@ -41,11 +44,14 @@
import android.app.DownloadManager;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.os.UserHandle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.modules.utils.build.SdkLevel;
+
public class PermissionUtils {
// Callers must hold both the old and new permissions, so that we can
@@ -61,7 +67,7 @@
public static void clearOpDescription() { sOpDescription.set(null); }
public static boolean checkPermissionSelf(@NonNull Context context, int pid, int uid) {
- return android.os.Process.myUid() == uid;
+ return UserHandle.getAppId(android.os.Process.myUid()) == UserHandle.getAppId(uid);
}
public static boolean checkPermissionShell(@NonNull Context context, int pid, int uid) {
@@ -146,9 +152,18 @@
return checkNoIsolatedStorageGranted(context, uid, packageName, attributionTag);
}
- public static boolean checkPermissionReadAudio(@NonNull Context context, int pid, int uid,
- @NonNull String packageName, @Nullable String attributionTag) {
- if (!checkPermissionForPreflight(context, READ_EXTERNAL_STORAGE, pid, uid, packageName)) {
+ public static boolean checkPermissionReadAudio(
+ @NonNull Context context,
+ int pid,
+ int uid,
+ @NonNull String packageName,
+ @Nullable String attributionTag,
+ boolean targetSdkIsAtLeastT) {
+
+ String permission = targetSdkIsAtLeastT && SdkLevel.isAtLeastT()
+ ? READ_MEDIA_AUDIO : READ_EXTERNAL_STORAGE;
+
+ if (!checkPermissionForPreflight(context, permission, pid, uid, packageName)) {
return false;
}
return checkAppOpAllowingLegacy(context, OPSTR_READ_MEDIA_AUDIO, pid,
@@ -167,11 +182,20 @@
generateAppOpMessage(packageName, sOpDescription.get()));
}
- public static boolean checkPermissionReadVideo(@NonNull Context context, int pid, int uid,
- @NonNull String packageName, @Nullable String attributionTag) {
- if (!checkPermissionForPreflight(context, READ_EXTERNAL_STORAGE, pid, uid, packageName)) {
+ public static boolean checkPermissionReadVideo(
+ @NonNull Context context,
+ int pid,
+ int uid,
+ @NonNull String packageName,
+ @Nullable String attributionTag,
+ boolean targetSdkIsAtLeastT) {
+ String permission = targetSdkIsAtLeastT && SdkLevel.isAtLeastT()
+ ? READ_MEDIA_VIDEO : READ_EXTERNAL_STORAGE;
+
+ if (!checkPermissionForPreflight(context, permission, pid, uid, packageName)) {
return false;
}
+
return checkAppOpAllowingLegacy(context, OPSTR_READ_MEDIA_VIDEO, pid,
uid, packageName, attributionTag,
generateAppOpMessage(packageName, sOpDescription.get()));
@@ -188,11 +212,20 @@
generateAppOpMessage(packageName, sOpDescription.get()));
}
- public static boolean checkPermissionReadImages(@NonNull Context context, int pid, int uid,
- @NonNull String packageName, @Nullable String attributionTag) {
- if (!checkPermissionForPreflight(context, READ_EXTERNAL_STORAGE, pid, uid, packageName)) {
+ public static boolean checkPermissionReadImages(
+ @NonNull Context context,
+ int pid,
+ int uid,
+ @NonNull String packageName,
+ @Nullable String attributionTag,
+ boolean targetSdkIsAtLeastT) {
+ String permission = targetSdkIsAtLeastT && SdkLevel.isAtLeastT()
+ ? READ_MEDIA_IMAGES : READ_EXTERNAL_STORAGE;
+
+ if (!checkPermissionForPreflight(context, permission, pid, uid, packageName)) {
return false;
}
+
return checkAppOpAllowingLegacy(context, OPSTR_READ_MEDIA_IMAGES, pid,
uid, packageName, attributionTag,
generateAppOpMessage(packageName, sOpDescription.get()));
@@ -423,6 +456,7 @@
return checkRuntimePermission(context, permission, pid, uid, packageName,
attributionTag, message, forDataDelivery);
}
+
return context.checkPermission(permission, pid, uid) == PERMISSION_GRANTED;
}
@@ -440,6 +474,9 @@
case ACCESS_MEDIA_LOCATION:
case READ_EXTERNAL_STORAGE:
case WRITE_EXTERNAL_STORAGE:
+ case READ_MEDIA_AUDIO:
+ case READ_MEDIA_VIDEO:
+ case READ_MEDIA_IMAGES:
return true;
}
return false;
diff --git a/src/com/android/providers/media/util/Preconditions.java b/src/com/android/providers/media/util/Preconditions.java
new file mode 100644
index 0000000..fb87130
--- /dev/null
+++ b/src/com/android/providers/media/util/Preconditions.java
@@ -0,0 +1,62 @@
+/*
+ * 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.util;
+
+public final class Preconditions {
+
+ /**
+ * Ensures that that the argument numeric value is non-negative (greater than or equal to 0).
+ *
+ * @param value a numeric int value
+ * @return the validated numeric value
+ * @throws IllegalArgumentException if {@code value} was negative
+ */
+ public static int checkArgumentNonnegative(final int value) {
+ if (value < 0) {
+ throw new IllegalArgumentException();
+ }
+
+ return value;
+ }
+
+ /**
+ * Ensures that the argument int value is within the inclusive range.
+ *
+ * @param value a int value
+ * @param lower the lower endpoint of the inclusive range
+ * @param upper the upper endpoint of the inclusive range
+ * @param valueName the name of the argument to use if the check fails
+ *
+ * @return the validated int value
+ *
+ * @throws IllegalArgumentException if {@code value} was not within the range
+ */
+ public static int checkArgumentInRange(int value, int lower, int upper,
+ String valueName) {
+ if (value < lower) {
+ throw new IllegalArgumentException(
+ String.format(
+ "%s is out of range of [%d, %d] (too low)", valueName, lower, upper));
+ } else if (value > upper) {
+ throw new IllegalArgumentException(
+ String.format(
+ "%s is out of range of [%d, %d] (too high)", valueName, lower, upper));
+ }
+
+ return value;
+ }
+}
diff --git a/src/com/android/providers/media/util/SpecialFormatDetector.java b/src/com/android/providers/media/util/SpecialFormatDetector.java
new file mode 100644
index 0000000..d8d65d9
--- /dev/null
+++ b/src/com/android/providers/media/util/SpecialFormatDetector.java
@@ -0,0 +1,296 @@
+/*
+ * 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.util;
+
+import android.graphics.BitmapFactory;
+import android.graphics.ImageDecoder;
+import android.graphics.drawable.AnimatedImageDrawable;
+import android.graphics.drawable.Drawable;
+import android.media.ExifInterface;
+import android.os.Trace;
+import android.provider.MediaStore.Files.FileColumns;
+import android.util.Log;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.File;
+import java.io.StringReader;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Class to detect and return special format for a media file.
+ */
+public class SpecialFormatDetector {
+ private static final String TAG = "SpecialFormatDetector";
+ // These are the known MotionPhoto attribute names
+ private static final String[] MOTION_PHOTO_ATTRIBUTE_NAMES = {
+ "Camera:MotionPhoto", // Motion Photo V1
+ "GCamera:MotionPhoto", // Motion Photo V1 (legacy element naming)
+ "Camera:MicroVideo", // Micro Video V1b
+ "GCamera:MicroVideo", // Micro Video V1b (legacy element naming)
+ };
+
+ private static final String[] DESCRIPTION_MICRO_VIDEO_OFFSET_ATTRIBUTE_NAMES = {
+ "Camera:MicroVideoOffset", // Micro Video V1b
+ "GCamera:MicroVideoOffset", // Micro Video V1b (legacy element naming)
+ };
+
+ private static final String XMP_META_TAG = "x:xmpmeta";
+ private static final String XMP_RDF_DESCRIPTION_TAG = "rdf:Description";
+
+ private static final String XMP_CONTAINER_PREFIX = "Container";
+ private static final String XMP_GCONTAINER_PREFIX = "GContainer";
+
+ private static final String XMP_ITEM_PREFIX = "Item";
+ private static final String XMP_GCONTAINER_ITEM_PREFIX =
+ XMP_GCONTAINER_PREFIX + XMP_ITEM_PREFIX;
+
+ private static final String XMP_DIRECTORY_TAG = ":Directory";
+ private static final String XMP_CONTAINER_DIRECTORY_PREFIX =
+ XMP_CONTAINER_PREFIX + XMP_DIRECTORY_TAG;
+ private static final String XMP_GCONTAINER_DIRECTORY_PREFIX =
+ XMP_GCONTAINER_PREFIX + XMP_DIRECTORY_TAG;
+
+ private static final String SEMANTIC_PRIMARY = "Primary";
+ private static final String SEMANTIC_MOTION_PHOTO = "MotionPhoto";
+
+ /**
+ * {@return} special format for a file
+ */
+ public static int detect(File file) throws Exception {
+ try (FileInputStream is = new FileInputStream(file)) {
+ final ExifInterface exif = new ExifInterface(is);
+ return detect(exif, file);
+ }
+ }
+
+ /**
+ * {@return} special format for a file
+ */
+ public static int detect(ExifInterface exif, File file) throws Exception {
+ if (isMotionPhoto(exif)) {
+ return FileColumns._SPECIAL_FORMAT_MOTION_PHOTO;
+ }
+
+ return detectGifOrAnimatedWebp(file);
+ }
+
+ /**
+ * Checks file metadata to detect if the given file is a GIF or Animated Webp.
+ *
+ * Note: This does not respect file extension.
+ *
+ * @return {@link FileColumns#_SPECIAL_FORMAT_GIF} if the file is a GIF file or
+ * {@link FileColumns#_SPECIAL_FORMAT_ANIMATED_WEBP} if the file is an Animated Webp
+ * file. Otherwise returns {@link FileColumns#_SPECIAL_FORMAT_NONE}
+ */
+ private static int detectGifOrAnimatedWebp(File file) throws IOException {
+ final BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
+ // Set options such that the image is not decoded to a bitmap, as we only want mimetype
+ // options
+ bitmapOptions.inSampleSize = 1;
+ bitmapOptions.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(file.getAbsolutePath(), bitmapOptions);
+
+ if (bitmapOptions.outMimeType.equalsIgnoreCase("image/gif")) {
+ return FileColumns._SPECIAL_FORMAT_GIF;
+ }
+ if (bitmapOptions.outMimeType.equalsIgnoreCase("image/webp") &&
+ isAnimatedWebp(file)) {
+ return FileColumns._SPECIAL_FORMAT_ANIMATED_WEBP;
+ }
+ return FileColumns._SPECIAL_FORMAT_NONE;
+ }
+
+ private static boolean isAnimatedWebp(File file) throws IOException {
+ final ImageDecoder.Source source = ImageDecoder.createSource(file);
+ final Drawable drawable = ImageDecoder.decodeDrawable(source);
+ return (drawable instanceof AnimatedImageDrawable);
+ }
+
+ private static boolean isMotionPhoto(ExifInterface exif) throws Exception {
+ if (!exif.hasAttribute(ExifInterface.TAG_XMP)) {
+ return false;
+ }
+ final String xmp = new String(exif.getAttributeBytes(ExifInterface.TAG_XMP),
+ StandardCharsets.UTF_8);
+
+ // The below logic is copied from ExoPlayer#XmpMotionPhotoDescriptionParser class
+ XmlPullParserFactory xmlPullParserFactory = XmlPullParserFactory.newInstance();
+ XmlPullParser xpp = xmlPullParserFactory.newPullParser();
+ xpp.setInput(new StringReader(xmp));
+ xpp.next();
+ if (!isStartTag(xpp, XMP_META_TAG)) {
+ Log.d(TAG, "Couldn't find xmp metadata");
+ return false;
+ }
+
+ Trace.beginSection("motionPhotoDetectionUsingXpp");
+ try {
+ return isMotionPhoto(xpp);
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ private static boolean isMotionPhoto(XmlPullParser xpp) throws Exception {
+ boolean isMotionPhotoAttributesFound = false;
+
+ do {
+ xpp.next();
+ if (!isStartTag(xpp)) {
+ continue;
+ }
+
+ switch (xpp.getName()) {
+ case XMP_RDF_DESCRIPTION_TAG:
+ if (!isMotionPhotoFlagSet(xpp)) {
+ // The motion photo flag is not set, so the file should not be treated as a
+ // motion photo.
+ return false;
+ }
+ isMotionPhotoAttributesFound = isMicroVideoPresent(xpp);
+ break;
+ case XMP_CONTAINER_DIRECTORY_PREFIX:
+ isMotionPhotoAttributesFound = isMotionPhotoDirectory(xpp, XMP_CONTAINER_PREFIX,
+ XMP_ITEM_PREFIX);
+ break;
+ case XMP_GCONTAINER_DIRECTORY_PREFIX:
+ isMotionPhotoAttributesFound = isMotionPhotoDirectory(xpp,
+ XMP_GCONTAINER_PREFIX, XMP_GCONTAINER_ITEM_PREFIX);
+ break;
+ default: // do nothing
+ }
+
+ // Return early if motion photo attributes were found in the xpp,
+ // otherwise continue looking
+ if (isMotionPhotoAttributesFound) {
+ return true;
+ }
+
+ } while (!isEndTag(xpp, XMP_META_TAG) && xpp.getEventType() != XmlPullParser.END_DOCUMENT);
+
+ return false;
+ }
+
+ private static boolean isMotionPhotoDirectory(XmlPullParser xpp,
+ String containerNamespacePrefix, String itemNamespacePrefix)
+ throws XmlPullParserException, IOException {
+ final String itemTagName = containerNamespacePrefix + ":Item";
+ final String directoryTagName = containerNamespacePrefix + ":Directory";
+ final String mimeAttributeName = itemNamespacePrefix + ":Mime";
+ final String semanticAttributeName = itemNamespacePrefix + ":Semantic";
+ final String lengthAttributeName = itemNamespacePrefix + ":Length";
+ boolean isPrimaryImagePresent = false;
+ boolean isMotionPhotoPresent = false;
+
+ do {
+ xpp.next();
+ if (!isStartTag(xpp, itemTagName)) {
+ continue;
+ }
+
+ String semantic = getAttributeValue(xpp, semanticAttributeName);
+ if (getAttributeValue(xpp, mimeAttributeName) == null || semantic == null) {
+ // Required values are missing.
+ return false;
+ }
+
+ switch (semantic) {
+ case SEMANTIC_PRIMARY:
+ isPrimaryImagePresent = true;
+ break;
+ case SEMANTIC_MOTION_PHOTO:
+ String length = getAttributeValue(xpp, lengthAttributeName);
+ isMotionPhotoPresent = (length != null && Integer.parseInt(length) > 0);
+ break;
+ default: // do nothing
+ }
+
+ if (isMotionPhotoPresent && isPrimaryImagePresent) {
+ return true;
+ }
+ } while (!isEndTag(xpp, directoryTagName) &&
+ xpp.getEventType() != XmlPullParser.END_DOCUMENT);
+ // We need a primary item (photo) and at least one secondary item (video).
+ return false;
+ }
+
+ private static boolean isMicroVideoPresent(XmlPullParser xpp) {
+ for (String attributeName : DESCRIPTION_MICRO_VIDEO_OFFSET_ATTRIBUTE_NAMES) {
+ String attributeValue = getAttributeValue(xpp, attributeName);
+ if (attributeValue != null) {
+ long microVideoOffset = Long.parseLong(attributeValue);
+ return microVideoOffset > 0;
+ }
+ }
+ return false;
+ }
+
+ private static boolean isMotionPhotoFlagSet(XmlPullParser xpp) {
+ for (String attributeName : MOTION_PHOTO_ATTRIBUTE_NAMES) {
+ String attributeValue = getAttributeValue(xpp, attributeName);
+ if (attributeValue != null) {
+ int motionPhotoFlag = Integer.parseInt(attributeValue);
+ return motionPhotoFlag == 1;
+ }
+ }
+ return false;
+ }
+
+ private static String getAttributeValue(XmlPullParser xpp, String attributeName) {
+ for (int i = 0; i < xpp.getAttributeCount(); i++) {
+ if (xpp.getAttributeName(i).equals(attributeName)) {
+ return xpp.getAttributeValue(i);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns whether the current event is an end tag with the specified name.
+ *
+ * @param xpp The {@link XmlPullParser} to query.
+ * @param name The specified name.
+ * @return Whether the current event is an end tag.
+ * @throws XmlPullParserException If an error occurs querying the parser.
+ */
+ private static boolean isEndTag(XmlPullParser xpp, String name) throws XmlPullParserException {
+ return xpp.getEventType() == XmlPullParser.END_TAG && xpp.getName().equals(name);
+ }
+
+ /**
+ * Returns whether the current event is a start tag with the specified name.
+ *
+ * @param xpp The {@link XmlPullParser} to query.
+ * @param name The specified name.
+ * @return Whether the current event is a start tag with the specified name.
+ * @throws XmlPullParserException If an error occurs querying the parser.
+ */
+ private static boolean isStartTag(XmlPullParser xpp, String name)
+ throws XmlPullParserException {
+ return isStartTag(xpp) && xpp.getName().equals(name);
+ }
+
+ private static boolean isStartTag(XmlPullParser xpp) throws XmlPullParserException {
+ return xpp.getEventType() == XmlPullParser.START_TAG;
+ }
+}
diff --git a/src/com/android/providers/media/util/StringUtils.java b/src/com/android/providers/media/util/StringUtils.java
new file mode 100644
index 0000000..49bf935
--- /dev/null
+++ b/src/com/android/providers/media/util/StringUtils.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 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.util;
+
+import static com.android.providers.media.util.Logging.TAG;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.icu.text.MessageFormat;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+
+public class StringUtils {
+
+ /**
+ * Returns the formatted ICU format string corresponding to the provided resource ID and count
+ * number of entities in the plural string.
+ */
+ public static String getICUFormatString(Resources resources, int count, int resourceID) {
+ MessageFormat msgFormat = new MessageFormat(
+ resources.getString(resourceID),
+ Locale.getDefault());
+ Map<String, Object> arguments = new HashMap<>();
+ arguments.put("count", count);
+ return msgFormat.format(arguments);
+ }
+
+ public static String getStringConfig(Context context, int resId) {
+ final Resources res = context.getResources();
+ try {
+ return res.getString(resId);
+ } catch (NotFoundException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Variant of {@link String#startsWith(String)} but which tests with
+ * case-insensitivity.
+ */
+ public static boolean startsWithIgnoreCase(@Nullable String target, @Nullable String other) {
+ if (target == null || other == null) return false;
+ if (other.length() > target.length()) return false;
+ return target.regionMatches(true, 0, other, 0, other.length());
+ }
+
+ /**
+ * Variant of {@link Objects#equal(Object, Object)} but which tests with
+ * case-insensitivity.
+ */
+ public static boolean equalIgnoreCase(@Nullable String a, @Nullable String b) {
+ return (a != null) && a.equalsIgnoreCase(b);
+ }
+
+ /**
+ * Returns a string array config as a {@code List<String>}.
+ */
+ public static List<String> getStringArrayConfig(Context context, int resId) {
+ final Resources res = context.getResources();
+ try {
+ final String[] configValue = res.getStringArray(resId);
+ return Arrays.asList(configValue);
+ } catch (NotFoundException e) {
+ return new ArrayList<String>();
+ }
+ }
+
+ /**
+ * Returns the list of uncached relative paths after removing invalid ones.
+ */
+ public static List<String> verifySupportedUncachedRelativePaths(List<String> unverifiedPaths) {
+ final List<String> verifiedPaths = new ArrayList<>();
+ for (final String path : unverifiedPaths) {
+ if (path == null) {
+ continue;
+ }
+ if (path.startsWith("/")) {
+ Log.w(TAG, "Relative path config must not start with '/'. Ignoring: " + path);
+ continue;
+ }
+ if (!path.endsWith("/")) {
+ Log.w(TAG, "Relative path config must end with '/'. Ignoring: " + path);
+ continue;
+ }
+
+ verifiedPaths.add(path);
+ }
+
+ return verifiedPaths;
+ }
+}
diff --git a/src/com/android/providers/media/util/SyntheticPathUtils.java b/src/com/android/providers/media/util/SyntheticPathUtils.java
new file mode 100644
index 0000000..aa0db93
--- /dev/null
+++ b/src/com/android/providers/media/util/SyntheticPathUtils.java
@@ -0,0 +1,125 @@
+/*
+ * 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.util;
+
+import static com.android.providers.media.util.FileUtils.buildPath;
+import static com.android.providers.media.util.FileUtils.buildPrimaryVolumeFile;
+import static com.android.providers.media.util.FileUtils.extractFileName;
+
+import androidx.annotation.VisibleForTesting;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+public final class SyntheticPathUtils {
+ private static final String TAG = "SyntheticPathUtils";
+
+ private static final String TRANSFORMS_DIR = ".transforms";
+ private static final String SYNTHETIC_DIR = "synthetic";
+ private static final String REDACTED_DIR = "redacted";
+ private static final String PICKER_DIR = "picker";
+
+ public static final String REDACTED_URI_ID_PREFIX = "RUID";
+ public static final int REDACTED_URI_ID_SIZE = 36;
+
+ private SyntheticPathUtils() {}
+
+ public static String getRedactedRelativePath() {
+ return buildPath(/* base */ null, TRANSFORMS_DIR, SYNTHETIC_DIR, REDACTED_DIR).getPath();
+ }
+
+ public static String getPickerRelativePath() {
+ return buildPath(/* base */ null, TRANSFORMS_DIR, SYNTHETIC_DIR, PICKER_DIR).getPath();
+ }
+
+ public static boolean isRedactedPath(String path, int userId) {
+ if (path == null) return false;
+
+ final String redactedDir = buildPrimaryVolumeFile(userId, getRedactedRelativePath())
+ .getAbsolutePath();
+ final String fileName = extractFileName(path);
+
+ return fileName != null
+ && startsWith(path, redactedDir)
+ && startsWith(fileName, REDACTED_URI_ID_PREFIX)
+ && fileName.length() == REDACTED_URI_ID_SIZE;
+ }
+
+ public static boolean isPickerPath(String path, int userId) {
+ final String pickerDir = buildPrimaryVolumeFile(userId, getPickerRelativePath())
+ .getAbsolutePath();
+
+ return path != null && startsWith(path, pickerDir);
+ }
+
+ public static boolean isSyntheticPath(String path, int userId) {
+ final String syntheticDir = buildPrimaryVolumeFile(userId, getSyntheticRelativePath())
+ .getAbsolutePath();
+
+ return path != null && startsWith(path, syntheticDir);
+ }
+
+ public static List<String> extractSyntheticRelativePathSegements(String path, int userId) {
+ final List<String> segments = new ArrayList<>();
+ final String syntheticDir = buildPrimaryVolumeFile(userId,
+ getSyntheticRelativePath()).getAbsolutePath();
+
+ if (path.toLowerCase(Locale.ROOT).indexOf(syntheticDir.toLowerCase(Locale.ROOT)) < 0) {
+ return segments;
+ }
+
+ final String[] segmentArray = path.substring(syntheticDir.length()).split("/");
+ for (String segment : segmentArray) {
+ if (TextUtils.isEmpty(segment)) {
+ continue;
+ }
+ segments.add(segment);
+ }
+
+ return segments;
+ }
+
+ public static boolean createSparseFile(File file, long size) {
+ if (size < 0) {
+ return false;
+ }
+
+ try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) {
+ raf.setLength(size);
+ return true;
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to create sparse file: " + file, e);
+ file.delete();
+ return false;
+ }
+ }
+
+ @VisibleForTesting
+ static String getSyntheticRelativePath() {
+ return buildPath(/* base */ null, TRANSFORMS_DIR, SYNTHETIC_DIR).getPath();
+ }
+
+ private static boolean startsWith(String value, String prefix) {
+ return value.toLowerCase(Locale.ROOT).startsWith(prefix.toLowerCase(Locale.ROOT));
+ }
+}
diff --git a/src/com/android/providers/media/util/UserCache.java b/src/com/android/providers/media/util/UserCache.java
index a5b05c4..fa46779 100644
--- a/src/com/android/providers/media/util/UserCache.java
+++ b/src/com/android/providers/media/util/UserCache.java
@@ -18,7 +18,9 @@
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static com.android.providers.media.util.Logging.TAG;
+import android.annotation.SuppressLint;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -26,7 +28,9 @@
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.Log;
import android.util.LongSparseArray;
+import android.util.SparseArray;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
@@ -49,14 +53,21 @@
* aren't guaranteed to be received before the volume events for a user.
*/
public class UserCache {
+ // This is being used for non work profile users. It is introduced to remove the necessity of
+ // second cache i.e. mUserIsWorkProfile
+ private static final String NO_WORK_PROFILE_OWNER_APP = "No Work Profile Owner App";
+
final Object mLock = new Object();
final Context mContext;
final UserManager mUserManager;
@GuardedBy("mLock")
final LongSparseArray<Context> mUserContexts = new LongSparseArray<>();
+
+ // This contains a mapping from userId to packageName of the Profile Owner App
+ // or NO_WORK_PROFILE_OWNER_APP
@GuardedBy("mLock")
- final LongSparseArray<Boolean> mUserIsWorkProfile = new LongSparseArray<>();
+ final SparseArray<String> mWorkProfileOwnerApps = new SparseArray<>();
@GuardedBy("mLock")
final ArrayList<UserHandle> mUsers = new ArrayList<>();
@@ -68,6 +79,7 @@
update();
}
+ @SuppressLint("NewApi")
private void update() {
List<UserHandle> profiles = mUserManager.getEnabledProfiles();
synchronized (mLock) {
@@ -78,12 +90,17 @@
// Before S, we only handle the owner user
return;
}
+
+ // App cloning is not supported for profile users like AFW.
+ if (mUserManager.isProfile()) {
+ return;
+ }
+
// And find all profiles that share media with us
for (UserHandle profile : profiles) {
if (!profile.equals(mContext.getUser())) {
- // Check if it's a profile that shares media with us
- Context userContext = getContextForUser(profile);
- if (userContext.getSystemService(UserManager.class).isMediaSharedWithParent()) {
+ // Check if it's unlocked, and it's a profile that shares media with us
+ if (isUnlockedAndMediaSharedWithParent(profile)) {
mUsers.add(profile);
}
}
@@ -91,6 +108,14 @@
}
}
+ private boolean isUnlockedAndMediaSharedWithParent(@NonNull UserHandle profile) {
+ Context userContext = getContextForUser(profile);
+ UserManager userManager = userContext.getSystemService(UserManager.class);
+ return (SdkLevel.isAtLeastT() ?
+ userManager.isUserUnlocked() : userManager.isUserUnlocked(profile))
+ && userManager.isMediaSharedWithParent();
+ }
+
public @NonNull List<UserHandle> updateAndGetUsers() {
update();
synchronized (mLock) {
@@ -109,10 +134,11 @@
// Owner user can not have a work profile
return false;
}
+
synchronized (mLock) {
- int index = mUserIsWorkProfile.indexOfKey(userId);
+ int index = mWorkProfileOwnerApps.indexOfKey(userId);
if (index >= 0) {
- return mUserIsWorkProfile.valueAt(index);
+ return !NO_WORK_PROFILE_OWNER_APP.equals(mWorkProfileOwnerApps.valueAt(index));
}
}
@@ -124,12 +150,19 @@
for (ApplicationInfo ai : packageManager.getInstalledApplications(
MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE)) {
if (policyManager.isProfileOwnerApp(ai.packageName)) {
+ synchronized (mLock) {
+ mWorkProfileOwnerApps.put(userId, ai.packageName);
+ }
isWorkProfile = true;
+ break;
}
}
- synchronized (mLock) {
- mUserIsWorkProfile.put(userId, isWorkProfile);
+ if(!isWorkProfile) {
+ synchronized (mLock) {
+ // NO_WORK_PROFILE_OWNER_APP is being used for all the non work profile users
+ mWorkProfileOwnerApps.put(userId, NO_WORK_PROFILE_OWNER_APP);
+ }
}
return isWorkProfile;
@@ -197,4 +230,28 @@
}
}
}
+
+ public void invalidateWorkProfileOwnerApps(@NonNull String packageName) {
+ synchronized (mLock) {
+ if (mWorkProfileOwnerApps.size() == 0) {
+ Log.w(TAG, "WorkProfileOwnerApps cache is empty");
+ return;
+ }
+
+ boolean cacheMissForGivenPackage = true;
+ for (int i = 0; i < mWorkProfileOwnerApps.size(); i++) {
+ final int userId = mWorkProfileOwnerApps.keyAt(i);
+ if (packageName.equals(mWorkProfileOwnerApps.get(userId))) {
+ Log.i(TAG, "Invalidating WorkProfileOwnerApps cache for package " + packageName
+ + ". UserId: " + userId);
+ mWorkProfileOwnerApps.remove(userId);
+ cacheMissForGivenPackage = false;
+ }
+ }
+
+ if(cacheMissForGivenPackage) {
+ Log.w(TAG, "WorkProfileOwnerApps cache miss for package " + packageName);
+ }
+ }
+ }
}
diff --git a/src/com/android/providers/media/util/XAttrUtils.java b/src/com/android/providers/media/util/XAttrUtils.java
new file mode 100644
index 0000000..236f7ed
--- /dev/null
+++ b/src/com/android/providers/media/util/XAttrUtils.java
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ */
+
+package com.android.providers.media.util;
+
+import static com.android.providers.media.util.FileUtils.extractDisplayName;
+import static com.android.providers.media.util.FileUtils.extractRelativePath;
+import static com.android.providers.media.util.Logging.TAG;
+
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Log;
+
+import com.android.providers.media.FileAccessAttributes;
+
+import java.nio.ByteOrder;
+import java.util.Optional;
+
+public class XAttrUtils {
+
+ /**
+ * Path on which {@link XAttrUtils#DATA_MEDIA_XATTR_DIRECTORY_PATH} is set.
+ * /storage/emulated/.. can point to /data/media/.. on ext4/f2fs on modern devices. However, for
+ * legacy devices with sdcardfs, it points to /mnt/runtime/.. which then points to
+ * /data/media/.. sdcardfs does not support xattrs, hence xattrs are set on /data/media/.. path.
+ *
+ * TODO(b/220895679): Add logic to handle external sd cards with primary volume with paths
+ * /mnt/expand/<volume>/media/<user-id>.
+ */
+ static final String DATA_MEDIA_XATTR_DIRECTORY_PATH = String.format(
+ "/data/media/%s", UserHandle.myUserId());
+
+ static final int SIZE_OF_FILE_ATTRIBUTES = 18;
+
+ /**
+ * Flag to turn on reading file metadata through xattr in FUSE file open calls
+ */
+ public static final boolean ENABLE_XATTR_METADATA_FOR_FUSE =
+ SystemProperties.getBoolean("persist.sys.fuse.perf.xattr_metadata_enabled",
+ false);
+
+ /**
+ * XAttribute key against which the file metadata is stored
+ */
+ public static final String FILE_ACCESS_XATTR_KEY = "user.fattr";
+
+ public static Optional<FileAccessAttributes> getFileAttributesFromXAttr(String path,
+ String key) {
+ Trace.beginSection("getFileAttributesFromXAttr");
+ String relativePathWithDisplayName = DATA_MEDIA_XATTR_DIRECTORY_PATH + "/"
+ + extractRelativePath(path) + extractDisplayName(path);
+ try {
+ return Optional.of(deserializeFileAccessAttributes(
+ Os.getxattr(relativePathWithDisplayName, key)));
+ } catch (ErrnoException e) {
+ Log.w(TAG,
+ String.format("Exception encountered while reading xattr:%s from path:%s.", key,
+ path));
+ return Optional.empty();
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ /**
+ * Serializes file access attributes into byte array that will be stored in the xattr.
+ * This method serializes only the id, mediaType, isPending, isTrashed and ownerId fields.
+ * @param fileAccessAttributes File attributes to be stored as byte[] in the file inode
+ * @return byte[]
+ */
+ public static byte[] serializeFileAccessAttributes(
+ FileAccessAttributes fileAccessAttributes) {
+ byte[] bytes = new byte[SIZE_OF_FILE_ATTRIBUTES];
+ int offset = 0;
+ ByteOrder byteOrder = ByteOrder.nativeOrder();
+
+ Memory.pokeLong(bytes, offset, fileAccessAttributes.getId(), byteOrder);
+ offset += Long.BYTES;
+
+ // TODO(b/227753174): Merge mediaType and the booleans in a single byte
+ Memory.pokeInt(bytes, offset, fileAccessAttributes.getMediaType(), byteOrder);
+ offset += Integer.BYTES;
+
+ bytes[offset++] = (byte) (fileAccessAttributes.isPending() ? 1 : 0);
+ bytes[offset++] = (byte) (fileAccessAttributes.isTrashed() ? 1 : 0);
+
+ Memory.pokeInt(bytes, offset, fileAccessAttributes.getOwnerId(), byteOrder);
+ offset += Integer.BYTES;
+ if (offset != SIZE_OF_FILE_ATTRIBUTES) {
+ Log.wtf(TAG, "Error: Serialized byte[] is of unexpected size");
+ }
+ return bytes;
+ }
+
+ /**
+ * Deserialize the byte[] data into the corresponding fields - id, mediaType, isPending,
+ * isTrashed and ownerId in that order, and returns an instance of FileAccessAttributes
+ * containing this deserialized data.
+ * @param data Data that is read from the file inode as a result of the xattr call
+ * @return FileAccessAttributes
+ */
+ public static FileAccessAttributes deserializeFileAccessAttributes(byte[] data) {
+ ByteOrder byteOrder = ByteOrder.nativeOrder();
+ int offset = 0;
+
+ long id = Memory.peekLong(data, offset, byteOrder);
+ offset += Long.BYTES;
+
+ int mediaType = Memory.peekInt(data, offset, byteOrder);
+ offset += Integer.BYTES;
+
+ boolean isPending = data[offset++] != 0;
+ boolean isTrashed = data[offset++] != 0;
+
+ int ownerId = Memory.peekInt(data, offset, byteOrder);
+ offset += Integer.BYTES;
+ if (offset != SIZE_OF_FILE_ATTRIBUTES) {
+ Log.wtf(TAG, " Error: Deserialized attributes are of unexpected size");
+ }
+ return new FileAccessAttributes(id, mediaType, isPending, isTrashed,
+ ownerId, null);
+ }
+}
diff --git a/src/com/android/providers/media/util/XmpInterface.java b/src/com/android/providers/media/util/XmpInterface.java
index f5ead46..05615ea 100644
--- a/src/com/android/providers/media/util/XmpInterface.java
+++ b/src/com/android/providers/media/util/XmpInterface.java
@@ -22,6 +22,7 @@
import android.media.ExifInterface;
import android.text.TextUtils;
+import android.util.Log;
import android.util.Xml;
import androidx.annotation.NonNull;
@@ -52,6 +53,7 @@
* any subsequent attempts to redefine that value.
*/
public class XmpInterface {
+ private static final String TAG = "XmpInterface";
private static final String NS_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
private static final String NS_XMP = "http://ns.adobe.com/xap/1.0/";
private static final String NS_XMPMM = "http://ns.adobe.com/xap/1.0/mm/";
@@ -130,6 +132,9 @@
}
} catch (XmlPullParserException e) {
throw new IOException(e);
+ } catch (OutOfMemoryError e) {
+ Log.w(TAG, "Couldn't read large xmp", e);
+ throw new IOException(e);
}
}
diff --git a/tests/Android.bp b/tests/Android.bp
index 8a0503d..05aa556 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -1,3 +1,8 @@
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
android_test_helper_app {
name: "MediaProviderTestAppForPermissionActivity",
manifest: "test_app/TestAppForPermissionActivity.xml",
@@ -12,7 +17,26 @@
target_sdk_version: "30",
min_sdk_version: "30",
test_suites: [
- "device-tests",
+ "general-tests",
+ "mts-mediaprovider",
+ ],
+}
+
+android_test_helper_app {
+ name: "MediaProviderTestAppForPermissionActivity33",
+ manifest: "test_app/TestAppForPermissionActivity33.xml",
+ srcs: [
+ "test_app/src/**/*.java",
+ "src/com/android/providers/media/util/TestUtils.java",
+ ],
+ static_libs: [
+ "cts-install-lib",
+ ],
+ sdk_version: "test_current",
+ target_sdk_version: "33",
+ min_sdk_version: "30",
+ test_suites: [
+ "general-tests",
"mts-mediaprovider",
],
}
@@ -31,7 +55,26 @@
target_sdk_version: "30",
min_sdk_version: "30",
test_suites: [
- "device-tests",
+ "general-tests",
+ "mts-mediaprovider",
+ ],
+}
+
+android_test_helper_app {
+ name: "MediaProviderTestAppWithMediaPerms",
+ manifest: "test_app/TestAppWithMediaPerms.xml",
+ srcs: [
+ "test_app/src/**/*.java",
+ "src/com/android/providers/media/util/TestUtils.java",
+ ],
+ static_libs: [
+ "cts-install-lib",
+ ],
+ sdk_version: "test_current",
+ target_sdk_version: "30",
+ min_sdk_version: "30",
+ test_suites: [
+ "general-tests",
"mts-mediaprovider",
],
}
@@ -50,7 +93,7 @@
target_sdk_version: "30",
min_sdk_version: "30",
test_suites: [
- "device-tests",
+ "general-tests",
"mts-mediaprovider",
],
}
@@ -69,7 +112,7 @@
target_sdk_version: "28",
min_sdk_version: "30",
test_suites: [
- "device-tests",
+ "general-tests",
"mts-mediaprovider",
],
}
@@ -79,19 +122,10 @@
// on the device being tested, so we can't sign our tests with a key that
// will allow instrumentation. Thus we pull all the sources we need to
// run tests against into the test itself.
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "packages_providers_MediaProvider_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["packages_providers_MediaProvider_license"],
-}
-
android_test {
name: "MediaProviderTests",
test_suites: [
- "device-tests",
+ "general-tests",
"mts-mediaprovider",
],
compile_multilib: "both",
@@ -115,17 +149,38 @@
"android.test.mock",
"android.test.runner",
"unsupportedappusage",
+ "framework-mediaprovider.impl",
],
static_libs: [
"androidx.appcompat_appcompat",
+ "modules-utils-backgroundthread",
"androidx.core_core",
"androidx.test.rules",
"guava",
- "mockito-target",
+ "mockito-target-extended-minus-junit4",
"modules-utils-build",
"truth-prebuilt",
+ "com.google.android.material_material",
"cts-install-lib",
+ "androidx.test.espresso.core",
+ "androidx.test.espresso.contrib",
+ "androidx.test.core",
+ "androidx.arch.core_core-runtime",
+ "glide-prebuilt",
+ "glide-gifdecoder-prebuilt",
+ "glide-disklrucache-prebuilt",
+ "glide-annotation-and-compiler-prebuilt",
+ "androidx.fragment_fragment",
+ "androidx.vectordrawable_vectordrawable-animated",
+ "androidx.exifinterface_exifinterface",
+ "exoplayer-mediaprovider-ui",
+ ],
+
+ // these are needed for Extended Mockito
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
],
certificate: "media",
@@ -141,8 +196,10 @@
java_resources: [
":MediaProviderTestAppWithStoragePerms",
+ ":MediaProviderTestAppWithMediaPerms",
":MediaProviderTestAppWithoutPerms",
":MediaProviderTestAppForPermissionActivity",
+ ":MediaProviderTestAppForPermissionActivity33",
":LegacyMediaProviderTestApp",
],
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 199f617..fd94863 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -7,12 +7,69 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
- <application android:label="MediaProvider Tests">
+ <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">
<uses-library android:name="android.test.runner" />
<activity android:name="com.android.providers.media.GetResultActivity" />
<activity android:name="com.android.providers.media.PermissionActivity" />
<activity android:name="com.android.providers.media.CacheClearingActivity" />
+ <activity android:name="com.android.providers.media.photopicker.espresso.PhotoPickerTestActivity"
+ android:theme="@style/PickerDefaultTheme"
+ android:excludeFromRecents="true">
+ <intent-filter>
+ <action android:name="android.provider.action.PICK_IMAGES" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ <data android:mimeType="image/*" />
+ <data android:mimeType="video/*" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.provider.action.PICK_IMAGES" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
+ <provider android:name="com.android.providers.media.photopicker.LocalProvider"
+ android:authorities="com.android.providers.media.photopicker.tests.local"
+ android:exported="false" />
+
+ <provider android:name="com.android.providers.media.cloudproviders.CloudProviderPrimary"
+ android:authorities="com.android.providers.media.photopicker.tests.cloud_primary"
+ android:permission="com.android.providers.media.permission.MANAGE_CLOUD_MEDIA_PROVIDERS"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.content.action.CLOUD_MEDIA_PROVIDER" />
+ </intent-filter>
+ </provider>
+
+ <provider android:name="com.android.providers.media.cloudproviders.CloudProviderSecondary"
+ android:authorities="com.android.providers.media.photopicker.tests.cloud_secondary"
+ android:readPermission="com.android.providers.media.permission.MANAGE_CLOUD_MEDIA_PROVIDERS"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.content.action.CLOUD_MEDIA_PROVIDER" />
+ </intent-filter>
+ </provider>
+
+ <provider android:name="com.android.providers.media.cloudproviders.CloudProviderNoPermission"
+ android:authorities="com.android.providers.media.photopicker.tests.cloud_no_permission"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.content.action.CLOUD_MEDIA_PROVIDER" />
+ </intent-filter>
+ </provider>
+
+ <provider android:name="com.android.providers.media.cloudproviders.CloudProviderNoIntentFilter"
+ android:permission="com.android.providers.media.permission.MANAGE_CLOUD_MEDIA_PROVIDERS"
+ android:authorities="com.android.providers.media.photopicker.tests.cloud_no_intent_filter"
+ android:exported="true">
+ </provider>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml
index 1cae732..7be4b76 100644
--- a/tests/AndroidTest.xml
+++ b/tests/AndroidTest.xml
@@ -17,12 +17,19 @@
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
<option name="test-file-name" value="MediaProviderTests.apk" />
<option name="test-file-name" value="MediaProviderTestAppForPermissionActivity.apk" />
+ <option name="test-file-name" value="MediaProviderTestAppForPermissionActivity33.apk" />
<option name="test-file-name" value="MediaProviderTestAppWithStoragePerms.apk" />
+ <option name="test-file-name" value="MediaProviderTestAppWithMediaPerms.apk" />
<option name="test-file-name" value="MediaProviderTestAppWithoutPerms.apk" />
<option name="test-file-name" value="LegacyMediaProviderTestApp.apk" />
<option name="install-arg" value="-g" />
</target_preparer>
+ <option
+ name="config-descriptor:metadata"
+ key="mainline-param"
+ value="com.google.android.mediaprovider.apex" />
+
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="framework-base-presubmit" />
<option name="test-tag" value="MediaProviderTests" />
@@ -30,8 +37,14 @@
<option name="package" value="com.android.providers.media.tests" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
+ <option name="instrumentation-arg" key="thisisignored" value="thisisignored --no-window-animation" />
</test>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
+ <option name="run-command" value="wm dismiss-keyguard" />
+ </target_preparer>
+
<object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
<option name="mainline-module-package-name" value="com.google.android.mediaprovider" />
</object>
diff --git a/tests/client/Android.bp b/tests/client/Android.bp
index 1541da6..7e51828 100644
--- a/tests/client/Android.bp
+++ b/tests/client/Android.bp
@@ -1,10 +1,6 @@
package {
// See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "packages_providers_MediaProvider_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["packages_providers_MediaProvider_license"],
+ default_applicable_licenses: ["Android-Apache-2.0"],
}
android_test {
diff --git a/tests/client/src/com/android/providers/media/client/DelegatorTest.java b/tests/client/src/com/android/providers/media/client/DelegatorTest.java
index 042223c..26c40fc 100644
--- a/tests/client/src/com/android/providers/media/client/DelegatorTest.java
+++ b/tests/client/src/com/android/providers/media/client/DelegatorTest.java
@@ -47,10 +47,10 @@
private static final String TAG = "DelegatorTest";
/**
- * To confirm behaviors, we need to pick an app installed on all devices
- * which has no permissions, and the best candidate is the "Easter Egg" app.
+ * To confirm behaviors, use the test app without permissions
*/
- private static final String PERMISSIONLESS_APP = "com.android.egg";
+ private static final String PERMISSIONLESS_APP =
+ "com.android.providers.media.testapp.withoutperms";
private ContentResolver mResolver;
diff --git a/tests/client/src/com/android/providers/media/client/DownloadProviderTest.java b/tests/client/src/com/android/providers/media/client/DownloadProviderTest.java
index b25e8df..3deb259 100644
--- a/tests/client/src/com/android/providers/media/client/DownloadProviderTest.java
+++ b/tests/client/src/com/android/providers/media/client/DownloadProviderTest.java
@@ -16,10 +16,10 @@
package com.android.providers.media.client;
-import static com.android.providers.media.client.PublicVolumeSetupHelper.createNewPublicVolume;
-import static com.android.providers.media.client.PublicVolumeSetupHelper.deletePublicVolumes;
-import static com.android.providers.media.client.PublicVolumeSetupHelper.executeShellCommand;
-import static com.android.providers.media.client.PublicVolumeSetupHelper.pollForCondition;
+import static com.android.providers.media.tests.utils.PublicVolumeSetupHelper.createNewPublicVolume;
+import static com.android.providers.media.tests.utils.PublicVolumeSetupHelper.deletePublicVolumes;
+import static com.android.providers.media.tests.utils.PublicVolumeSetupHelper.executeShellCommand;
+import static com.android.providers.media.tests.utils.PublicVolumeSetupHelper.pollForCondition;
import static com.google.common.truth.Truth.assertThat;
diff --git a/tests/client/src/com/android/providers/media/client/PublicVolumePlaylistTest.java b/tests/client/src/com/android/providers/media/client/PublicVolumePlaylistTest.java
index f4eea1f..af17d50 100644
--- a/tests/client/src/com/android/providers/media/client/PublicVolumePlaylistTest.java
+++ b/tests/client/src/com/android/providers/media/client/PublicVolumePlaylistTest.java
@@ -19,10 +19,10 @@
import static android.provider.MediaStore.VOLUME_EXTERNAL;
import static android.provider.MediaStore.VOLUME_EXTERNAL_PRIMARY;
-import static com.android.providers.media.client.PublicVolumeSetupHelper.createNewPublicVolume;
-import static com.android.providers.media.client.PublicVolumeSetupHelper.deletePublicVolumes;
-import static com.android.providers.media.client.PublicVolumeSetupHelper.mountPublicVolume;
-import static com.android.providers.media.client.PublicVolumeSetupHelper.unmountPublicVolume;
+import static com.android.providers.media.tests.utils.PublicVolumeSetupHelper.createNewPublicVolume;
+import static com.android.providers.media.tests.utils.PublicVolumeSetupHelper.deletePublicVolumes;
+import static com.android.providers.media.tests.utils.PublicVolumeSetupHelper.mountPublicVolume;
+import static com.android.providers.media.tests.utils.PublicVolumeSetupHelper.unmountPublicVolume;
import static com.google.common.truth.Truth.assertThat;
diff --git a/tests/client/src/com/android/providers/media/client/PublicVolumeTest.java b/tests/client/src/com/android/providers/media/client/PublicVolumeTest.java
index def72f7..988dcf9 100644
--- a/tests/client/src/com/android/providers/media/client/PublicVolumeTest.java
+++ b/tests/client/src/com/android/providers/media/client/PublicVolumeTest.java
@@ -16,10 +16,10 @@
package com.android.providers.media.client;
-import static com.android.providers.media.client.PublicVolumeSetupHelper.createNewPublicVolume;
-import static com.android.providers.media.client.PublicVolumeSetupHelper.deletePublicVolumes;
-import static com.android.providers.media.client.PublicVolumeSetupHelper.mountPublicVolume;
-import static com.android.providers.media.client.PublicVolumeSetupHelper.unmountPublicVolume;
+import static com.android.providers.media.tests.utils.PublicVolumeSetupHelper.createNewPublicVolume;
+import static com.android.providers.media.tests.utils.PublicVolumeSetupHelper.deletePublicVolumes;
+import static com.android.providers.media.tests.utils.PublicVolumeSetupHelper.mountPublicVolume;
+import static com.android.providers.media.tests.utils.PublicVolumeSetupHelper.unmountPublicVolume;
import static com.google.common.truth.Truth.assertThat;
diff --git a/tests/res/raw/test_animated_webp.webp b/tests/res/raw/test_animated_webp.webp
new file mode 100644
index 0000000..24a4c68
--- /dev/null
+++ b/tests/res/raw/test_animated_webp.webp
Binary files differ
diff --git a/tests/res/raw/test_gif.gif b/tests/res/raw/test_gif.gif
new file mode 100644
index 0000000..d2e2aad
--- /dev/null
+++ b/tests/res/raw/test_gif.gif
Binary files differ
diff --git a/tests/res/raw/test_motion_photo.jpg b/tests/res/raw/test_motion_photo.jpg
new file mode 100644
index 0000000..da4bcb1
--- /dev/null
+++ b/tests/res/raw/test_motion_photo.jpg
Binary files differ
diff --git a/tests/res/raw/test_non_animated_webp.webp b/tests/res/raw/test_non_animated_webp.webp
new file mode 100644
index 0000000..a622fee
--- /dev/null
+++ b/tests/res/raw/test_non_animated_webp.webp
Binary files differ
diff --git a/tests/src/com/android/providers/media/DatabaseHelperTest.java b/tests/src/com/android/providers/media/DatabaseHelperTest.java
index eef8bbd..f1ce350 100644
--- a/tests/src/com/android/providers/media/DatabaseHelperTest.java
+++ b/tests/src/com/android/providers/media/DatabaseHelperTest.java
@@ -18,15 +18,18 @@
import static android.provider.MediaStore.VOLUME_EXTERNAL_PRIMARY;
-import static com.android.providers.media.DatabaseHelper.VERSION_LATEST;
+import static com.android.providers.media.DatabaseHelper.TEST_CLEAN_DB;
+import static com.android.providers.media.DatabaseHelper.TEST_DOWNGRADE_DB;
+import static com.android.providers.media.DatabaseHelper.TEST_RECOMPUTE_DB;
+import static com.android.providers.media.DatabaseHelper.TEST_UPGRADE_DB;
import static com.android.providers.media.DatabaseHelper.makePristineSchema;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
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 android.Manifest;
@@ -37,6 +40,8 @@
import android.database.sqlite.SQLiteDatabase;
import android.os.UserHandle;
import android.provider.Column;
+import android.provider.ExportedSince;
+import android.provider.MediaStore.Audio;
import android.provider.MediaStore.Audio.AudioColumns;
import android.provider.MediaStore.Files.FileColumns;
import android.util.Log;
@@ -59,12 +64,6 @@
@RunWith(AndroidJUnit4.class)
public class DatabaseHelperTest {
private static final String TAG = "DatabaseHelperTest";
-
- private static final String TEST_RECOMPUTE_DB = "test_recompute";
- private static final String TEST_UPGRADE_DB = "test_upgrade";
- private static final String TEST_DOWNGRADE_DB = "test_downgrade";
- private static final String TEST_CLEAN_DB = "test_clean";
-
private static final String SQLITE_MASTER_ORDER_BY = "type,name,tbl_name";
private static Context sIsolatedContext;
@@ -81,7 +80,7 @@
@Test
public void testFilterVolumeNames() throws Exception {
- try (DatabaseHelper helper = new DatabaseHelperS(sIsolatedContext, TEST_CLEAN_DB)) {
+ try (DatabaseHelper helper = new DatabaseHelperT(sIsolatedContext, TEST_CLEAN_DB)) {
SQLiteDatabase db = helper.getWritableDatabaseForTest();
{
final ContentValues values = new ContentValues();
@@ -240,23 +239,28 @@
}
@Test
- public void testStoO() throws Exception {
- assertDowngrade(DatabaseHelperS.class, DatabaseHelperO.class);
+ public void testTtoO() throws Exception {
+ assertDowngrade(DatabaseHelperT.class, DatabaseHelperO.class);
}
@Test
- public void testStoP() throws Exception {
- assertDowngrade(DatabaseHelperS.class, DatabaseHelperP.class);
+ public void testTtoP() throws Exception {
+ assertDowngrade(DatabaseHelperT.class, DatabaseHelperP.class);
}
@Test
- public void testStoQ() throws Exception {
- assertDowngrade(DatabaseHelperS.class, DatabaseHelperQ.class);
+ public void testTtoQ() throws Exception {
+ assertDowngrade(DatabaseHelperT.class, DatabaseHelperQ.class);
}
@Test
- public void testStoR() throws Exception {
- assertDowngrade(DatabaseHelperS.class, DatabaseHelperR.class);
+ public void testTtoR() throws Exception {
+ assertDowngrade(DatabaseHelperT.class, DatabaseHelperR.class);
+ }
+
+ @Test
+ public void testTtoS() throws Exception {
+ assertDowngrade(DatabaseHelperT.class, DatabaseHelperS.class);
}
private void assertDowngrade(Class<? extends DatabaseHelper> before,
@@ -264,6 +268,7 @@
try (DatabaseHelper helper = before.getConstructor(Context.class, String.class)
.newInstance(sIsolatedContext, TEST_DOWNGRADE_DB)) {
SQLiteDatabase db = helper.getWritableDatabaseForTest();
+ assertThat(sIsolatedContext.getDatabasePath(TEST_DOWNGRADE_DB).exists()).isTrue();
{
final ContentValues values = new ContentValues();
values.put(FileColumns.DATA,
@@ -279,36 +284,39 @@
}
}
- // Downgrade will wipe data, but at least we don't crash
+ // Downgrade will delete the database file and crash the process
try (DatabaseHelper helper = after.getConstructor(Context.class, String.class)
.newInstance(sIsolatedContext, TEST_DOWNGRADE_DB)) {
- SQLiteDatabase db = helper.getWritableDatabaseForTest();
- try (Cursor c = db.query("files", null, null, null, null, null, null, null)) {
- assertEquals(0, c.getCount());
- }
+ assertThrows(RuntimeException.class, helper::getWritableDatabaseForTest);
+ assertThat(sIsolatedContext.getDatabasePath(TEST_DOWNGRADE_DB).exists()).isFalse();
}
}
@Test
- public void testOtoS() throws Exception {
- assertRecompute(DatabaseHelperO.class, DatabaseHelperS.class);
- assertUpgrade(DatabaseHelperO.class, DatabaseHelperS.class);
+ public void testOtoT() throws Exception {
+ assertRecompute(DatabaseHelperO.class, DatabaseHelperT.class);
+ assertUpgrade(DatabaseHelperO.class, DatabaseHelperT.class);
}
@Test
- public void testPtoS() throws Exception {
- assertRecompute(DatabaseHelperP.class, DatabaseHelperS.class);
- assertUpgrade(DatabaseHelperP.class, DatabaseHelperS.class);
+ public void testPtoT() throws Exception {
+ assertRecompute(DatabaseHelperP.class, DatabaseHelperT.class);
+ assertUpgrade(DatabaseHelperP.class, DatabaseHelperT.class);
}
@Test
- public void testQtoS() throws Exception {
- assertUpgrade(DatabaseHelperQ.class, DatabaseHelperS.class);
+ public void testQtoT() throws Exception {
+ assertUpgrade(DatabaseHelperQ.class, DatabaseHelperT.class);
}
@Test
- public void testRtoS() throws Exception {
- assertUpgrade(DatabaseHelperR.class, DatabaseHelperS.class);
+ public void testRtoT() throws Exception {
+ assertUpgrade(DatabaseHelperR.class, DatabaseHelperT.class);
+ }
+
+ @Test
+ public void testStoT() throws Exception {
+ assertUpgrade(DatabaseHelperS.class, DatabaseHelperT.class);
}
private void assertRecompute(Class<? extends DatabaseHelper> before,
@@ -527,52 +535,34 @@
}
}
- /**
- * Test that database downgrade changed the UUID saved in database file.
- */
@Test
- public void testDowngradeChangesUUID() throws Exception {
- Class<? extends DatabaseHelper> dbVersionHigher = DatabaseHelperS.class;
- Class<? extends DatabaseHelper> dbVersionLower = DatabaseHelperR.class;
- String originalUUID;
- int originalVersion;
-
- // Create the database with database version = dbVersionLower
- try (DatabaseHelper helper = dbVersionLower.getConstructor(Context.class, String.class)
- .newInstance(sIsolatedContext, TEST_DOWNGRADE_DB)) {
+ public void testAddSpecialFormat() throws Exception {
+ try (DatabaseHelper helper = new DatabaseHelperS(sIsolatedContext, TEST_UPGRADE_DB)) {
SQLiteDatabase db = helper.getWritableDatabaseForTest();
- originalUUID = DatabaseHelper.getOrCreateUuid(db);
- originalVersion = db.getVersion();
- // Verify that original version of the database is dbVersionLower.
- assertWithMessage("Current database version")
- .that(db.getVersion()).isEqualTo(DatabaseHelper.VERSION_R);
+ {
+ // Insert a row before database upgrade.
+ final ContentValues values = new ContentValues();
+ values.put(FileColumns.DATA, "/storage/emulated/0/DCIM/test.jpg");
+ assertThat(db.insert("files", FileColumns.DATA, values)).isNotEqualTo(-1);
+ }
}
- // Upgrade the database by changing the version to dbVersionHigher
- try (DatabaseHelper helper = dbVersionHigher.getConstructor(Context.class, String.class)
- .newInstance(sIsolatedContext, TEST_DOWNGRADE_DB)) {
+ try (DatabaseHelper helper = new DatabaseHelperT(sIsolatedContext, TEST_UPGRADE_DB)) {
SQLiteDatabase db = helper.getWritableDatabaseForTest();
- // Verify that upgrade resulted in database version change.
- assertWithMessage("Current database version after upgrade")
- .that(db.getVersion()).isNotEqualTo(originalVersion);
- // Verify that upgrade resulted in database version same as latest version.
- assertWithMessage("Current database version after upgrade")
- .that(db.getVersion()).isEqualTo(VERSION_LATEST);
- // Verify that upgrade didn't change UUID
- assertWithMessage("Current database UUID after upgrade")
- .that(DatabaseHelper.getOrCreateUuid(db)).isEqualTo(originalUUID);
- }
+ // Insert a row in the new version as well
+ final ContentValues values = new ContentValues();
+ values.put(FileColumns.DATA, "/storage/emulated/0/DCIM/test2.jpg");
+ assertThat(db.insert("files", FileColumns.DATA, values)).isNotEqualTo(-1);
- // Downgrade the database by changing the version to dbVersionLower
- try (DatabaseHelper helper = dbVersionLower.getConstructor(Context.class, String.class)
- .newInstance(sIsolatedContext, TEST_DOWNGRADE_DB)) {
- SQLiteDatabase db = helper.getWritableDatabaseForTest();
- // Verify that downgraded version is same as original database version before upgrade
- assertWithMessage("Current database version after downgrade")
- .that(db.getVersion()).isEqualTo(originalVersion);
- // Verify that downgrade changed UUID
- assertWithMessage("Current database UUID after downgrade")
- .that(DatabaseHelper.getOrCreateUuid(db)).isNotEqualTo(originalUUID);
+ try (Cursor cr = db.query("files", new String[]{FileColumns._SPECIAL_FORMAT}, null,
+ null, null, null, null)) {
+ assertEquals(2, cr.getCount());
+ while (cr.moveToNext()) {
+ // Verify that after db upgrade, for all database rows (new inserts and
+ // upgrades), we set _special_format column as NULL
+ assertThat(cr.isNull(0)).isTrue();
+ }
+ }
}
}
@@ -594,8 +584,8 @@
private static class DatabaseHelperO extends DatabaseHelper {
public DatabaseHelperO(Context context, String name) {
- super(context, name, DatabaseHelper.VERSION_O,
- false, false, false, Column.class, null, null, null, null);
+ super(context, name, DatabaseHelper.VERSION_O, false, false, Column.class,
+ ExportedSince.class, null, null, null, null, false);
}
@Override
@@ -606,8 +596,8 @@
private static class DatabaseHelperP extends DatabaseHelper {
public DatabaseHelperP(Context context, String name) {
- super(context, name, DatabaseHelper.VERSION_P,
- false, false, false, Column.class, null, null, null, null);
+ super(context, name, DatabaseHelper.VERSION_P, false, false, Column.class,
+ ExportedSince.class, null, null, null, null, false);
}
@Override
@@ -618,8 +608,8 @@
private static class DatabaseHelperQ extends DatabaseHelper {
public DatabaseHelperQ(Context context, String name) {
- super(context, name, DatabaseHelper.VERSION_Q,
- false, false, false, Column.class, null, null, null, null);
+ super(context, name, DatabaseHelper.VERSION_Q, false, false, Column.class,
+ ExportedSince.class, null, null, null, null, false);
}
@Override
@@ -630,9 +620,8 @@
private static class DatabaseHelperR extends DatabaseHelper {
public DatabaseHelperR(Context context, String name) {
- super(context, name, DatabaseHelper.VERSION_R,
- false, false, false, Column.class, null, null,
- MediaProvider.MIGRATION_LISTENER, null);
+ super(context, name, DatabaseHelper.VERSION_R, false, false, Column.class,
+ ExportedSince.class, null, null, MediaProvider.MIGRATION_LISTENER, null, false);
}
@Override
@@ -643,9 +632,21 @@
private static class DatabaseHelperS extends DatabaseHelper {
public DatabaseHelperS(Context context, String name) {
- super(context, name, DatabaseHelper.VERSION_S,
- false, false, false, Column.class, null, null,
- MediaProvider.MIGRATION_LISTENER, null);
+ super(context, name, VERSION_S, false, false, Column.class, ExportedSince.class, null,
+ null, MediaProvider.MIGRATION_LISTENER, null, false);
+ }
+
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ createSSchema(db, false);
+ }
+ }
+
+ private static class DatabaseHelperT extends DatabaseHelper {
+ public DatabaseHelperT(Context context, String name) {
+ super(context, name, DatabaseHelper.VERSION_T, false, false, Column.class,
+ ExportedSince.class, null, null, MediaProvider.MIGRATION_LISTENER, null, false);
}
}
@@ -1221,4 +1222,197 @@
db.execSQL("CREATE INDEX titlekey_index ON files(title_key)");
}
+ /**
+ * Snapshot of {@link DatabaseHelper#createLatestSchema} as of
+ * {@link android.os.Build.VERSION_CODES#S}.
+ */
+ private static void createSSchema(SQLiteDatabase db, boolean internal) {
+ makePristineSchema(db);
+
+ // CAUTION: THIS IS A SNAPSHOTTED GOLDEN SCHEMA THAT SHOULD NEVER BE
+ // DIRECTLY MODIFIED, SINCE IT REPRESENTS A DEVICE IN THE WILD THAT WE
+ // MUST SUPPORT. IF TESTS ARE FAILING, THE CORRECT FIX IS TO ADJUST THE
+ // DATABASE UPGRADE LOGIC TO MIGRATE THIS SNAPSHOTTED GOLDEN SCHEMA TO
+ // THE LATEST SCHEMA.
+
+ db.execSQL("CREATE TABLE local_metadata (generation INTEGER DEFAULT 0)");
+ db.execSQL("INSERT INTO local_metadata VALUES (0)");
+
+ db.execSQL("CREATE TABLE android_metadata (locale TEXT)");
+ db.execSQL("CREATE TABLE thumbnails (_id INTEGER PRIMARY KEY,_data TEXT,image_id INTEGER,"
+ + "kind INTEGER,width INTEGER,height INTEGER)");
+ db.execSQL("CREATE TABLE album_art (album_id INTEGER PRIMARY KEY,_data TEXT)");
+ db.execSQL("CREATE TABLE videothumbnails (_id INTEGER PRIMARY KEY,_data TEXT,"
+ + "video_id INTEGER,kind INTEGER,width INTEGER,height INTEGER)");
+ db.execSQL("CREATE TABLE files (_id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + "_data TEXT UNIQUE COLLATE NOCASE,_size INTEGER,format INTEGER,parent INTEGER,"
+ + "date_added INTEGER,date_modified INTEGER,mime_type TEXT,title TEXT,"
+ + "description TEXT,_display_name TEXT,picasa_id TEXT,orientation INTEGER,"
+ + "latitude DOUBLE,longitude DOUBLE,datetaken INTEGER,mini_thumb_magic INTEGER,"
+ + "bucket_id TEXT,bucket_display_name TEXT,isprivate INTEGER,title_key TEXT,"
+ + "artist_id INTEGER,album_id INTEGER,composer TEXT,track INTEGER,"
+ + "year INTEGER CHECK(year!=0),is_ringtone INTEGER,is_music INTEGER,"
+ + "is_alarm INTEGER,is_notification INTEGER,is_podcast INTEGER,album_artist TEXT,"
+ + "duration INTEGER,bookmark INTEGER,artist TEXT,album TEXT,resolution TEXT,"
+ + "tags TEXT,category TEXT,language TEXT,mini_thumb_data TEXT,name TEXT,"
+ + "media_type INTEGER,old_id INTEGER,is_drm INTEGER,"
+ + "width INTEGER, height INTEGER, title_resource_uri TEXT,"
+ + "owner_package_name TEXT DEFAULT NULL,"
+ + "color_standard INTEGER, color_transfer INTEGER, color_range INTEGER,"
+ + "_hash BLOB DEFAULT NULL, is_pending INTEGER DEFAULT 0,"
+ + "is_download INTEGER DEFAULT 0, download_uri TEXT DEFAULT NULL,"
+ + "referer_uri TEXT DEFAULT NULL, is_audiobook INTEGER DEFAULT 0,"
+ + "date_expires INTEGER DEFAULT NULL,is_trashed INTEGER DEFAULT 0,"
+ + "group_id INTEGER DEFAULT NULL,primary_directory TEXT DEFAULT NULL,"
+ + "secondary_directory TEXT DEFAULT NULL,document_id TEXT DEFAULT NULL,"
+ + "instance_id TEXT DEFAULT NULL,original_document_id TEXT DEFAULT NULL,"
+ + "relative_path TEXT DEFAULT NULL,volume_name TEXT DEFAULT NULL,"
+ + "artist_key TEXT DEFAULT NULL,album_key TEXT DEFAULT NULL,"
+ + "genre TEXT DEFAULT NULL,genre_key TEXT DEFAULT NULL,genre_id INTEGER,"
+ + "author TEXT DEFAULT NULL, bitrate INTEGER DEFAULT NULL,"
+ + "capture_framerate REAL DEFAULT NULL, cd_track_number TEXT DEFAULT NULL,"
+ + "compilation INTEGER DEFAULT NULL, disc_number TEXT DEFAULT NULL,"
+ + "is_favorite INTEGER DEFAULT 0, num_tracks INTEGER DEFAULT NULL,"
+ + "writer TEXT DEFAULT NULL, exposure_time TEXT DEFAULT NULL,"
+ + "f_number TEXT DEFAULT NULL, iso INTEGER DEFAULT NULL,"
+ + "scene_capture_type INTEGER DEFAULT NULL, generation_added INTEGER DEFAULT 0,"
+ + "generation_modified INTEGER DEFAULT 0, xmp BLOB DEFAULT NULL,"
+ + "_transcode_status INTEGER DEFAULT 0, _video_codec_type TEXT DEFAULT NULL,"
+ + "_modifier INTEGER DEFAULT 0, is_recording INTEGER DEFAULT 0,"
+ + "redacted_uri_id TEXT DEFAULT NULL, _user_id INTEGER DEFAULT "
+ + UserHandle.myUserId() + ")");
+
+ db.execSQL("CREATE TABLE log (time DATETIME, message TEXT)");
+ if (!internal) {
+ db.execSQL("CREATE TABLE audio_playlists_map (_id INTEGER PRIMARY KEY,"
+ + "audio_id INTEGER NOT NULL,playlist_id INTEGER NOT NULL,"
+ + "play_order INTEGER NOT NULL)");
+ }
+
+ if (!internal) {
+ db.execSQL("CREATE VIEW audio_playlists AS SELECT _id,_data,name,date_added,"
+ + "date_modified,owner_package_name,_hash,is_pending,date_expires,is_trashed,"
+ + "volume_name FROM files WHERE media_type=4");
+ }
+
+ db.execSQL("CREATE VIEW searchhelpertitle AS SELECT * FROM audio ORDER BY title_key");
+ db.execSQL("CREATE VIEW search AS SELECT _id,'artist' AS mime_type,artist,NULL AS album,"
+ + "NULL AS title,artist AS text1,NULL AS text2,number_of_albums AS data1,"
+ + "number_of_tracks AS data2,artist_key AS match,"
+ + "'content://media/external/audio/artists/'||_id AS suggest_intent_data,"
+ + "1 AS grouporder FROM artist_info WHERE (artist!='<unknown>')"
+ + " UNION ALL SELECT _id,'album' AS mime_type,artist,album,"
+ + "NULL AS title,album AS text1,artist AS text2,NULL AS data1,"
+ + "NULL AS data2,artist_key||' '||album_key AS match,"
+ + "'content://media/external/audio/albums/'||_id AS suggest_intent_data,"
+ + "2 AS grouporder FROM album_info"
+ + " WHERE (album!='<unknown>')"
+ + " UNION ALL SELECT searchhelpertitle._id AS _id,mime_type,artist,album,title,"
+ + "title AS text1,artist AS text2,NULL AS data1,"
+ + "NULL AS data2,artist_key||' '||album_key||' '||title_key AS match,"
+ + "'content://media/external/audio/media/'||searchhelpertitle._id"
+ + " AS suggest_intent_data,"
+ + "3 AS grouporder FROM searchhelpertitle WHERE (title != '')");
+
+ db.execSQL("CREATE VIEW audio AS SELECT "
+ + "title_key,instance_id,compilation,disc_number,duration,is_ringtone,album_artist,resolution,orientation,artist,author,height,is_drm,bucket_display_name,is_audiobook,owner_package_name,volume_name,title_resource_uri,date_modified,writer,date_expires,composer,_display_name,datetaken,mime_type,is_notification,bitrate,cd_track_number,_id,xmp,year,_data,_size,album,genre,is_alarm,title,track,width,is_music,album_key,is_favorite,is_trashed,group_id,document_id,artist_id,generation_added,artist_key,genre_key,is_download,generation_modified,is_pending,date_added,is_podcast,capture_framerate,album_id,num_tracks,original_document_id,genre_id,bucket_id,bookmark,relative_path"
+ + " FROM files WHERE media_type=2");
+ db.execSQL("CREATE VIEW video AS SELECT"
+ + " instance_id,compilation,disc_number,duration,album_artist,description,language,resolution,latitude,orientation,artist,color_transfer,author,color_standard,height,is_drm,bucket_display_name,owner_package_name,volume_name,date_modified,writer,date_expires,composer,_display_name,datetaken,mime_type,bitrate,cd_track_number,_id,xmp,tags,year,category,_data,_size,album,genre,title,width,longitude,is_favorite,is_trashed,group_id,document_id,generation_added,is_download,generation_modified,is_pending,date_added,mini_thumb_magic,capture_framerate,color_range,num_tracks,isprivate,original_document_id,bucket_id,bookmark,relative_path"
+ + " FROM files WHERE media_type=3");
+ db.execSQL("CREATE VIEW images AS SELECT"
+ + " instance_id,compilation,disc_number,duration,album_artist,description,picasa_id,resolution,latitude,orientation,artist,author,height,is_drm,bucket_display_name,owner_package_name,f_number,volume_name,date_modified,writer,date_expires,composer,_display_name,scene_capture_type,datetaken,mime_type,bitrate,cd_track_number,_id,iso,xmp,year,_data,_size,album,genre,title,width,longitude,is_favorite,is_trashed,exposure_time,group_id,document_id,generation_added,is_download,generation_modified,is_pending,date_added,mini_thumb_magic,capture_framerate,num_tracks,isprivate,original_document_id,bucket_id,relative_path"
+ + " FROM files WHERE media_type=1");
+ db.execSQL("CREATE VIEW downloads AS SELECT"
+ + " instance_id,compilation,disc_number,duration,album_artist,description,resolution,orientation,artist,author,height,is_drm,bucket_display_name,owner_package_name,volume_name,date_modified,writer,date_expires,composer,_display_name,datetaken,mime_type,bitrate,cd_track_number,referer_uri,_id,xmp,year,_data,_size,album,genre,title,width,is_favorite,is_trashed,group_id,document_id,generation_added,is_download,generation_modified,is_pending,date_added,download_uri,capture_framerate,num_tracks,original_document_id,bucket_id,relative_path"
+ + " FROM files WHERE is_download=1");
+
+ db.execSQL("CREATE VIEW audio_artists AS SELECT "
+ + " artist_id AS " + Audio.Artists._ID
+ + ", MIN(artist) AS " + Audio.Artists.ARTIST
+ + ", artist_key AS " + Audio.Artists.ARTIST_KEY
+ + ", COUNT(DISTINCT album_id) AS " + Audio.Artists.NUMBER_OF_ALBUMS
+ + ", COUNT(DISTINCT _id) AS " + Audio.Artists.NUMBER_OF_TRACKS
+ + " FROM audio"
+ + " WHERE is_music=1 AND is_pending=0 AND is_trashed=0"
+ + " AND volume_name IN " + "()"
+ + " GROUP BY artist_id");
+
+ db.execSQL("CREATE VIEW audio_artists_albums AS SELECT "
+ + " album_id AS " + Audio.Albums._ID
+ + ", album_id AS " + Audio.Albums.ALBUM_ID
+ + ", MIN(album) AS " + Audio.Albums.ALBUM
+ + ", album_key AS " + Audio.Albums.ALBUM_KEY
+ + ", artist_id AS " + Audio.Albums.ARTIST_ID
+ + ", artist AS " + Audio.Albums.ARTIST
+ + ", artist_key AS " + Audio.Albums.ARTIST_KEY
+ + ", (SELECT COUNT(*) FROM audio WHERE " + Audio.Albums.ALBUM_ID
+ + " = TEMP.album_id) AS " + Audio.Albums.NUMBER_OF_SONGS
+ + ", COUNT(DISTINCT _id) AS " + Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST
+ + ", MIN(year) AS " + Audio.Albums.FIRST_YEAR
+ + ", MAX(year) AS " + Audio.Albums.LAST_YEAR
+ + ", NULL AS " + Audio.Albums.ALBUM_ART
+ + " FROM audio TEMP"
+ + " WHERE is_music=1 AND is_pending=0 AND is_trashed=0"
+ + " AND volume_name IN " + "()"
+ + " GROUP BY album_id, artist_id");
+
+ db.execSQL("CREATE VIEW audio_albums AS SELECT "
+ + " album_id AS " + Audio.Albums._ID
+ + ", album_id AS " + Audio.Albums.ALBUM_ID
+ + ", MIN(album) AS " + Audio.Albums.ALBUM
+ + ", album_key AS " + Audio.Albums.ALBUM_KEY
+ + ", artist_id AS " + Audio.Albums.ARTIST_ID
+ + ", artist AS " + Audio.Albums.ARTIST
+ + ", artist_key AS " + Audio.Albums.ARTIST_KEY
+ + ", COUNT(DISTINCT _id) AS " + Audio.Albums.NUMBER_OF_SONGS
+ + ", COUNT(DISTINCT _id) AS " + Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST
+ + ", MIN(year) AS " + Audio.Albums.FIRST_YEAR
+ + ", MAX(year) AS " + Audio.Albums.LAST_YEAR
+ + ", NULL AS " + Audio.Albums.ALBUM_ART
+ + " FROM audio"
+ + " WHERE is_music=1 AND is_pending=0 AND is_trashed=0"
+ + " AND volume_name IN " + "()"
+ + " GROUP BY album_id");
+
+ db.execSQL("CREATE VIEW audio_genres AS SELECT "
+ + " genre_id AS " + Audio.Genres._ID
+ + ", MIN(genre) AS " + Audio.Genres.NAME
+ + " FROM audio"
+ + " WHERE is_pending=0 AND is_trashed=0 AND volume_name IN " + "()"
+ + " GROUP BY genre_id");
+
+ final String insertArg =
+ "new.volume_name||':'||new._id||':'||new.media_type||':'||new.is_download";
+ final String updateArg =
+ "old.volume_name||':'||old._id||':'||old.media_type||':'||old.is_download"
+ + "||':'||new._id||':'||new.media_type||':'||new.is_download"
+ + "||':'||ifnull(old.owner_package_name,'null')"
+ + "||':'||ifnull(new.owner_package_name,'null')||':'||old._data";
+ final String deleteArg =
+ "old.volume_name||':'||old._id||':'||old.media_type||':'||old.is_download"
+ + "||':'||ifnull(old.owner_package_name,'null')||':'||old._data";
+
+ db.execSQL("CREATE TRIGGER files_insert AFTER INSERT ON files"
+ + " BEGIN SELECT _INSERT(" + insertArg + "); END");
+ db.execSQL("CREATE TRIGGER files_update AFTER UPDATE ON files"
+ + " BEGIN SELECT _UPDATE(" + updateArg + "); END");
+ db.execSQL("CREATE TRIGGER files_delete AFTER DELETE ON files"
+ + " BEGIN SELECT _DELETE(" + deleteArg + "); END");
+
+ db.execSQL("CREATE INDEX image_id_index on thumbnails(image_id)");
+ db.execSQL("CREATE INDEX video_id_index on videothumbnails(video_id)");
+ db.execSQL("CREATE INDEX album_id_idx ON files(album_id)");
+ db.execSQL("CREATE INDEX artist_id_idx ON files(artist_id)");
+ db.execSQL("CREATE INDEX genre_id_idx ON files(genre_id)");
+ db.execSQL("CREATE INDEX bucket_index on files(bucket_id,media_type,datetaken, _id)");
+ db.execSQL("CREATE INDEX bucket_name on files(bucket_id,media_type,bucket_display_name)");
+ db.execSQL("CREATE INDEX format_index ON files(format)");
+ db.execSQL("CREATE INDEX media_type_index ON files(media_type)");
+ db.execSQL("CREATE INDEX parent_index ON files(parent)");
+ db.execSQL("CREATE INDEX path_index ON files(_data)");
+ db.execSQL("CREATE INDEX sort_index ON files(datetaken ASC, _id ASC)");
+ db.execSQL("CREATE INDEX title_idx ON files(title)");
+ db.execSQL("CREATE INDEX titlekey_index ON files(title_key)");
+ }
}
diff --git a/tests/src/com/android/providers/media/IdleServiceTest.java b/tests/src/com/android/providers/media/IdleServiceTest.java
index 064a4f8..80d2261 100644
--- a/tests/src/com/android/providers/media/IdleServiceTest.java
+++ b/tests/src/com/android/providers/media/IdleServiceTest.java
@@ -19,7 +19,12 @@
import static android.os.Environment.DIRECTORY_MOVIES;
import static android.os.Environment.DIRECTORY_PICTURES;
import static android.os.Environment.buildPath;
+import static android.provider.MediaStore.Files.FileColumns._SPECIAL_FORMAT;
+import static android.provider.MediaStore.Files.FileColumns._SPECIAL_FORMAT_NONE;
import static android.provider.MediaStore.MediaColumns.DATE_EXPIRES;
+import static android.provider.MediaStore.MediaColumns.DISPLAY_NAME;
+import static android.provider.MediaStore.MediaColumns.GENERATION_MODIFIED;
+import static android.provider.MediaStore.MediaColumns.RELATIVE_PATH;
import static com.google.common.truth.Truth.assertThat;
@@ -27,13 +32,15 @@
import static org.junit.Assert.assertTrue;
import android.Manifest;
-import android.app.UiAutomation;
+import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentUris;
+import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
@@ -70,8 +77,11 @@
public void setUp() {
final Context context = InstrumentationRegistry.getTargetContext();
InstrumentationRegistry.getInstrumentation().getUiAutomation()
- .adoptShellPermissionIdentity(Manifest.permission.LOG_COMPAT_CHANGE,
- Manifest.permission.READ_COMPAT_CHANGE_CONFIG);
+ .adoptShellPermissionIdentity(android.Manifest.permission.LOG_COMPAT_CHANGE,
+ android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
+ android.Manifest.permission.READ_DEVICE_CONFIG,
+ Manifest.permission.INTERACT_ACROSS_USERS,
+ android.Manifest.permission.DUMP);
mDir = new File(context.getExternalMediaDirs()[0], "test_" + System.nanoTime());
mDir.mkdirs();
@@ -89,10 +99,9 @@
public void testPruneThumbnails() throws Exception {
final Context context = InstrumentationRegistry.getTargetContext();
final ContentResolver resolver = context.getContentResolver();
-
// Previous tests (like DatabaseHelperTest) may have left stale
// .database_uuid files, do an idle run first to clean them up.
- runIdleMaintenance(resolver);
+ MediaStore.runIdleMaintenance(resolver);
MediaStore.waitForIdle(resolver);
final File dir = Environment.getExternalStorageDirectory();
@@ -114,7 +123,7 @@
final File d = touch(buildPath(dir, DIRECTORY_PICTURES, ".thumbnails", "random.bin"));
// Idle maintenance pass should clean up unknown files
- runIdleMaintenance(resolver);
+ MediaStore.runIdleMaintenance(resolver);
assertFalse(exists(a));
assertFalse(exists(b));
assertTrue(exists(c));
@@ -128,62 +137,151 @@
touch(uuidFile);
// Idle maintenance pass should clean up all files
- runIdleMaintenance(resolver);
+ MediaStore.runIdleMaintenance(resolver);
assertFalse(exists(a));
assertFalse(exists(b));
assertFalse(exists(c));
assertFalse(exists(d));
}
+ /**
+ * b/199469244. If there are expired items with the same name in the same folder, we need to
+ * extend the expiration time of them successfully.
+ */
+ @Test
+ public void testExtendTrashedItemsHaveSameName() throws Exception {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final ContentResolver resolver = context.getContentResolver();
+ final String displayName = String.valueOf(System.nanoTime());
+ final long dateExpires1 =
+ (System.currentTimeMillis() - 10 * DateUtils.DAY_IN_MILLIS) / 1000;
+ final Uri uri1 = createExpiredTrashedItem(resolver, dateExpires1, displayName);
+ final long dateExpires2 =
+ (System.currentTimeMillis() - 11 * DateUtils.DAY_IN_MILLIS) / 1000;
+ final Uri uri2 = createExpiredTrashedItem(resolver, dateExpires2, displayName);
+ final long dateExpires3 =
+ (System.currentTimeMillis() - 12 * DateUtils.DAY_IN_MILLIS) / 1000;
+ final Uri uri3 = createExpiredTrashedItem(resolver, dateExpires3, displayName);
+
+ MediaStore.runIdleMaintenance(resolver);
+
+ assertExpiredItemIsExtended(resolver, uri1);
+ assertExpiredItemIsExtended(resolver, uri2);
+ assertExpiredItemIsExtended(resolver, uri3);
+ }
+
@Test
public void testExtendTrashedItemExpiresOverOneWeek() throws Exception {
final Context context = InstrumentationRegistry.getTargetContext();
final ContentResolver resolver = context.getContentResolver();
-
- // Create the expired item and scan the file to add it into database
final long dateExpires = (System.currentTimeMillis() - 10 * DateUtils.DAY_IN_MILLIS) / 1000;
- final String expiredFileName = String.format(Locale.US, ".%s-%d-%s",
- FileUtils.PREFIX_TRASHED, dateExpires, System.nanoTime() + ".jpg");
- final File file = MediaScannerTest.stage(R.raw.test_image,
- new File(mDir, expiredFileName));
- final Uri uri = MediaStore.scanFile(resolver, file);
+ final Uri uri = createExpiredTrashedItem(resolver, dateExpires);
- MediaStore.waitForIdle(resolver);
+ MediaStore.runIdleMaintenance(resolver);
- final String[] projection = new String[]{DATE_EXPIRES};
- final Bundle queryArgs = new Bundle();
- queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_TRASHED, MediaStore.MATCH_INCLUDE);
-
- try (Cursor cursor = resolver.query(uri, projection, queryArgs,
- null /* cancellationSignal */)) {
- assertThat(cursor.getCount()).isEqualTo(1);
- }
-
- final long expectedExtendedTimestamp =
- (System.currentTimeMillis() + FileUtils.DEFAULT_DURATION_EXTENDED) / 1000 - 1;
- runIdleMaintenance(resolver);
-
- final long dateExpiresAfter;
- try (Cursor cursor = resolver.query(uri, projection, queryArgs,
- null /* cancellationSignal */)) {
- assertThat(cursor.getCount()).isEqualTo(1);
- cursor.moveToFirst();
- dateExpiresAfter = cursor.getLong(0);
- assertThat(dateExpiresAfter).isGreaterThan(expectedExtendedTimestamp);
- }
+ assertExpiredItemIsExtended(resolver, uri);
}
@Test
public void testDeleteExpiredTrashedItem() throws Exception {
final Context context = InstrumentationRegistry.getTargetContext();
final ContentResolver resolver = context.getContentResolver();
-
// Create the expired item and scan the file to add it into database
final long dateExpires = (System.currentTimeMillis() - 3 * DateUtils.DAY_IN_MILLIS) / 1000;
+ final Uri uri = createExpiredTrashedItem(resolver, dateExpires);
+
+ MediaStore.runIdleMaintenance(resolver);
+
+ final String[] projection = new String[]{DATE_EXPIRES};
+ final Bundle queryArgs = new Bundle();
+ queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_TRASHED, MediaStore.MATCH_INCLUDE);
+ try (Cursor cursor = resolver.query(uri, projection, queryArgs,
+ null /* cancellationSignal */)) {
+ assertThat(cursor.getCount()).isEqualTo(0);
+ }
+ }
+
+ @Test
+ public void testDetectSpecialFormat() throws Exception {
+ // Require isolated resolver to query hidden column _special_format
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final Context isolatedContext = new MediaScannerTest.IsolatedContext(context, "modern",
+ /*asFuseThread*/ false);
+ final ContentResolver resolver = isolatedContext.getContentResolver();
+
+ // Create file such that it is not scanned
+ final File dir = Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOWNLOADS);
+ final String fileName = TAG + System.nanoTime() + ".jpg";
+
+ ContentValues values = new ContentValues();
+ values.put(DISPLAY_NAME, fileName);
+ values.put(RELATIVE_PATH, dir.getName());
+
+ final Uri filesUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL);
+ final Uri uri = resolver.insert(filesUri, values);
+ assertThat(uri).isNotNull();
+
+ final File file = new File(dir, fileName);
+ file.createNewFile();
+
+ try {
+ final String[] projection = new String[]{_SPECIAL_FORMAT, GENERATION_MODIFIED};
+ final int initialGenerationModified;
+ try (Cursor cr = resolver.query(uri, projection, null, null, null)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ assertThat(cr.moveToFirst()).isNotNull();
+ assertThat(cr.isNull(0)).isTrue();
+ initialGenerationModified = cr.getInt(1);
+ }
+
+ // We are not calling runIdleMaintenance as it also scans volumes.
+ try (ContentProviderClient cpc = resolver
+ .acquireContentProviderClient(MediaStore.AUTHORITY)) {
+ ((MediaProvider) cpc.getLocalContentProvider())
+ .detectSpecialFormat(new CancellationSignal());
+ }
+
+ try (Cursor cr = resolver.query(uri, projection, null, null, null)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ assertThat(cr.moveToFirst()).isNotNull();
+ assertThat(cr.getInt(0)).isEqualTo(_SPECIAL_FORMAT_NONE);
+ // Make sure updating special format column updates GENERATION_MODIFIED;
+ // This is essential for picker db to know which rows were modified.
+ assertThat(cr.getInt(1)).isGreaterThan(initialGenerationModified);
+ }
+ } finally {
+ file.delete();
+ }
+ }
+
+ private void assertExpiredItemIsExtended(ContentResolver resolver, Uri uri) throws Exception {
+ final long expectedExtendedTimestamp =
+ (System.currentTimeMillis() + FileUtils.DEFAULT_DURATION_EXTENDED) / 1000 - 1;
+ final String[] projection = new String[]{DATE_EXPIRES};
+ final Bundle queryArgs = new Bundle();
+ queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_TRASHED, MediaStore.MATCH_INCLUDE);
+ try (Cursor cursor = resolver.query(uri, projection, queryArgs,
+ null /* cancellationSignal */)) {
+ assertThat(cursor.getCount()).isEqualTo(1);
+ cursor.moveToFirst();
+ final long dateExpiresAfter = cursor.getLong(0);
+ assertThat(dateExpiresAfter).isGreaterThan(expectedExtendedTimestamp);
+ }
+ }
+
+ private Uri createExpiredTrashedItem(ContentResolver resolver, long dateExpires)
+ throws Exception {
+ return createExpiredTrashedItem(resolver, dateExpires,
+ /* displayName */ String.valueOf(System.nanoTime()));
+ }
+
+ private Uri createExpiredTrashedItem(ContentResolver resolver, long dateExpires,
+ String displayName) throws Exception {
+ // Create the expired item and scan the file to add it into database
final String expiredFileName = String.format(Locale.US, ".%s-%d-%s",
- FileUtils.PREFIX_TRASHED, dateExpires, System.nanoTime() + ".jpg");
- final File file = MediaScannerTest.stage(R.raw.test_image,
- new File(mDir, expiredFileName));
+ FileUtils.PREFIX_TRASHED, dateExpires, displayName + ".jpg");
+ final File file = MediaScannerTest.stage(R.raw.test_image, new File(mDir, expiredFileName));
final Uri uri = MediaStore.scanFile(resolver, file);
MediaStore.waitForIdle(resolver);
@@ -191,28 +289,11 @@
final String[] projection = new String[]{DATE_EXPIRES};
final Bundle queryArgs = new Bundle();
queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_TRASHED, MediaStore.MATCH_INCLUDE);
-
try (Cursor cursor = resolver.query(uri, projection, queryArgs,
null /* cancellationSignal */)) {
assertThat(cursor.getCount()).isEqualTo(1);
}
-
- runIdleMaintenance(resolver);
-
- try (Cursor cursor = resolver.query(uri, projection, queryArgs,
- null /* cancellationSignal */)) {
- assertThat(cursor.getCount()).isEqualTo(0);
- }
- }
-
- private static void runIdleMaintenance(ContentResolver resolver) {
- final UiAutomation ui = InstrumentationRegistry.getInstrumentation().getUiAutomation();
- ui.adoptShellPermissionIdentity(android.Manifest.permission.DUMP);
- try {
- MediaStore.runIdleMaintenance(resolver);
- } finally {
- ui.dropShellPermissionIdentity();
- }
+ return uri;
}
public static File delete(File file) throws IOException {
diff --git a/tests/src/com/android/providers/media/MediaDocumentsProviderTest.java b/tests/src/com/android/providers/media/MediaDocumentsProviderTest.java
index f9754e8..9567758 100644
--- a/tests/src/com/android/providers/media/MediaDocumentsProviderTest.java
+++ b/tests/src/com/android/providers/media/MediaDocumentsProviderTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
import android.Manifest;
import android.content.ContentResolver;
@@ -29,6 +30,7 @@
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
@@ -53,6 +55,7 @@
import org.junit.runner.RunWith;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Arrays;
@@ -129,6 +132,31 @@
}
}
+ @Test
+ public void testOpenFile() throws Exception {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final Context isolatedContext = new IsolatedContext(context, "modern",
+ /*asFuseThread*/ false);
+ final ContentResolver resolver = isolatedContext.getContentResolver();
+
+ // Give ourselves some basic media to work with
+ stageTestMedia(isolatedContext);
+
+ for (String item : new String[] {
+ MediaDocumentsProvider.TYPE_ARTIST,
+ MediaDocumentsProvider.TYPE_ALBUM,
+ MediaDocumentsProvider.TYPE_VIDEOS_BUCKET,
+ MediaDocumentsProvider.TYPE_IMAGES_BUCKET,
+ MediaDocumentsProvider.TYPE_DOCUMENTS_BUCKET,
+ MediaDocumentsProvider.TYPE_AUDIO,
+ MediaDocumentsProvider.TYPE_VIDEO,
+ MediaDocumentsProvider.TYPE_IMAGE,
+ MediaDocumentsProvider.TYPE_DOCUMENT,
+ }) {
+ assertOpenFile(resolver, item);
+ }
+ }
+
/**
* Recursively walk every item published by provider and confirm we can
* query it, open it, and obtain a thumbnail for it.
@@ -262,4 +290,22 @@
final MediaScanner scanner = new ModernMediaScanner(context);
scanner.scanDirectory(dir, REASON_UNKNOWN);
}
+
+ private void assertOpenFile(ContentResolver resolver, String item)
+ throws FileNotFoundException {
+ final Uri.Builder probe = Uri.parse("content://" + MediaDocumentsProvider.AUTHORITY)
+ .buildUpon().appendPath(MediaDocumentsProvider.TYPE_DOCUMENT).appendPath(item);
+ try (Cursor c = resolver.query(probe.build(), null, Bundle.EMPTY, null)) {
+ while (c.moveToNext()) {
+ final Uri uri = DocumentsContract.buildDocumentUri(AUTHORITY,
+ getDocIdForIdent(item, c.getLong(0)));
+ assertNotNull(resolver.openFile(uri, "r", null));
+ assertNotNull(resolver.openFile(uri, "rw", null));
+ }
+ }
+ }
+
+ private static String getDocIdForIdent(String type, long id) {
+ return type + ":" + id;
+ }
}
diff --git a/tests/src/com/android/providers/media/MediaProviderForFuseTest.java b/tests/src/com/android/providers/media/MediaProviderForFuseTest.java
index 7de9834..df9f575 100644
--- a/tests/src/com/android/providers/media/MediaProviderForFuseTest.java
+++ b/tests/src/com/android/providers/media/MediaProviderForFuseTest.java
@@ -16,18 +16,30 @@
package com.android.providers.media;
+import static com.android.providers.media.MediaProvider.DIRECTORY_ACCESS_FOR_READ;
+import static com.android.providers.media.MediaProvider.DIRECTORY_ACCESS_FOR_WRITE;
+import static com.android.providers.media.MediaProvider.DIRECTORY_ACCESS_FOR_CREATE;
+import static com.android.providers.media.MediaProvider.DIRECTORY_ACCESS_FOR_DELETE;
+
import android.Manifest;
+import android.app.UiAutomation;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
+import android.system.OsConstants;
+import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.providers.media.scan.MediaScannerTest.IsolatedContext;
+import com.google.common.io.ByteStreams;
import com.google.common.truth.Truth;
import org.junit.AfterClass;
@@ -36,16 +48,20 @@
import org.junit.runner.RunWith;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InterruptedIOException;
import java.util.Arrays;
/**
- * This class is purely here to convince internal code coverage tools that
- * {@code FuseDaemonHostTest} is actually covering all of these methods; the
- * current coverage infrastructure doesn't support host tests yet.
+ * Unit tests for {@link MediaProvider} forFuse methods. {@code CtsScopedStorageHostTest} (and
+ * similar) are the host tests for these scenarios.
*/
@RunWith(AndroidJUnit4.class)
public class MediaProviderForFuseTest {
+ private static final String TAG = "MediaProviderForFuseTest";
+
private static Context sIsolatedContext;
private static ContentResolver sIsolatedResolver;
private static MediaProvider sMediaProvider;
@@ -97,7 +113,10 @@
// We can write our file
FileOpenResult result = sMediaProvider.onFileOpenForFuse(
- file.getPath(), file.getPath(), sTestUid, 0 /* tid */, 0 /* transforms_reason */,
+ file.getPath(),
+ file.getPath(),
+ sTestUid,
+ 0 /* tid */, 0 /* transforms_reason */,
true /* forWrite */, false /* redact */, false /* transcode_metrics */);
Truth.assertThat(result.status).isEqualTo(0);
Truth.assertThat(result.redactionRanges).isEqualTo(new long[0]);
@@ -120,32 +139,174 @@
@Test
public void testRenameDirectory() throws Exception {
- sTestDir = new File(sTestDir, "subdir" + System.nanoTime());
- sTestDir.mkdirs();
+ File file = createSubdirWithOneFile(sTestDir);
+ File oldDir = file.getParentFile();
- // Create test directory and file
- final File file = new File(sTestDir, "test" + System.nanoTime() + ".jpg");
+ // Rename directory should bring along files
+ final File renamedDir = new File(sTestDir, "renamed" + System.nanoTime());
+ Truth.assertThat(sMediaProvider.renameForFuse(
+ oldDir.getPath(), renamedDir.getPath(), sTestUid)).isEqualTo(0);
+ Truth.assertThat(Arrays.asList(sMediaProvider.getFilesInDirectoryForFuse(
+ renamedDir.getPath(), sTestUid))).contains(file.getName());
+
+ // Querying renamed dir shows the file inside
+ final Bundle queryArgs = queryArgsForDirContents(renamedDir);
+ try (Cursor cursor = sIsolatedResolver
+ .query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, queryArgs, null)) {
+ Truth.assertThat(cursor.getCount()).isEqualTo(1);
+ }
+ }
+
+ @Test
+ public void testRenameDirectory_WhenParentDirectoryIsHidden() throws Exception {
+ // Create parent dir with nomedia file
+ final File parent = new File(sTestDir, "hidden" + System.nanoTime());
+ parent.mkdirs();
+ createNomediaFile(parent);
+ // Create dir in hidden parent dir
+ File file = createSubdirWithOneFile(parent);
+ File oldDir = file.getParentFile();
+
+ // Rename dir within hidden parent.
+ final File renamedDir = new File(parent, "renamed" + System.nanoTime());
+ Truth.assertThat(sMediaProvider.renameForFuse(
+ oldDir.getPath(), renamedDir.getPath(), sTestUid)).isEqualTo(0);
+
+ // Files should be in renamed dir.
+ Truth.assertThat(Arrays.asList(sMediaProvider.getFilesInDirectoryForFuse(
+ renamedDir.getPath(), sTestUid))).contains(file.getName());
+
+ // Querying renamed dir doesn't show the file inside (because parent is hidden)
+ final Bundle queryArgs = queryArgsForDirContents(renamedDir);
+ try (Cursor cursor = sIsolatedResolver
+ .query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, queryArgs, null)) {
+ Truth.assertThat(cursor.getCount()).isEqualTo(0);
+ }
+ }
+
+ private @NonNull File createNomediaFile(@NonNull File dir) throws IOException {
+ final File nomediaFile = new File(dir, ".nomedia");
+ executeShellCommand("touch " + nomediaFile.getAbsolutePath());
+ Truth.assertWithMessage("cannot create nomedia file: " + nomediaFile.getAbsolutePath())
+ .that(nomediaFile.exists())
+ .isTrue();
+ return nomediaFile;
+ }
+
+ private Bundle queryArgsForDirContents(File renamedDir) {
+ final Bundle queryArgs = new Bundle();
+ queryArgs.putString(ContentResolver.QUERY_ARG_SQL_SELECTION, "_data like ?");
+ queryArgs.putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS,
+ new String[]{renamedDir.getPath() + "/%"});
+ return queryArgs;
+ }
+
+ /**
+ * Executes a shell command.
+ */
+ private static String executeShellCommand(String command) throws IOException {
+ int attempt = 0;
+ while (attempt++ < 5) {
+ try {
+ return executeShellCommandInternal(command);
+ } catch (InterruptedIOException e) {
+ Log.v(TAG, "Trouble executing " + command + "; trying again", e);
+ }
+ }
+ throw new IOException("Failed to execute " + command);
+ }
+
+ private static String executeShellCommandInternal(String cmd) throws IOException {
+ UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ try (FileInputStream output = new FileInputStream(
+ uiAutomation.executeShellCommand(cmd).getFileDescriptor())) {
+ return new String(ByteStreams.toByteArray(output));
+ }
+ }
+
+ private File createSubdirWithOneFile(@NonNull File parent) throws Exception {
+ final File subDir = new File(parent, "subdir" + System.nanoTime());
+ subDir.mkdirs();
+
+ final File file = new File(subDir, "test" + System.nanoTime() + ".jpg");
Truth.assertThat(sMediaProvider.insertFileIfNecessaryForFuse(
file.getPath(), sTestUid)).isEqualTo(0);
Truth.assertThat(file.createNewFile()).isTrue();
- // Rename directory should bring along files
- final File renamed = new File(sTestDir.getParentFile(), "renamed" + System.nanoTime());
- Truth.assertThat(sMediaProvider.renameForFuse(
- sTestDir.getPath(), renamed.getPath(), sTestUid)).isEqualTo(0);
- Truth.assertThat(Arrays.asList(sMediaProvider.getFilesInDirectoryForFuse(
- renamed.getPath(), sTestUid))).contains(file.getName());
+ return file;
}
@Test
- public void test_isOpendirAllowedForFuse() throws Exception {
- Truth.assertThat(sMediaProvider.isOpendirAllowedForFuse(
- sTestDir.getPath(), sTestUid, /* forWrite */ false)).isEqualTo(0);
- }
+ public void test_isDirAccessAllowedForFuse() throws Exception {
+ //verify can create and write but not delete top-level default folder
+ final File topLevelDefaultDir = Environment.buildExternalStoragePublicDirs(
+ Environment.DIRECTORY_PICTURES)[0];
+ final String topLevelDefaultDirPath = topLevelDefaultDir.getPath();
+ Truth.assertThat(sMediaProvider.isDirAccessAllowedForFuse(
+ topLevelDefaultDirPath, sTestUid,
+ DIRECTORY_ACCESS_FOR_READ)).isEqualTo(0);
+ Truth.assertThat(sMediaProvider.isDirAccessAllowedForFuse(
+ topLevelDefaultDirPath, sTestUid,
+ DIRECTORY_ACCESS_FOR_CREATE)).isEqualTo(0);
+ Truth.assertThat(sMediaProvider.isDirAccessAllowedForFuse(
+ topLevelDefaultDirPath, sTestUid,
+ DIRECTORY_ACCESS_FOR_WRITE)).isEqualTo(0);
+ Truth.assertThat(sMediaProvider.isDirAccessAllowedForFuse(
+ topLevelDefaultDirPath, sTestUid,
+ DIRECTORY_ACCESS_FOR_DELETE)).isEqualTo(
+ OsConstants.EACCES);
- @Test
- public void test_isDirectoryCreationOrDeletionAllowedForFuse() throws Exception {
- Truth.assertThat(sMediaProvider.isDirectoryCreationOrDeletionAllowedForFuse(
- sTestDir.getPath(), sTestUid, true)).isEqualTo(0);
+ //verify cannot create or write top-level non-default folder, but can read it
+ final File topLevelNonDefaultDir = Environment.buildExternalStoragePublicDirs(
+ "non-default-dir")[0];
+ final String topLevelNonDefaultDirPath = topLevelNonDefaultDir.getPath();
+ Truth.assertThat(sMediaProvider.isDirAccessAllowedForFuse(
+ topLevelNonDefaultDirPath, sTestUid,
+ DIRECTORY_ACCESS_FOR_READ)).isEqualTo(0);
+ Truth.assertThat(sMediaProvider.isDirAccessAllowedForFuse(
+ topLevelNonDefaultDirPath, sTestUid,
+ DIRECTORY_ACCESS_FOR_CREATE)).isEqualTo(
+ OsConstants.EACCES);
+ Truth.assertThat(sMediaProvider.isDirAccessAllowedForFuse(
+ topLevelNonDefaultDirPath, sTestUid,
+ DIRECTORY_ACCESS_FOR_WRITE)).isEqualTo(OsConstants.EACCES);
+ Truth.assertThat(sMediaProvider.isDirAccessAllowedForFuse(
+ topLevelNonDefaultDirPath, sTestUid,
+ DIRECTORY_ACCESS_FOR_DELETE)).isEqualTo(OsConstants.EACCES);
+
+ //verify can read, create, write and delete random non-top-level folder
+ final File lowerLevelNonDefaultDir = new File(topLevelDefaultDir,
+ "subdir" + System.nanoTime());
+ lowerLevelNonDefaultDir.mkdirs();
+ final String lowerLevelNonDefaultDirPath = lowerLevelNonDefaultDir.getPath();
+ Truth.assertThat(sMediaProvider.isDirAccessAllowedForFuse(
+ lowerLevelNonDefaultDirPath, sTestUid,
+ DIRECTORY_ACCESS_FOR_READ)).isEqualTo(0);
+ Truth.assertThat(sMediaProvider.isDirAccessAllowedForFuse(
+ lowerLevelNonDefaultDirPath, sTestUid,
+ DIRECTORY_ACCESS_FOR_CREATE)).isEqualTo(0);
+ Truth.assertThat(sMediaProvider.isDirAccessAllowedForFuse(
+ lowerLevelNonDefaultDirPath, sTestUid,
+ DIRECTORY_ACCESS_FOR_WRITE)).isEqualTo(0);
+ Truth.assertThat(sMediaProvider.isDirAccessAllowedForFuse(
+ lowerLevelNonDefaultDirPath, sTestUid,
+ DIRECTORY_ACCESS_FOR_DELETE)).isEqualTo(0);
+
+ //verify cannot update outside /storage folder
+ final File rootDir = new File("/myfolder");
+ final String rootDirPath = rootDir.getPath();
+ Truth.assertThat(sMediaProvider.isDirAccessAllowedForFuse(
+ rootDirPath, sTestUid,
+ DIRECTORY_ACCESS_FOR_READ)).isEqualTo(0);
+ Truth.assertThat(sMediaProvider.isDirAccessAllowedForFuse(
+ rootDirPath, sTestUid,
+ DIRECTORY_ACCESS_FOR_CREATE)).isEqualTo(OsConstants.EPERM);
+ Truth.assertThat(sMediaProvider.isDirAccessAllowedForFuse(
+ rootDirPath, sTestUid,
+ DIRECTORY_ACCESS_FOR_WRITE)).isEqualTo(OsConstants.EPERM);
+ Truth.assertThat(sMediaProvider.isDirAccessAllowedForFuse(
+ rootDirPath, sTestUid,
+ DIRECTORY_ACCESS_FOR_DELETE)).isEqualTo(OsConstants.EPERM);
+
}
}
diff --git a/tests/src/com/android/providers/media/MediaProviderTest.java b/tests/src/com/android/providers/media/MediaProviderTest.java
index 11fc327..fc30292 100644
--- a/tests/src/com/android/providers/media/MediaProviderTest.java
+++ b/tests/src/com/android/providers/media/MediaProviderTest.java
@@ -45,7 +45,9 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
+import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -57,10 +59,14 @@
import android.provider.MediaStore.Files.FileColumns;
import android.provider.MediaStore.Images.ImageColumns;
import android.provider.MediaStore.MediaColumns;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
import android.util.ArrayMap;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SdkSuppress;
import androidx.test.runner.AndroidJUnit4;
import com.android.providers.media.MediaProvider.FallbackException;
@@ -81,8 +87,9 @@
import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.IOException;
import java.io.PrintWriter;
-import java.sql.Array;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -99,6 +106,7 @@
static final String PERMISSIONLESS_APP = "com.android.providers.media.testapp.withoutperms";
private static Context sIsolatedContext;
+ private static Context sContext;
private static ContentResolver sIsolatedResolver;
@BeforeClass
@@ -109,9 +117,7 @@
Manifest.permission.READ_DEVICE_CONFIG,
Manifest.permission.INTERACT_ACROSS_USERS);
- final Context context = InstrumentationRegistry.getTargetContext();
- sIsolatedContext = new IsolatedContext(context, "modern", /*asFuseThread*/ false);
- sIsolatedResolver = sIsolatedContext.getContentResolver();
+ resetIsolatedContext();
}
@AfterClass
@@ -254,9 +260,7 @@
@Test
public void testCanonicalize() throws Exception {
// We might have old files lurking, so force a clean slate
- final Context context = InstrumentationRegistry.getTargetContext();
- sIsolatedContext = new IsolatedContext(context, "modern", /*asFuseThread*/ false);
- sIsolatedResolver = sIsolatedContext.getContentResolver();
+ resetIsolatedContext();
final File dir = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
@@ -333,9 +337,7 @@
private void testActionLongFileNameItemHasTrimmedFileName(String columnKey) throws Exception {
// We might have old files lurking, so force a clean slate
- final Context context = InstrumentationRegistry.getTargetContext();
- sIsolatedContext = new IsolatedContext(context, "modern", /*asFuseThread*/ false);
- sIsolatedResolver = sIsolatedContext.getContentResolver();
+ resetIsolatedContext();
final String[] projection = new String[]{MediaColumns.DATA};
final File dir = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
@@ -571,6 +573,24 @@
}
@Test
+ public void testSpecialFormatDefaultValue() throws Exception {
+ final Uri uri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
+ final ContentValues values = new ContentValues();
+ values.put(MediaColumns.DISPLAY_NAME, "test_specialFormat");
+ values.put(MediaColumns.MIME_TYPE, "image/png");
+ Uri result = sIsolatedResolver.insert(uri, values);
+ try (Cursor c = sIsolatedResolver.query(result,
+ new String[]{MediaColumns.DISPLAY_NAME, FileColumns._SPECIAL_FORMAT},
+ null, null)) {
+ assertNotNull(c);
+ assertEquals(1, c.getCount());
+ assertTrue(c.moveToFirst());
+ assertEquals("test_specialFormat.png", c.getString(0));
+ assertEquals(FileColumns._SPECIAL_FORMAT_NONE, c.getInt(1));
+ }
+ }
+
+ @Test
public void testBuildData_Primary() throws Exception {
final Uri uri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
assertEndsWith("/DCIM/IMG_1024.JPG",
@@ -1255,9 +1275,8 @@
private void testQueryAudioViewsNoItemWithColumn(String columnKey) throws Exception {
// We might have old files lurking, so force a clean slate
- final Context context = InstrumentationRegistry.getTargetContext();
- sIsolatedContext = new IsolatedContext(context, "modern", /*asFuseThread*/ false);
- sIsolatedResolver = sIsolatedContext.getContentResolver();
+ resetIsolatedContext();
+
final File dir = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
@@ -1281,20 +1300,127 @@
Uri result = sIsolatedResolver.insert(audioUri, values);
+ final long genreId;
// Check the audio file is inserted correctly
try (Cursor c = sIsolatedResolver.query(result,
- new String[]{MediaColumns.DISPLAY_NAME, columnKey}, null, null)) {
+ new String[]{MediaColumns.DISPLAY_NAME, AudioColumns.GENRE_ID, columnKey},
+ null, null)) {
assertNotNull(c);
assertEquals(1, c.getCount());
assertTrue(c.moveToFirst());
assertEquals(displayName, c.getString(0));
- assertEquals(1, c.getInt(1));
+ assertEquals(1, c.getInt(2));
+ genreId = c.getLong(1);
}
final String volume = MediaStore.VOLUME_EXTERNAL_PRIMARY;
assertQueryResultNoItems(MediaStore.Audio.Albums.getContentUri(volume));
assertQueryResultNoItems(MediaStore.Audio.Artists.getContentUri(volume));
assertQueryResultNoItems(MediaStore.Audio.Genres.getContentUri(volume));
+ assertQueryResultNoItems(MediaStore.Audio.Genres.Members.getContentUri(volume, genreId));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R, maxSdkVersion = Build.VERSION_CODES.R)
+ @Ignore("b/211068960")
+ public void testQueryAudioTableNoIsRecordingColumnInR() throws Exception {
+ final File file = createAudioRecordingFile();
+ final Uri audioUri =
+ MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
+
+ try (Cursor c = sIsolatedResolver.query(audioUri, null, null, null, null)) {
+ assertThat(c).isNotNull();
+ assertThat(c.getCount()).isEqualTo(1);
+ assertThat(c.getColumnIndex("is_recording")).isEqualTo(-1);
+ } finally {
+ file.delete();
+ final File dir = file.getParentFile();
+ dir.delete();
+ }
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R, maxSdkVersion = Build.VERSION_CODES.R)
+ @Ignore("b/211068960")
+ public void testQueryIsRecordingInAudioTableExceptionInR() throws Exception {
+ final File file = createAudioRecordingFile();
+ final Uri audioUri =
+ MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
+ final String[] projection = new String[]{"is_recording"};
+
+ try (Cursor c = sIsolatedResolver.query(audioUri, projection, null, null, null)) {
+ fail("Expected exception with the is_recording is not a column in Audio table");
+ } catch (IllegalArgumentException | SQLiteException expected) {
+ } finally {
+ file.delete();
+ final File dir = file.getParentFile();
+ dir.delete();
+ }
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
+ public void testQueryAudioTableHasIsRecordingColumnAfterR() throws Exception {
+ final File file = createAudioRecordingFile();
+ final Uri audioUri =
+ MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
+
+ try (Cursor c = sIsolatedResolver.query(audioUri, null, null, null, null)) {
+ assertThat(c).isNotNull();
+ assertThat(c.getCount()).isEqualTo(1);
+ final int columnIndex = c.getColumnIndex(AudioColumns.IS_RECORDING);
+ assertThat(columnIndex).isNotEqualTo(-1);
+ assertThat(c.moveToFirst()).isTrue();
+ assertThat(c.getInt(columnIndex)).isEqualTo(1);
+ } finally {
+ file.delete();
+ final File dir = file.getParentFile();
+ dir.delete();
+ }
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
+ public void testQueryIsRecordingInAudioTableAfterR() throws Exception {
+ final File file = createAudioRecordingFile();
+ final Uri audioUri =
+ MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
+ final String[] projection = new String[]{AudioColumns.IS_RECORDING};
+
+ try (Cursor c = sIsolatedResolver.query(audioUri, projection, null, null, null)) {
+ assertThat(c).isNotNull();
+ assertThat(c.getCount()).isEqualTo(1);
+ assertThat(c.moveToFirst()).isTrue();
+ assertThat(c.getInt(0)).isEqualTo(1);
+ } finally {
+ file.delete();
+ final File dir = file.getParentFile();
+ dir.delete();
+ }
+ }
+
+ private File createAudioRecordingFile() throws Exception {
+ // We might have old files lurking, so force a clean slate
+ resetIsolatedContext();
+ final File dir = Environment
+ .getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
+ final File recordingDir = new File(dir, "Recordings");
+ recordingDir.mkdirs();
+ final String displayName = "test" + System.nanoTime() + ".mp3";
+ final File audio = new File(recordingDir, displayName);
+ stage(R.raw.test_audio, audio);
+ final Uri result = MediaStore.scanFile(sIsolatedResolver, audio);
+
+ // Check the audio music file exists
+ try (Cursor c = sIsolatedResolver.query(result,
+ new String[]{MediaColumns.DISPLAY_NAME, AudioColumns.IS_MUSIC}, null, null)) {
+ assertThat(c).isNotNull();
+ assertThat(c.getCount()).isEqualTo(1);
+ assertThat(c.moveToFirst()).isTrue();
+ assertThat(c.getString(0)).isEqualTo(displayName);
+ assertThat(c.getInt(1)).isEqualTo(0);
+ }
+ return audio;
}
private static void assertQueryResultNoItems(Uri uri) throws Exception {
@@ -1477,6 +1603,31 @@
testRedactionForFileExtension(R.raw.lg_g4_iso_800_jpg, ".jpg");
}
+ @Test
+ public void testOpenTypedAssetFile_setModeInBundle_failsWrite() throws IOException {
+ final File dir = Environment
+ .getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
+ final File file = new File(dir, "test" + System.nanoTime() + ".txt");
+ stage(R.raw.test_txt, file);
+ Uri mediaUri = MediaStore.scanFile(sContext.getContentResolver(), file);
+ Bundle opts = new Bundle();
+ opts.putString(MediaStore.EXTRA_MODE, "w");
+
+ try (AssetFileDescriptor afd = sContext.getContentResolver().openTypedAssetFile(mediaUri,
+ "*/*", opts, null)) {
+ String rawText = "Hello";
+ Os.write(afd.getFileDescriptor(), rawText.getBytes(StandardCharsets.UTF_8),
+ 0, rawText.length());
+ fail("Expected failure in write to fail with ErrnoException.");
+ } catch (ErrnoException expected) {
+ // Expecting ErrnoException: Bad File Descriptor. Mode set in bundle would not be
+ // respected if calling app is not MediaProvider itself.
+ assertThat(expected.errno).isEqualTo(OsConstants.EBADF);
+ } finally {
+ file.delete();
+ }
+ }
+
private void testRedactionForFileExtension(int resId, String extension) throws Exception {
final File dir = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
@@ -1506,4 +1657,16 @@
file.delete();
}
}
+
+ private static void resetIsolatedContext() {
+ if (sIsolatedResolver != null) {
+ // This is necessary, we wait for all unfinished tasks to finish before we create a
+ // new IsolatedContext.
+ MediaStore.waitForIdle(sIsolatedResolver);
+ }
+
+ sContext = InstrumentationRegistry.getTargetContext();
+ sIsolatedContext = new IsolatedContext(sContext, "modern", /*asFuseThread*/ false);
+ sIsolatedResolver = sIsolatedContext.getContentResolver();
+ }
}
diff --git a/tests/src/com/android/providers/media/PermissionActivityTest.java b/tests/src/com/android/providers/media/PermissionActivityTest.java
index 6eb738e..281e93a 100644
--- a/tests/src/com/android/providers/media/PermissionActivityTest.java
+++ b/tests/src/com/android/providers/media/PermissionActivityTest.java
@@ -21,6 +21,9 @@
import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE;
import static android.Manifest.permission.MANAGE_MEDIA;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.Manifest.permission.READ_MEDIA_AUDIO;
+import static android.Manifest.permission.READ_MEDIA_IMAGES;
+import static android.Manifest.permission.READ_MEDIA_VIDEO;
import static android.Manifest.permission.UPDATE_APP_OPS_STATS;
import static androidx.test.InstrumentationRegistry.getContext;
@@ -33,7 +36,10 @@
import static com.android.providers.media.util.PermissionUtils.checkPermissionAccessMediaLocation;
import static com.android.providers.media.util.PermissionUtils.checkPermissionManageMedia;
import static com.android.providers.media.util.PermissionUtils.checkPermissionManager;
+import static com.android.providers.media.util.PermissionUtils.checkPermissionReadAudio;
+import static com.android.providers.media.util.PermissionUtils.checkPermissionReadImages;
import static com.android.providers.media.util.PermissionUtils.checkPermissionReadStorage;
+import static com.android.providers.media.util.PermissionUtils.checkPermissionReadVideo;
import static com.android.providers.media.util.TestUtils.adoptShellPermission;
import static com.android.providers.media.util.TestUtils.dropShellPermission;
@@ -53,6 +59,7 @@
import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SdkSuppress;
+import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
import com.android.providers.media.scan.MediaScannerTest;
@@ -74,6 +81,8 @@
public class PermissionActivityTest {
private static final String TEST_APP_PACKAGE_NAME =
"com.android.providers.media.testapp.permission";
+ private static final String TEST_APP_33_PACKAGE_NAME =
+ "com.android.providers.media.testapp.permissionmedia";
private static final String OP_ACCESS_MEDIA_LOCATION =
AppOpsManager.permissionToOp(ACCESS_MEDIA_LOCATION);
@@ -83,6 +92,12 @@
AppOpsManager.permissionToOp(MANAGE_EXTERNAL_STORAGE);
private static final String OP_READ_EXTERNAL_STORAGE =
AppOpsManager.permissionToOp(READ_EXTERNAL_STORAGE);
+ private static final String OP_READ_MEDIA_IMAGES =
+ AppOpsManager.permissionToOp(READ_MEDIA_IMAGES);
+ private static final String OP_READ_MEDIA_AUDIO =
+ AppOpsManager.permissionToOp(READ_MEDIA_AUDIO);
+ private static final String OP_READ_MEDIA_VIDEO =
+ AppOpsManager.permissionToOp(READ_MEDIA_VIDEO);
// The list is used to restore the permissions after the test is finished.
// The default value for these app ops is {@link AppOpsManager#MODE_DEFAULT}
@@ -103,10 +118,12 @@
private static final int TEST_APP_PID = -1;
private int mTestAppUid = -1;
+ private int mTestAppUid33 = -1;
@Before
public void setUp() throws Exception {
mTestAppUid = getContext().getPackageManager().getPackageUid(TEST_APP_PACKAGE_NAME, 0);
+ mTestAppUid33 = getContext().getPackageManager().getPackageUid(TEST_APP_33_PACKAGE_NAME, 0);
}
@Test
@@ -120,6 +137,13 @@
}
@Test
+ public void testLaunchWithNoCallerInfoNoCrash() throws Exception {
+ ActivityTestRule<PermissionActivity> activityTestRule = new ActivityTestRule<>(
+ PermissionActivity.class, /* initialTouchMode */ true, /* launchActivity */ false);
+ activityTestRule.launchActivity(new Intent());
+ }
+
+ @Test
public void testShouldShowActionDialog_favorite_false() throws Exception {
assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid,
TEST_APP_PACKAGE_NAME, null, VERB_FAVORITE)).isFalse();
@@ -139,7 +163,8 @@
adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
try {
- setupPermissions(mTestAppUid, enableAppOpsList, disableAppOpsList);
+ setupPermissions(
+ mTestAppUid, enableAppOpsList, disableAppOpsList, TEST_APP_PACKAGE_NAME);
assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid,
TEST_APP_PACKAGE_NAME, null, VERB_TRASH)).isTrue();
@@ -150,6 +175,103 @@
}
@Test
+ @SdkSuppress(minSdkVersion = 33, codeName = "T")
+ public void testShouldShowActionDialog_noRMAAndMES_true_33() throws Exception {
+ final String[] enableAppOpsList =
+ {OP_MANAGE_MEDIA, OP_READ_MEDIA_IMAGES, OP_READ_MEDIA_VIDEO};
+ final String[] disableAppOpsList = {OP_MANAGE_EXTERNAL_STORAGE, OP_READ_MEDIA_AUDIO};
+ adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
+
+ try {
+ setupPermissions(
+ mTestAppUid33, enableAppOpsList, disableAppOpsList, TEST_APP_33_PACKAGE_NAME);
+
+ assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid33,
+ TEST_APP_33_PACKAGE_NAME, null, VERB_TRASH,
+ /* shouldCheckMediaPermissions */ true, /* shouldCheckReadAudio */ true,
+ /* shouldCheckReadImages */ false, /* shouldCheckReadVideo */ false,
+ /* mShouldCheckReadAudioOrReadVideo */ false,
+ /* isTargetSdkAtLeastT */ true)).isTrue();
+ } finally {
+ restoreDefaultAppOpPermissions(mTestAppUid33);
+ dropShellPermission();
+ }
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 33, codeName = "T")
+ public void testShouldShowActionDialog_noRMIAndMES_true_33() throws Exception {
+ final String[] enableAppOpsList =
+ {OP_MANAGE_MEDIA, OP_READ_MEDIA_AUDIO, OP_READ_MEDIA_VIDEO};
+ final String[] disableAppOpsList = {OP_MANAGE_EXTERNAL_STORAGE, OP_READ_MEDIA_IMAGES};
+ adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
+
+ try {
+ setupPermissions(
+ mTestAppUid33, enableAppOpsList, disableAppOpsList, TEST_APP_33_PACKAGE_NAME);
+
+ assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid33,
+ TEST_APP_33_PACKAGE_NAME, null, VERB_TRASH,
+ /* shouldCheckMediaPermissions */ true, /* shouldCheckReadAudio */ false,
+ /* shouldCheckReadImages */ true, /* shouldCheckReadVideo */ false,
+ /* mShouldCheckReadAudioOrReadVideo */ false,
+ /* isTargetSdkAtLeastT */ true)).isTrue();
+ } finally {
+ restoreDefaultAppOpPermissions(mTestAppUid33);
+ dropShellPermission();
+ }
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 33, codeName = "T")
+ public void testShouldShowActionDialog_noRMVAndMES_true_33() throws Exception {
+ final String[] enableAppOpsList =
+ {OP_MANAGE_MEDIA, OP_READ_MEDIA_AUDIO, OP_READ_MEDIA_IMAGES};
+ final String[] disableAppOpsList = {OP_MANAGE_EXTERNAL_STORAGE, OP_READ_MEDIA_VIDEO};
+ adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
+
+ try {
+ setupPermissions(
+ mTestAppUid33, enableAppOpsList, disableAppOpsList, TEST_APP_33_PACKAGE_NAME);
+
+ assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid33,
+ TEST_APP_33_PACKAGE_NAME, null, VERB_TRASH,
+ /* shouldCheckMediaPermissions */ true, /* shouldCheckReadAudio */ false,
+ /* shouldCheckReadImages */ false, /* shouldCheckReadVideo */ true,
+ /* mShouldCheckReadAudioOrReadVideo */ false,
+ /* isTargetSdkAtLeastT */ true)).isTrue();
+ } finally {
+ restoreDefaultAppOpPermissions(mTestAppUid33);
+ dropShellPermission();
+ }
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 33, codeName = "T")
+ public void testShouldShowActionDialogForSubtitle_noRMARMVAndMES_true_33() throws Exception {
+ final String[] enableAppOpsList =
+ {OP_MANAGE_MEDIA, OP_READ_MEDIA_IMAGES};
+ final String[] disableAppOpsList =
+ {OP_MANAGE_EXTERNAL_STORAGE, OP_READ_MEDIA_AUDIO, OP_READ_MEDIA_VIDEO};
+ adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
+
+ try {
+ setupPermissions(
+ mTestAppUid33, enableAppOpsList, disableAppOpsList, TEST_APP_33_PACKAGE_NAME);
+
+ assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid33,
+ TEST_APP_33_PACKAGE_NAME, null, VERB_TRASH,
+ /* shouldCheckMediaPermissions */ true, /* shouldCheckReadAudio */ false,
+ /* shouldCheckReadImages */ false, /* shouldCheckReadVideo */ false,
+ /* mShouldCheckReadAudioOrReadVideo */ true,
+ /* isTargetSdkAtLeastT */ true)).isTrue();
+ } finally {
+ restoreDefaultAppOpPermissions(mTestAppUid33);
+ dropShellPermission();
+ }
+ }
+
+ @Test
@SdkSuppress(minSdkVersion = 31, codeName = "S")
public void testShouldShowActionDialog_noMANAGE_MEDIA_true() throws Exception {
final String[] enableAppOpsList = {OP_MANAGE_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE};
@@ -157,7 +279,8 @@
adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
try {
- setupPermissions(mTestAppUid, enableAppOpsList, disableAppOpsList);
+ setupPermissions(
+ mTestAppUid, enableAppOpsList, disableAppOpsList, TEST_APP_PACKAGE_NAME);
assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid,
TEST_APP_PACKAGE_NAME, null, VERB_TRASH)).isTrue();
@@ -168,14 +291,43 @@
}
@Test
+ @SdkSuppress(minSdkVersion = 33, codeName = "T")
+ public void testShouldShowActionDialog_noMANAGE_MEDIA_true_33() throws Exception {
+ final String[] enableAppOpsList = {
+ OP_MANAGE_EXTERNAL_STORAGE,
+ OP_READ_MEDIA_AUDIO,
+ OP_READ_MEDIA_VIDEO,
+ OP_READ_MEDIA_IMAGES
+ };
+ final String[] disableAppOpsList = {OP_MANAGE_MEDIA};
+ adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
+
+ try {
+ setupPermissions(
+ mTestAppUid33, enableAppOpsList, disableAppOpsList, TEST_APP_33_PACKAGE_NAME);
+
+ assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid33,
+ TEST_APP_33_PACKAGE_NAME, null, VERB_TRASH,
+ /* shouldCheckMediaPermissions */ true, /* shouldCheckReadAudio */ true,
+ /* shouldCheckReadImages */ true, /* shouldCheckReadVideo */ true,
+ /* mShouldCheckReadAudioOrReadVideo */ true,
+ /* isTargetSdkAtLeastT */ true)).isTrue();
+ } finally {
+ restoreDefaultAppOpPermissions(mTestAppUid33);
+ dropShellPermission();
+ }
+ }
+
+ @Test
@SdkSuppress(minSdkVersion = 31, codeName = "S")
- public void testShouldShowActionDialog_hasPermissionWithRES_false() throws Exception {
+ public void testShouldShowActionDialog_hasMMWithRES_false() throws Exception {
final String[] enableAppOpsList = {OP_MANAGE_MEDIA, OP_READ_EXTERNAL_STORAGE};
final String[] disableAppOpsList = {OP_MANAGE_EXTERNAL_STORAGE};
adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
try {
- setupPermissions(mTestAppUid, enableAppOpsList, disableAppOpsList);
+ setupPermissions(
+ mTestAppUid, enableAppOpsList, disableAppOpsList, TEST_APP_PACKAGE_NAME);
assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid,
TEST_APP_PACKAGE_NAME, null, VERB_TRASH)).isFalse();
@@ -186,14 +338,40 @@
}
@Test
+ @SdkSuppress(minSdkVersion = 33, codeName = "T")
+ public void testShouldShowActionDialog_hasMMWithRM_false_33() throws Exception {
+ final String[] enableAppOpsList = {
+ OP_MANAGE_MEDIA, OP_READ_MEDIA_AUDIO, OP_READ_MEDIA_VIDEO, OP_READ_MEDIA_IMAGES
+ };
+ final String[] disableAppOpsList = {OP_MANAGE_EXTERNAL_STORAGE};
+ adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
+
+ try {
+ setupPermissions(
+ mTestAppUid33, enableAppOpsList, disableAppOpsList, TEST_APP_33_PACKAGE_NAME);
+
+ assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid33,
+ TEST_APP_33_PACKAGE_NAME, null, VERB_TRASH,
+ /* shouldCheckMediaPermissions */ true, /* shouldCheckReadAudio */ true,
+ /* shouldCheckReadImages */ true, /* shouldCheckReadVideo */ true,
+ /* mShouldCheckReadAudioOrReadVideo */ true,
+ /* isTargetSdkAtLeastT */ true)).isFalse();
+ } finally {
+ restoreDefaultAppOpPermissions(mTestAppUid33);
+ dropShellPermission();
+ }
+ }
+
+ @Test
@SdkSuppress(minSdkVersion = 31, codeName = "S")
- public void testShouldShowActionDialog_hasPermissionWithMES_false() throws Exception {
+ public void testShouldShowActionDialog_hasMMWithMES_false() throws Exception {
final String[] enableAppOpsList = {OP_MANAGE_EXTERNAL_STORAGE, OP_MANAGE_MEDIA};
final String[] disableAppOpsList = {OP_READ_EXTERNAL_STORAGE};
adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
try {
- setupPermissions(mTestAppUid, enableAppOpsList, disableAppOpsList);
+ setupPermissions(
+ mTestAppUid, enableAppOpsList, disableAppOpsList, TEST_APP_PACKAGE_NAME);
assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid,
TEST_APP_PACKAGE_NAME, null, VERB_TRASH)).isFalse();
@@ -204,6 +382,32 @@
}
@Test
+ @SdkSuppress(minSdkVersion = 33, codeName = "T")
+ public void testShouldShowActionDialog_hasMMWithMES_false_33() throws Exception {
+ final String[] enableAppOpsList = {OP_MANAGE_EXTERNAL_STORAGE, OP_MANAGE_MEDIA};
+ final String[] disableAppOpsList = {
+ OP_READ_MEDIA_AUDIO, OP_READ_MEDIA_VIDEO, OP_READ_MEDIA_IMAGES
+ };
+
+ adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
+
+ try {
+ setupPermissions(
+ mTestAppUid33, enableAppOpsList, disableAppOpsList, TEST_APP_33_PACKAGE_NAME);
+
+ assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid33,
+ TEST_APP_33_PACKAGE_NAME, null, VERB_TRASH,
+ /* shouldCheckMediaPermissions */ true, /* shouldCheckReadAudio */ true,
+ /* shouldCheckReadImages */ true, /* shouldCheckReadVideo */ true,
+ /* mShouldCheckReadAudioOrReadVideo */ true,
+ /* isTargetSdkAtLeastT */ true)).isFalse();
+ } finally {
+ restoreDefaultAppOpPermissions(mTestAppUid33);
+ dropShellPermission();
+ }
+ }
+
+ @Test
@SdkSuppress(minSdkVersion = 31, codeName = "S")
public void testShouldShowActionDialog_writeNoACCESS_MEDIA_LOCATION_true() throws Exception {
final String[] enableAppOpsList =
@@ -212,7 +416,8 @@
adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
try {
- setupPermissions(mTestAppUid, enableAppOpsList, disableAppOpsList);
+ setupPermissions(
+ mTestAppUid, enableAppOpsList, disableAppOpsList, TEST_APP_PACKAGE_NAME);
assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid,
TEST_APP_PACKAGE_NAME, null, VERB_WRITE)).isTrue();
@@ -234,7 +439,8 @@
adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
try {
- setupPermissions(mTestAppUid, enableAppOpsList, disableAppOpsList);
+ setupPermissions(
+ mTestAppUid, enableAppOpsList, disableAppOpsList, TEST_APP_PACKAGE_NAME);
assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid,
TEST_APP_PACKAGE_NAME, null, VERB_WRITE)).isFalse();
@@ -245,7 +451,7 @@
}
private static void setupPermissions(int uid, @NonNull String[] enableAppOpsList,
- @NonNull String[] disableAppOpsList) throws Exception {
+ @NonNull String[] disableAppOpsList, @NonNull String packageName) throws Exception {
for (String op : enableAppOpsList) {
modifyAppOp(uid, op, AppOpsManager.MODE_ALLOWED);
}
@@ -254,8 +460,10 @@
modifyAppOp(uid, op, AppOpsManager.MODE_ERRORED);
}
- pollForAppOpPermissions(TEST_APP_PID, uid, enableAppOpsList, /* hasPermission= */ true);
- pollForAppOpPermissions(TEST_APP_PID, uid, disableAppOpsList, /* hasPermission= */ false);
+ pollForAppOpPermissions(
+ TEST_APP_PID, packageName, uid, enableAppOpsList, /* hasPermission= */ true);
+ pollForAppOpPermissions(
+ TEST_APP_PID, packageName, uid, disableAppOpsList, /* hasPermission= */ false);
}
private static void restoreDefaultAppOpPermissions(int uid) {
@@ -288,16 +496,16 @@
getContext().getSystemService(AppOpsManager.class).setUidMode(op, uid, mode);
}
- private static void pollForAppOpPermissions(int pid, int uid, String[] opList,
- boolean hasPermission) throws Exception {
+ private static void pollForAppOpPermissions(int pid, @NonNull String packageName, int uid,
+ String[] opList, boolean hasPermission) throws Exception {
long current = System.currentTimeMillis();
final long timeout = current + TIMEOUT_MILLIS;
final HashSet<String> checkedOpSet = new HashSet<>();
while (current < timeout && checkedOpSet.size() < opList.length) {
for (String op : opList) {
- if (!checkedOpSet.contains(op) && checkPermission(op, pid, uid,
- TEST_APP_PACKAGE_NAME, hasPermission)) {
+ if (!checkedOpSet.contains(op)
+ && checkPermission(op, pid, uid, packageName, hasPermission)) {
checkedOpSet.add(op);
continue;
}
@@ -318,6 +526,15 @@
if (TextUtils.equals(op, OP_READ_EXTERNAL_STORAGE)) {
return expected == checkPermissionReadStorage(context, pid, uid, packageName,
/* attributionTag= */ null);
+ } else if (TextUtils.equals(op, OP_READ_MEDIA_IMAGES)) {
+ return expected == checkPermissionReadImages(
+ context, pid, uid, packageName, /* attributionTag= */ null, /* isAtleastT */ true);
+ } else if (TextUtils.equals(op, OP_READ_MEDIA_AUDIO)) {
+ return expected == checkPermissionReadAudio(
+ context, pid, uid, packageName, /* attributionTag= */ null, /* isAtleastT */ true);
+ } else if (TextUtils.equals(op, OP_READ_MEDIA_VIDEO)) {
+ return expected == checkPermissionReadVideo(
+ context, pid, uid, packageName, /* attributionTag= */ null, /* isAtleastT */ true);
} else if (TextUtils.equals(op, OP_MANAGE_EXTERNAL_STORAGE)) {
return expected == checkPermissionManager(context, pid, uid, packageName,
/* attributionTag= */ null);
diff --git a/tests/src/com/android/providers/media/PickerProviderMediaGenerator.java b/tests/src/com/android/providers/media/PickerProviderMediaGenerator.java
new file mode 100644
index 0000000..2f90d2f
--- /dev/null
+++ b/tests/src/com/android/providers/media/PickerProviderMediaGenerator.java
@@ -0,0 +1,468 @@
+/*
+ * 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;
+
+import static android.provider.CloudMediaProviderContract.AlbumColumns;
+import static android.provider.CloudMediaProviderContract.EXTRA_ALBUM_ID;
+import static android.provider.CloudMediaProviderContract.EXTRA_MEDIA_COLLECTION_ID;
+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_DEFAULT;
+
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.os.Bundle;
+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;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Generates {@link TestMedia} items that can be accessed via test {@link CloudMediaProvider}
+ * instances.
+ */
+public class PickerProviderMediaGenerator {
+ private static final Map<String, MediaGenerator> sMediaGeneratorMap = new HashMap<>();
+ private static final String TAG = "PickerProviderMediaGenerator";
+ private static final String[] MEDIA_PROJECTION = new String[] {
+ MediaColumns.ID,
+ MediaColumns.MEDIA_STORE_URI,
+ MediaColumns.MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION,
+ MediaColumns.DATE_TAKEN_MILLIS,
+ MediaColumns.SYNC_GENERATION,
+ MediaColumns.SIZE_BYTES,
+ MediaColumns.DURATION_MILLIS,
+ MediaColumns.IS_FAVORITE,
+ };
+ private static final String[] ALBUM_MEDIA_PROJECTION = new String[] {
+ MediaColumns.ID,
+ MediaColumns.MEDIA_STORE_URI,
+ MediaColumns.MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION,
+ MediaColumns.DATE_TAKEN_MILLIS,
+ MediaColumns.SYNC_GENERATION,
+ MediaColumns.SIZE_BYTES,
+ MediaColumns.DURATION_MILLIS,
+ };
+
+ private static final String[] ALBUM_PROJECTION = new String[] {
+ AlbumColumns.ID,
+ AlbumColumns.DISPLAY_NAME,
+ AlbumColumns.DATE_TAKEN_MILLIS,
+ AlbumColumns.MEDIA_COVER_ID,
+ AlbumColumns.MEDIA_COUNT,
+ AlbumColumns.AUTHORITY
+ };
+
+ private static final String[] DELETED_MEDIA_PROJECTION = new String[] { MediaColumns.ID };
+
+ public static class MediaGenerator {
+ private final List<TestMedia> mMedia = new ArrayList<>();
+ private final List<TestMedia> mDeletedMedia = new ArrayList<>();
+ private final List<TestAlbum> mAlbums = new ArrayList<>();
+ private String mCollectionId;
+ private long mLastSyncGeneration;
+ private String mAccountName;
+ private Intent mAccountConfigurationIntent;
+ private int mCursorExtraQueryCount;
+ 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,
+ /* isDeleted */ false);
+
+ if (mCursorExtra != null) {
+ cursor.setExtras(mCursorExtra);
+ } else {
+ cursor.setExtras(buildCursorExtras(mCollectionId, generation > 0, albumId != null));
+ }
+
+ if (--mCursorExtraQueryCount == 0) {
+ clearCursorExtras();
+ }
+ return cursor;
+ }
+
+ public Cursor getAlbums(String mimeType, long sizeBytes, boolean isLocal) {
+ final Cursor cursor = getCursor(mAlbums, mimeType, sizeBytes, isLocal);
+
+ if (mCursorExtra != null) {
+ cursor.setExtras(mCursorExtra);
+ } else {
+ cursor.setExtras(buildCursorExtras(mCollectionId, false, false));
+ }
+
+ if (--mCursorExtraQueryCount == 0) {
+ clearCursorExtras();
+ }
+ return cursor;
+ }
+
+ // 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,
+ /* isDeleted */ true);
+
+ if (mCursorExtra != null) {
+ cursor.setExtras(mCursorExtra);
+ } else {
+ cursor.setExtras(buildCursorExtras(mCollectionId, generation > 0, false));
+ }
+
+ if (--mCursorExtraQueryCount == 0) {
+ clearCursorExtras();
+ }
+ return cursor;
+ }
+
+ public Bundle getMediaCollectionInfo() {
+ Bundle bundle = new Bundle();
+ bundle.putString(MediaCollectionInfo.MEDIA_COLLECTION_ID, mCollectionId);
+ bundle.putLong(MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION, mLastSyncGeneration);
+ bundle.putString(MediaCollectionInfo.ACCOUNT_NAME, mAccountName);
+ bundle.putParcelable(MediaCollectionInfo.ACCOUNT_CONFIGURATION_INTENT,
+ mAccountConfigurationIntent);
+
+ return bundle;
+ }
+
+ public void setAccountInfo(String accountName, Intent configIntent) {
+ mAccountName = accountName;
+ mAccountConfigurationIntent = configIntent;
+ }
+
+ public void clearCursorExtras() {
+ mCursorExtra = null;
+ }
+
+ public void setNextCursorExtras(int queryCount, String mediaCollectionId,
+ boolean honoredSyncGeneration, boolean honoredAlbumId) {
+ mCursorExtraQueryCount = queryCount;
+ mCursorExtra = buildCursorExtras(mediaCollectionId, honoredSyncGeneration,
+ honoredAlbumId);
+ }
+
+ public Bundle buildCursorExtras(String mediaCollectionId, boolean honoredSyncGeneration,
+ boolean honoredAlbumdId) {
+ final ArrayList<String> honoredArgs = new ArrayList<>();
+ if (honoredSyncGeneration) {
+ honoredArgs.add(EXTRA_SYNC_GENERATION);
+ }
+ if (honoredAlbumdId) {
+ honoredArgs.add(EXTRA_ALBUM_ID);
+ }
+
+ final Bundle bundle = new Bundle();
+ bundle.putString(CloudMediaProviderContract.EXTRA_MEDIA_COLLECTION_ID,
+ mediaCollectionId);
+ bundle.putStringArrayList(ContentResolver.EXTRA_HONORED_ARGS, honoredArgs);
+
+ return bundle;
+ }
+
+ public void addMedia(String localId, String cloudId) {
+ mDeletedMedia.remove(createPlaceholderMedia(localId, cloudId));
+ mMedia.add(0, createTestMedia(localId, cloudId));
+ }
+
+ public void addAlbumMedia(String localId, String cloudId, String albumId) {
+ mMedia.add(0, createTestAlbumMedia(localId, cloudId, albumId));
+ }
+
+ public void addMedia(String localId, String cloudId, String albumId, String mimeType,
+ int standardMimeTypeExtension, long sizeBytes, boolean isFavorite) {
+ mDeletedMedia.remove(createPlaceholderMedia(localId, cloudId));
+ mMedia.add(0,
+ createTestMedia(localId, cloudId, albumId, mimeType, standardMimeTypeExtension,
+ sizeBytes, isFavorite));
+ }
+
+ public void deleteMedia(String localId, String cloudId) {
+ if (mMedia.remove(createPlaceholderMedia(localId, cloudId))) {
+ mDeletedMedia.add(createTestMedia(localId, cloudId));
+ }
+ }
+
+ public void createAlbum(String id) {
+ mAlbums.add(createTestAlbum(id));
+ }
+
+ public void resetAll() {
+ mMedia.clear();
+ mDeletedMedia.clear();
+ mAlbums.clear();
+ clearCursorExtras();
+ }
+
+ public void setMediaCollectionId(String id) {
+ mCollectionId = id;
+ }
+
+ public long getCount() {
+ return mMedia.size();
+ }
+
+ private TestAlbum createTestAlbum(String id) {
+ return new TestAlbum(id, mMedia);
+ }
+
+ private TestMedia createTestMedia(String localId, String cloudId) {
+ // Increase generation
+ return new TestMedia(localId, cloudId, ++mLastSyncGeneration);
+ }
+ private TestMedia createTestAlbumMedia(String localId, String cloudId, String albumId) {
+ // Increase generation
+ return new TestMedia(localId, cloudId, albumId);
+ }
+
+ private TestMedia createTestMedia(String localId, String cloudId, String albumId,
+ String mimeType, int standardMimeTypeExtension, long sizeBytes,
+ boolean isFavorite) {
+ // Increase generation
+ return new TestMedia(localId, cloudId, albumId, mimeType, standardMimeTypeExtension,
+ sizeBytes, /* durationMs */ 0, ++mLastSyncGeneration, isFavorite);
+ }
+
+ private static TestMedia createPlaceholderMedia(String localId, String cloudId) {
+ // Don't increase generation. Used to create a throw-away element used for removal from
+ // |mMedia| or |mDeletedMedia|
+ return new TestMedia(localId, cloudId, 0);
+ }
+
+ private static Cursor getCursor(List<TestMedia> mediaList, long generation,
+ String albumId, String mimeType, long sizeBytes, boolean isDeleted) {
+ final MatrixCursor matrix;
+ if (isDeleted) {
+ matrix = new MatrixCursor(DELETED_MEDIA_PROJECTION);
+ } else if(!TextUtils.isEmpty(albumId)) {
+ matrix = new MatrixCursor(ALBUM_MEDIA_PROJECTION);
+ } else {
+ matrix = new MatrixCursor(MEDIA_PROJECTION);
+ }
+
+ for (TestMedia media : mediaList) {
+ if (!TextUtils.isEmpty(albumId) && matchesFilter(media,
+ albumId, mimeType, sizeBytes)) {
+ matrix.addRow(media.toAlbumMediaArray());
+ } else if (media.generation > generation
+ && matchesFilter(media, albumId, mimeType, sizeBytes)) {
+ matrix.addRow(media.toArray(isDeleted));
+ }
+ }
+ return matrix;
+ }
+
+ private static Cursor getCursor(List<TestAlbum> albumList, String mimeType, long sizeBytes,
+ boolean isLocal) {
+ final MatrixCursor matrix = new MatrixCursor(ALBUM_PROJECTION);
+
+ for (TestAlbum album : albumList) {
+ final String[] res = album.toArray(mimeType, sizeBytes, isLocal);
+ if (res != null) {
+ matrix.addRow(res);
+ }
+ }
+ return matrix;
+ }
+ }
+
+ private static class TestMedia {
+ public final String localId;
+ public final String cloudId;
+ public final String albumId;
+ public final String mimeType;
+ public final int standardMimeTypeExtension;
+ public final long sizeBytes;
+ public final long dateTakenMs;
+ public final long durationMs;
+ public final long generation;
+ public final boolean isFavorite;
+
+ public TestMedia(String localId, String cloudId, long generation) {
+ this(localId, cloudId, /* albumId */ null, "image/jpeg",
+ /* standardMimeTypeExtension */ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE,
+ /* sizeBytes */ 4096, /* durationMs */ 0, generation,
+ /* isFavorite */ false);
+ }
+
+
+ public TestMedia(String localId, String cloudId, String albumId) {
+ this(localId, cloudId, /* albumId */ albumId, "image/jpeg",
+ /* standardMimeTypeExtension */ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE,
+ /* sizeBytes */ 4096, /* durationMs */ 0, 0,
+ /* isFavorite */ false);
+ }
+
+ public TestMedia(String localId, String cloudId, String albumId, String mimeType,
+ int standardMimeTypeExtension, long sizeBytes, long durationMs, long generation,
+ boolean isFavorite) {
+ this.localId = localId;
+ this.cloudId = cloudId;
+ this.albumId = albumId;
+ this.mimeType = mimeType;
+ this.standardMimeTypeExtension = standardMimeTypeExtension;
+ this.sizeBytes = sizeBytes;
+ this.dateTakenMs = System.currentTimeMillis();
+ this.durationMs = durationMs;
+ this.generation = generation;
+ this.isFavorite = isFavorite;
+ SystemClock.sleep(1);
+ }
+
+ public String[] toArray(boolean isDeleted) {
+ if (isDeleted) {
+ return new String[] {getId()};
+ }
+
+ return new String[] {
+ getId(),
+ localId == null ? null : "content://media/external/files/" + localId,
+ mimeType,
+ String.valueOf(standardMimeTypeExtension),
+ String.valueOf(dateTakenMs),
+ String.valueOf(generation),
+ String.valueOf(sizeBytes),
+ String.valueOf(durationMs),
+ String.valueOf(isFavorite ? 1 : 0)
+ };
+ }
+
+ public String[] toAlbumMediaArray() {
+ return new String[] {
+ getId(),
+ localId == null ? null : "content://media/external/files/" + localId,
+ mimeType,
+ String.valueOf(standardMimeTypeExtension),
+ String.valueOf(dateTakenMs),
+ String.valueOf(generation),
+ String.valueOf(sizeBytes),
+ String.valueOf(durationMs)
+ };
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof TestMedia)) {
+ return false;
+ }
+ TestMedia other = (TestMedia) o;
+ return Objects.equals(localId, other.localId) && Objects.equals(cloudId, other.cloudId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(localId, cloudId);
+ }
+
+ private String getId() {
+ return cloudId == null ? localId : cloudId;
+ }
+ }
+
+ private static class TestAlbum {
+ public final String id;
+ private final List<TestMedia> media;
+
+ public TestAlbum(String id, List<TestMedia> media) {
+ this.id = id;
+ this.media = media;
+ }
+
+ public String[] toArray(String mimeType, long sizeBytes, boolean isLocal) {
+ long mediaCount = 0;
+ String mediaCoverId = null;
+ long dateTakenMs = 0;
+
+ for (TestMedia m : media) {
+ if (matchesFilter(m, id, mimeType, sizeBytes)) {
+ if (mediaCount++ == 0) {
+ mediaCoverId = m.getId();
+ dateTakenMs = m.dateTakenMs;
+ }
+ }
+ }
+
+ if (mediaCount == 0) {
+ return null;
+ }
+
+ return new String[] {
+ id,
+ mediaCoverId,
+ /* displayName */ id,
+ String.valueOf(dateTakenMs),
+ String.valueOf(mediaCount),
+ isLocal ? LocalProvider.AUTHORITY : null
+ };
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof TestAlbum)) {
+ return false;
+ }
+
+ TestAlbum other = (TestAlbum) o;
+ return Objects.equals(id, other.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id);
+ }
+ }
+
+ private static boolean matchesFilter(TestMedia media, String albumId, String mimeType,
+ 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 (sizeBytes != LONG_DEFAULT && media.sizeBytes > sizeBytes) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public static MediaGenerator getMediaGenerator(String authority) {
+ MediaGenerator generator = sMediaGeneratorMap.get(authority);
+ if (generator == null) {
+ generator = new MediaGenerator();
+ sMediaGeneratorMap.put(authority, generator);
+ }
+ return generator;
+ }
+}
diff --git a/tests/src/com/android/providers/media/PickerUriResolverTest.java b/tests/src/com/android/providers/media/PickerUriResolverTest.java
new file mode 100644
index 0000000..3de26ec
--- /dev/null
+++ b/tests/src/com/android/providers/media/PickerUriResolverTest.java
@@ -0,0 +1,463 @@
+/*
+ * 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;
+
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.provider.MediaStore.MediaColumns._ID;
+
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
+import android.provider.CloudMediaProviderContract;
+import android.provider.MediaStore;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.providers.media.photopicker.PickerSyncController;
+import com.android.providers.media.photopicker.data.PickerDbFacade;
+import com.android.providers.media.scan.MediaScannerTest;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+
+@RunWith(AndroidJUnit4.class)
+public class PickerUriResolverTest {
+ private static final String TAG = PickerUriResolverTest.class.getSimpleName();
+ private static final File TEST_FILE = new File(Environment.getExternalStorageDirectory(),
+ Environment.DIRECTORY_DOWNLOADS + "/" + TAG + System.currentTimeMillis() + ".jpeg");
+ // UserId for which context and content resolver are set up such that TEST_FILE
+ // file exists in this user's content resolver.
+ private static final int TEST_USER = 20;
+
+ private static Context sCurrentContext;
+ private static TestPickerUriResolver sTestPickerUriResolver;
+ private static Uri sTestPickerUri;
+ private static String TEST_ID;
+
+ private static class TestPickerUriResolver extends PickerUriResolver {
+ TestPickerUriResolver(Context context) {
+ super(context, new PickerDbFacade(getTargetContext()));
+ }
+
+ @Override
+ Cursor queryPickerUri(Uri uri, String[] projection) {
+ if (!uri.getLastPathSegment().equals(TEST_ID)) {
+ return super.queryPickerUri(uri, projection);
+ }
+
+ final String[] p = new String[] {
+ CloudMediaProviderContract.MediaColumns.ID,
+ CloudMediaProviderContract.MediaColumns.MIME_TYPE
+ };
+
+ final MatrixCursor c = new MatrixCursor(p);
+ c.addRow(new String[] { TEST_ID, "image/jpeg"});
+ return c;
+ }
+
+ @Override
+ File getPickerFileFromUri(Uri uri) {
+ if (!uri.getLastPathSegment().equals(TEST_ID)) {
+ return super.getPickerFileFromUri(uri);
+ }
+
+ return TEST_FILE;
+ }
+ }
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ // this test uses isolated context which requires these permissions to be granted
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .adoptShellPermissionIdentity(android.Manifest.permission.LOG_COMPAT_CHANGE,
+ android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
+ Manifest.permission.INTERACT_ACROSS_USERS);
+ sCurrentContext = mock(Context.class);
+ when(sCurrentContext.getUser()).thenReturn(UserHandle.of(UserHandle.myUserId()));
+
+ final Context otherUserContext = createOtherUserContext(TEST_USER);
+ sTestPickerUriResolver = new TestPickerUriResolver(sCurrentContext);
+
+ final Uri mediaStoreUriInOtherContext = createTestFileInContext(otherUserContext);
+ TEST_ID = mediaStoreUriInOtherContext.getLastPathSegment();
+ sTestPickerUri = getPickerUriForId(ContentUris.parseId(mediaStoreUriInOtherContext),
+ TEST_USER);
+ }
+
+ @AfterClass
+ public static void tearDown() {
+ TEST_FILE.delete();
+ }
+
+ @Test
+ public void wrapProviderUriValid() throws Exception {
+ final String providerSuffix = "authority/media/media_id";
+
+ final Uri providerUriUserImplicit = Uri.parse("content://" + providerSuffix);
+
+ final Uri providerUriUser0 = Uri.parse("content://0@" + providerSuffix);
+ final Uri mediaUriUser0 = Uri.parse("content://media/picker/0/" + providerSuffix);
+
+ final Uri providerUriUser10 = Uri.parse("content://10@" + providerSuffix);
+ final Uri mediaUriUser10 = Uri.parse("content://media/picker/10/" + providerSuffix);
+
+ assertThat(PickerUriResolver.wrapProviderUri(providerUriUserImplicit, 0))
+ .isEqualTo(mediaUriUser0);
+ assertThat(PickerUriResolver.wrapProviderUri(providerUriUser0, 0)).isEqualTo(mediaUriUser0);
+ assertThat(PickerUriResolver.unwrapProviderUri(mediaUriUser0)).isEqualTo(providerUriUser0);
+
+ assertThat(PickerUriResolver.wrapProviderUri(providerUriUserImplicit, 10))
+ .isEqualTo(mediaUriUser10);
+ assertThat(PickerUriResolver.wrapProviderUri(providerUriUser10, 10))
+ .isEqualTo(mediaUriUser10);
+ assertThat(PickerUriResolver.unwrapProviderUri(mediaUriUser10))
+ .isEqualTo(providerUriUser10);
+ }
+
+ @Test
+ public void wrapProviderUriInvalid() throws Exception {
+ final String providerSuffixLong = "authority/media/media_id/another_media_id";
+ final String providerSuffixShort = "authority/media";
+
+ final Uri providerUriUserLong = Uri.parse("content://0@" + providerSuffixLong);
+ final Uri mediaUriUserLong = Uri.parse("content://media/picker/0/" + providerSuffixLong);
+
+ final Uri providerUriUserShort = Uri.parse("content://0@" + providerSuffixShort);
+ final Uri mediaUriUserShort = Uri.parse("content://media/picker/0/" + providerSuffixShort);
+
+ assertThrows(IllegalArgumentException.class,
+ () -> PickerUriResolver.wrapProviderUri(providerUriUserLong, 0));
+ assertThrows(IllegalArgumentException.class,
+ () -> PickerUriResolver.unwrapProviderUri(mediaUriUserLong));
+
+ assertThrows(IllegalArgumentException.class,
+ () -> PickerUriResolver.unwrapProviderUri(mediaUriUserShort));
+ assertThrows(IllegalArgumentException.class,
+ () -> PickerUriResolver.wrapProviderUri(providerUriUserShort, 0));
+ }
+
+ @Test
+ public void testGetAlbumUri() throws Exception {
+ final String authority = "foo";
+ final Uri uri = Uri.parse("content://foo/album");
+ assertThat(PickerUriResolver.getAlbumUri(authority)).isEqualTo(uri);
+ }
+
+ @Test
+ public void testGetMediaUri() throws Exception {
+ final String authority = "foo";
+ final Uri uri = Uri.parse("content://foo/media");
+ assertThat(PickerUriResolver.getMediaUri(authority)).isEqualTo(uri);
+ }
+
+ @Test
+ public void testGetDeletedMediaUri() throws Exception {
+ final String authority = "foo";
+ final Uri uri = Uri.parse("content://foo/deleted_media");
+ assertThat(PickerUriResolver.getDeletedMediaUri(authority)).isEqualTo(uri);
+ }
+
+ @Test
+ public void testCreateSurfaceControllerUri() throws Exception {
+ final String authority = "foo";
+ final Uri uri = Uri.parse("content://foo/surface_controller");
+ assertThat(PickerUriResolver.createSurfaceControllerUri(authority)).isEqualTo(uri);
+ }
+
+ @Test
+ public void testOpenFile_mode_w() throws Exception {
+ updateReadUriPermission(sTestPickerUri, /* grant */ true);
+ try {
+ sTestPickerUriResolver.openFile(sTestPickerUri, "w", /* signal */ null,
+ /* callingPid */ -1, /* callingUid */ -1);
+ fail("Write is not supported for Picker Uris. uri: " + sTestPickerUri);
+ } catch (SecurityException expected) {
+ // expected
+ assertThat(expected.getMessage()).isEqualTo("PhotoPicker Uris can only be accessed to"
+ + " read. Uri: " + sTestPickerUri);
+ }
+ }
+
+ @Test
+ public void testOpenFile_mode_rw() throws Exception {
+ updateReadUriPermission(sTestPickerUri, /* grant */ true);
+ try {
+ sTestPickerUriResolver.openFile(sTestPickerUri, "rw", /* signal */ null,
+ /* callingPid */ -1, /* callingUid */ -1);
+ fail("Read-Write is not supported for Picker Uris. uri: " + sTestPickerUri);
+ } catch (SecurityException expected) {
+ // expected
+ assertThat(expected.getMessage()).isEqualTo("PhotoPicker Uris can only be accessed to"
+ + " read. Uri: " + sTestPickerUri);
+ }
+ }
+
+ @Test
+ public void testOpenFile_mode_invalid() throws Exception {
+ updateReadUriPermission(sTestPickerUri, /* grant */ true);
+ try {
+ sTestPickerUriResolver.openFile(sTestPickerUri, "foo", /* signal */ null,
+ /* callingPid */ -1, /* callingUid */ -1);
+ fail("Invalid mode should not be supported for openFile. uri: " + sTestPickerUri);
+ } catch (IllegalArgumentException expected) {
+ // expected
+ assertThat(expected.getMessage()).isEqualTo("Bad mode: foo");
+ }
+ }
+
+ @Test
+ public void testPickerUriResolver_permissionDenied() throws Exception {
+ updateReadUriPermission(sTestPickerUri, /* grant */ false);
+
+ testOpenFile_permissionDenied(sTestPickerUri);
+ testOpenTypedAssetFile_permissionDenied(sTestPickerUri);
+ testQuery_permissionDenied(sTestPickerUri);
+ testGetType_permissionDenied(sTestPickerUri);
+ }
+
+ @Test
+ public void testPermissionGrantedOnOtherUserUri() throws Exception {
+ // This test requires the uri to be valid in 2 different users, but the permission is
+ // granted in one user only.
+ final int otherUserId = 50;
+ final Context otherUserContext = createOtherUserContext(otherUserId);
+ final Uri mediaStoreUserInAnotherValidUser = createTestFileInContext(otherUserContext);
+ final Uri grantedUri = getPickerUriForId(ContentUris.parseId(
+ mediaStoreUserInAnotherValidUser), otherUserId);
+ updateReadUriPermission(grantedUri, /* grant */ true);
+
+ final Uri deniedUri = sTestPickerUri;
+ updateReadUriPermission(deniedUri, /* grant */ false);
+
+ testOpenFile_permissionDenied(deniedUri);
+ testOpenTypedAssetFile_permissionDenied(deniedUri);
+ testQuery_permissionDenied(deniedUri);
+ testGetType_permissionDenied(deniedUri);
+ }
+
+ @Test
+ public void testPickerUriResolver_userInvalid() throws Exception {
+ final int invalidUserId = 40;
+
+ final Uri inValidUserPickerUri = getPickerUriForId(/* id */ 1, invalidUserId);
+ updateReadUriPermission(inValidUserPickerUri, /* grant */ true);
+
+ // This method is called on current context when pickerUriResolver wants to get the content
+ // resolver for another user.
+ // NameNotFoundException is thrown when such a user does not exist.
+ when(sCurrentContext.createPackageContextAsUser("android", /* flags= */ 0,
+ UserHandle.of(invalidUserId))).thenThrow(
+ new PackageManager.NameNotFoundException());
+
+ testOpenFileInvalidUser(inValidUserPickerUri);
+ testOpenTypedAssetFileInvalidUser(inValidUserPickerUri);
+ testQueryInvalidUser(inValidUserPickerUri);
+ testGetTypeInvalidUser(inValidUserPickerUri);
+ }
+
+ @Test
+ public void testPickerUriResolver_userValid() throws Exception {
+ updateReadUriPermission(sTestPickerUri, /* grant */ true);
+
+ assertThat(PickerUriResolver.getUserId(sTestPickerUri)).isEqualTo(TEST_USER);
+ testOpenFile(sTestPickerUri);
+ testOpenTypedAssetFile(sTestPickerUri);
+ testQuery(sTestPickerUri);
+ testGetType(sTestPickerUri, "image/jpeg");
+ }
+
+ private static Context createOtherUserContext(int user) throws Exception {
+ final UserHandle userHandle = UserHandle.of(user);
+ // For unit testing: IsolatedContext is the context of another User: user.
+ // PickerUriResolver should correctly be able to call into other user's content resolver
+ // from the current context.
+ final MediaScannerTest.IsolatedContext otherUserContext =
+ new MediaScannerTest.IsolatedContext(getTargetContext(), "databases",
+ /* asFuseThread */ false, userHandle);
+ otherUserContext.setPickerUriResolver(new TestPickerUriResolver(otherUserContext));
+
+ when(sCurrentContext.createPackageContextAsUser("android", /* flags= */ 0, userHandle)).
+ thenReturn(otherUserContext);
+ return otherUserContext;
+ }
+
+ private static Uri createTestFileInContext(Context context) throws Exception {
+ TEST_FILE.createNewFile();
+ // Write 1 byte because 0byte files are not valid in the picker db
+ try (FileOutputStream fos = new FileOutputStream(TEST_FILE)) {
+ fos.write(1);
+ }
+
+ final Uri uri = MediaStore.scanFile(context.getContentResolver(), TEST_FILE);
+ assertThat(uri).isNotNull();
+ MediaStore.waitForIdle(context.getContentResolver());
+ return uri;
+ }
+
+ private void updateReadUriPermission(Uri uri, boolean grant) {
+ final int permission = grant ? PERMISSION_GRANTED : PERMISSION_DENIED;
+ when(sCurrentContext.checkUriPermission(uri, -1, -1,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION)).thenReturn(permission);
+ }
+
+ private static Uri getPickerUriForId(long id, int user) {
+ final Uri providerUri = PickerUriResolver
+ .getMediaUri(PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY)
+ .buildUpon()
+ .appendPath(String.valueOf(id))
+ .build();
+ return PickerUriResolver.wrapProviderUri(providerUri, user);
+ }
+
+ private void testOpenFile(Uri uri) throws Exception {
+ try (ParcelFileDescriptor pfd = sTestPickerUriResolver.openFile(uri, "r", /* signal */ null,
+ /* callingPid */ -1, /* callingUid */ -1)) {
+ assertThat(pfd).isNotNull();
+ }
+ }
+
+ private void testOpenTypedAssetFile(Uri uri) throws Exception {
+ try (AssetFileDescriptor afd = sTestPickerUriResolver.openTypedAssetFile(uri, "image/*",
+ /* opts */ null, /* signal */ null, /* callingPid */ -1, /* callingUid */ -1)) {
+ assertThat(afd).isNotNull();
+ }
+ }
+
+ private void testQuery(Uri uri) throws Exception {
+ Cursor result = sTestPickerUriResolver.query(uri,
+ /* projection */ new String[]{_ID}, /* callingPid */ -1, /* callingUid */ -1);
+ assertThat(result).isNotNull();
+ assertThat(result.getCount()).isEqualTo(1);
+ result.moveToFirst();
+ assertThat(result.getString(0)).isEqualTo(TEST_ID);
+ }
+
+ private void testGetType(Uri uri, String expectedMimeType) throws Exception {
+ String mimeType = sTestPickerUriResolver.getType(uri);
+ assertThat(mimeType).isEqualTo(expectedMimeType);
+ }
+
+ private void testOpenFileInvalidUser(Uri uri) {
+ try {
+ sTestPickerUriResolver.openFile(uri, "r", /* signal */ null, /* callingPid */ -1,
+ /* callingUid */ -1);
+ fail("Invalid user specified in the picker uri: " + uri);
+ } catch (FileNotFoundException expected) {
+ // expected
+ assertThat(expected.getMessage()).isEqualTo("No item at " + uri);
+ }
+ }
+
+ private void testOpenTypedAssetFileInvalidUser(Uri uri) throws Exception {
+ try {
+ sTestPickerUriResolver.openTypedAssetFile(uri, "image/*", /* opts */ null,
+ /* signal */ null, /* callingPid */ -1, /* callingUid */ -1);
+ fail("Invalid user specified in the picker uri: " + uri);
+ } catch (FileNotFoundException expected) {
+ // expected
+ assertThat(expected.getMessage()).isEqualTo("No item at " + uri);
+ }
+ }
+
+ private void testQueryInvalidUser(Uri uri) throws Exception {
+ Cursor result = sTestPickerUriResolver.query(uri, /* projection */ null,
+ /* callingPid */ -1, /* callingUid */ -1);
+ assertThat(result).isNotNull();
+ assertThat(result.getCount()).isEqualTo(0);
+ }
+
+ private void testGetTypeInvalidUser(Uri uri) throws Exception {
+ try {
+ sTestPickerUriResolver.getType(uri);
+ fail("Invalid user specified in the picker uri: " + uri);
+ } catch (IllegalStateException expected) {
+ // expected
+ assertThat(expected.getMessage()).isEqualTo("Cannot find content resolver for uri: "
+ + uri);
+ }
+ }
+
+ private void testOpenFile_permissionDenied(Uri uri) throws Exception {
+ try {
+ sTestPickerUriResolver.openFile(uri, "r", /* signal */ null, /* callingPid */ -1,
+ /* callingUid */ -1);
+ fail("openFile should fail if the caller does not have permission grant on the picker"
+ + " uri: " + uri);
+ } catch (SecurityException expected) {
+ // expected
+ assertThat(expected.getMessage()).isEqualTo("Calling uid ( -1 ) does not have"
+ + " permission to access picker uri: " + uri);
+ }
+ }
+
+ private void testOpenTypedAssetFile_permissionDenied(Uri uri) throws Exception {
+ try {
+ sTestPickerUriResolver.openTypedAssetFile(uri, "image/*", /* opts */ null,
+ /* signal */ null, /* callingPid */ -1, /* callingUid */ -1);
+ fail("openTypedAssetFile should fail if the caller does not have permission grant on"
+ + " the picker uri: " + uri);
+ } catch (SecurityException expected) {
+ // expected
+ assertThat(expected.getMessage()).isEqualTo("Calling uid ( -1 ) does not have"
+ + " permission to access picker uri: " + uri);
+ }
+ }
+
+ private void testQuery_permissionDenied(Uri uri) throws Exception {
+ try {
+ sTestPickerUriResolver.query(uri, /* projection */ null
+ , /* callingPid */ -1, /* callingUid */ -1);
+ fail("query should fail if the caller does not have permission grant on"
+ + " the picker uri: " + uri);
+ } catch (SecurityException expected) {
+ // expected
+ assertThat(expected.getMessage()).isEqualTo("Calling uid ( -1 ) does not have"
+ + " permission to access picker uri: " + uri);
+ }
+ }
+
+ private void testGetType_permissionDenied(Uri uri) throws Exception {
+ // getType is unaffected by uri permission grants
+ testGetType(uri, "image/jpeg");
+ }
+}
diff --git a/tests/src/com/android/providers/media/PublicVolumeTest.java b/tests/src/com/android/providers/media/PublicVolumeTest.java
new file mode 100644
index 0000000..503fab1
--- /dev/null
+++ b/tests/src/com/android/providers/media/PublicVolumeTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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;
+
+import static com.android.providers.media.tests.utils.PublicVolumeSetupHelper.createNewPublicVolume;
+import static com.android.providers.media.tests.utils.PublicVolumeSetupHelper.deletePublicVolumes;
+import static com.android.providers.media.tests.utils.PublicVolumeSetupHelper.partitionPublicVolume;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageVolume;
+import android.provider.MediaStore;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.providers.media.util.FileUtils;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class PublicVolumeTest {
+ static final int POLL_DELAY_MS = 500;
+ static final int WAIT_FOR_DEFAULT_FOLDERS_MS = 30000;
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ createNewPublicVolume();
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ deletePublicVolumes();
+ }
+
+ public boolean containsDefaultFolders(String rootPath) {
+ for (String dirName : FileUtils.DEFAULT_FOLDER_NAMES) {
+ final File defaultFolder = new File(rootPath, dirName);
+ if (!defaultFolder.exists()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean pollContainsDefaultFolders(String rootPath) {
+ // Default folders are created by MediaProvider after receiving a callback from
+ // the StorageManagerService that the volume has been mounted.
+ // Unfortunately, we don't have a reliable way to determine when this callback has
+ // happened, so poll here.
+ for (int i = 0; i < WAIT_FOR_DEFAULT_FOLDERS_MS / POLL_DELAY_MS; i++) {
+ if (containsDefaultFolders(rootPath)) {
+ return true;
+ }
+ try {
+ Thread.sleep(POLL_DELAY_MS);
+ } catch (InterruptedException e) {
+ }
+ }
+ return false;
+ }
+
+ @Test
+ public void testPublicVolumeDefaultFolders() throws Exception {
+ Context context = InstrumentationRegistry.getTargetContext();
+
+ // Reformat the volume, which should make sure we have default folders
+ partitionPublicVolume();
+
+ List<StorageVolume> volumes = context.
+ getSystemService(StorageManager.class).getStorageVolumes();
+ for (StorageVolume volume : volumes) {
+ // We only want to verify reliable public volumes (not OTG)
+ if (!volume.isPrimary() && volume.getPath().startsWith("/storage")) {
+ assertTrue(pollContainsDefaultFolders(volume.getPath()));
+ }
+ }
+
+ // We had a bug before where we didn't re-create public volumes when the same
+ // volume was re-formatted. Repartition it and try again.
+ partitionPublicVolume();
+
+ volumes = context.getSystemService(StorageManager.class).getStorageVolumes();
+ for (StorageVolume volume : volumes) {
+ // We only want to verify reliable public volumes (not OTG)
+ if (!volume.isPrimary() && volume.getPath().startsWith("/storage")) {
+ assertTrue(pollContainsDefaultFolders(volume.getPath()));
+ }
+ }
+ }
+}
+
diff --git a/tests/src/com/android/providers/media/UnreliableVolumeTest.java b/tests/src/com/android/providers/media/UnreliableVolumeTest.java
new file mode 100644
index 0000000..12871ef
--- /dev/null
+++ b/tests/src/com/android/providers/media/UnreliableVolumeTest.java
@@ -0,0 +1,244 @@
+/*
+ * 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;
+
+import static androidx.test.InstrumentationRegistry.getContext;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.*;
+
+import android.app.UiAutomation;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.Environment;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+
+import com.android.providers.media.photopicker.data.UnreliableVolumeDatabaseHelper;
+import com.android.providers.media.scan.MediaScannerTest;
+import com.android.providers.media.util.UserCache;
+import com.google.common.io.ByteStreams;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Supplier;
+
+public class UnreliableVolumeTest {
+
+ private static String sVolumePath;
+ private static String sVolumeName;
+ private static VolumeCache sVolumeCache;
+ private static final long POLLING_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(2);
+ private static final long POLLING_SLEEP_MILLIS = 100;
+ private static final String TAG = "UnreliableVolumeTest";
+
+ static final String UNRELIABLE_VOLUME_TABLE = "media";
+
+ private static final long SIZE_BYTES = 7000;
+ private static final String DISPLAY_NAME = "first day of school";
+ private static final long DATE_MODIFIED = 1623852851911L;
+ private static final String MIME_TYPE = "image/jpg";
+ private static String _DATA = "/foo/bar/internship.jpeg";
+
+ private static Context sIsolatedContext;
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ createRemovableVolume();
+ final Context context = getContext();
+ UserCache mUserCache = new UserCache(context);
+ sIsolatedContext = new MediaScannerTest.IsolatedContext(context, TAG,
+ /*asFuseThread*/ false);
+ sVolumeCache = new VolumeCache(context, mUserCache);
+ sVolumeCache.update();
+ sVolumeName = getCurrentPublicVolumeString();
+ sVolumePath = "/mnt/media_rw/" + sVolumeName;
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ executeShellCommand("sm set-virtual-disk false");
+ pollForCondition(() -> !isPublicVolumeMounted(), "Timed out while waiting for"
+ + " the public volume to disappear");
+ }
+
+ @Test
+ @Ignore("Enable after b/197816987 is fixed")
+ public void testUnreliableVolumeSimple() throws Exception {
+ assertEquals(sVolumeName, sVolumeCache.getUnreliableVolumePath().get(0).getName());
+ assertEquals(sVolumePath, sVolumeCache.getUnreliableVolumePath().get(0).getPath());
+ }
+
+ @Test
+ @Ignore("Enable after b/197816987 is fixed")
+ public void testDBisCreated() throws Exception {
+ String[] projection = new String[] {
+ UnreliableVolumeDatabaseHelper.MediaColumns.SIZE_BYTES,
+ UnreliableVolumeDatabaseHelper.MediaColumns.DISPLAY_NAME,
+ UnreliableVolumeDatabaseHelper.MediaColumns.DATE_MODIFIED,
+ UnreliableVolumeDatabaseHelper.MediaColumns.MIME_TYPE,
+ UnreliableVolumeDatabaseHelper.MediaColumns._DATA
+ };
+
+ try (UnreliableVolumeDatabaseHelper helper =
+ new UnreliableVolumeDatabaseHelper(sIsolatedContext)) {
+ SQLiteDatabase db = helper.getWritableDatabase();
+ ContentValues values = generateAndGetContentValues();
+ assertThat(db.insert(UNRELIABLE_VOLUME_TABLE, null, values)).isNotEqualTo(-1);
+
+ try (Cursor cr = db.query(UNRELIABLE_VOLUME_TABLE, projection, null, null, null,
+ null, null)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ while (cr.moveToNext()) {
+ assertThat(cr.getLong(0)).isEqualTo(SIZE_BYTES);
+ assertThat(cr.getString(1)).isEqualTo(DISPLAY_NAME);
+ assertThat(cr.getLong(2)).isEqualTo(DATE_MODIFIED);
+ assertThat(cr.getString(3)).isEqualTo(MIME_TYPE);
+ assertThat(cr.getString(4)).isEqualTo(_DATA);
+ }
+ }
+ }
+ }
+
+ private static ContentValues generateAndGetContentValues() {
+ ContentValues values = new ContentValues();
+ values.put(UnreliableVolumeDatabaseHelper.MediaColumns.SIZE_BYTES, SIZE_BYTES);
+ values.put(UnreliableVolumeDatabaseHelper.MediaColumns.DISPLAY_NAME, DISPLAY_NAME);
+ values.put(UnreliableVolumeDatabaseHelper.MediaColumns.DATE_MODIFIED, DATE_MODIFIED);
+ values.put(UnreliableVolumeDatabaseHelper.MediaColumns.MIME_TYPE, MIME_TYPE);
+ values.put(UnreliableVolumeDatabaseHelper.MediaColumns._DATA, _DATA);
+ return values;
+ }
+
+ /**
+ * Executes a shell command.
+ */
+ public static String executeShellCommand(String pattern, Object...args) throws IOException {
+ String command = String.format(pattern, args);
+ int attempt = 0;
+ while (attempt++ < 5) {
+ try {
+ return executeShellCommandInternal(command);
+ } catch (InterruptedIOException e) {
+ Log.v(TAG, "Trouble executing " + command + "; trying again", e);
+ }
+ }
+ throw new IOException("Failed to execute " + command);
+ }
+
+ private static String executeShellCommandInternal(String cmd) throws IOException {
+ UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ try (FileInputStream output = new FileInputStream(
+ uiAutomation.executeShellCommand(cmd).getFileDescriptor())) {
+ return new String(ByteStreams.toByteArray(output));
+ }
+ }
+
+ private static void pollForCondition(Supplier<Boolean> condition, String errorMessage)
+ throws Exception {
+ for (int i = 0; i < POLLING_TIMEOUT_MILLIS / POLLING_SLEEP_MILLIS; i++) {
+ if (condition.get()) {
+ return;
+ }
+ Thread.sleep(POLLING_SLEEP_MILLIS);
+ }
+ throw new TimeoutException(errorMessage);
+ }
+
+ private static boolean isPublicVolumeMounted() {
+ try {
+ final String publicVolume = executeShellCommand("sm list-volumes public").trim();
+ return publicVolume != null && publicVolume.contains("mounted");
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ private static boolean partitionDisk() {
+ try {
+ final String listDisks = executeShellCommand("sm list-disks").trim();
+ if (listDisks.length() > 0) {
+ executeShellCommand("sm partition " + listDisks + " public");
+ return true;
+ }
+ return false;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ private static String getCurrentPublicVolumeString() {
+ final String[] allPublicVolumeDetails;
+ try {
+ allPublicVolumeDetails = executeShellCommand("sm list-volumes public")
+ .trim().split("\n");
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to execute shell command", e);
+ return null;
+ }
+ for (String volDetails : allPublicVolumeDetails) {
+ if (volDetails.startsWith("public")) {
+ final String[] publicVolumeDetails = volDetails.trim().split(" ");
+ String res = publicVolumeDetails[publicVolumeDetails.length - 1];
+ if ("null".equals(res)) {
+ continue;
+ }
+ return res;
+ }
+ }
+ return null;
+ }
+
+ private static boolean isExternalStorageStateMounted() {
+ final File target = Environment.getExternalStorageDirectory();
+ try {
+ return (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState(target))
+ && Os.statvfs(target.getAbsolutePath()).f_blocks > 0);
+ } catch (ErrnoException ignored) {
+ }
+ return false;
+ }
+
+ /**
+ * Creates a new OTG-USB volume
+ */
+ public static void createRemovableVolume() throws Exception {
+ executeShellCommand("sm set-force-adoptable off");
+ executeShellCommand("sm set-virtual-disk true");
+ pollForCondition(() -> partitionDisk(), "Timed out while waiting for"
+ + " disk partitioning");
+ // Poll twice to avoid using previous mount status
+ pollForCondition(() -> isPublicVolumeMounted(), "Timed out while waiting for"
+ + " the public volume to mount");
+ pollForCondition(() -> isExternalStorageStateMounted(), "Timed out while"
+ + " waiting for ExternalStorageState to be MEDIA_MOUNTED");
+ }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/providers/media/cloudproviders/CloudProviderNoIntentFilter.java b/tests/src/com/android/providers/media/cloudproviders/CloudProviderNoIntentFilter.java
new file mode 100644
index 0000000..bb24193
--- /dev/null
+++ b/tests/src/com/android/providers/media/cloudproviders/CloudProviderNoIntentFilter.java
@@ -0,0 +1,64 @@
+/*
+ * 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.cloudproviders;
+
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.provider.CloudMediaProvider;
+
+import java.io.FileNotFoundException;
+
+/**
+ * Implements a placeholder {@link CloudMediaProvider}.
+ */
+public class CloudProviderNoIntentFilter extends CloudMediaProvider {
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor onQueryMedia(Bundle extras) {
+ throw new UnsupportedOperationException("onQueryMedia not supported");
+ }
+
+ @Override
+ public Cursor onQueryDeletedMedia(Bundle extras) {
+ throw new UnsupportedOperationException("onQueryDeletedMedia not supported");
+ }
+
+ @Override
+ public AssetFileDescriptor onOpenPreview(String mediaId, Point size, Bundle extras,
+ CancellationSignal signal) throws FileNotFoundException {
+ throw new UnsupportedOperationException("onOpenPreview not supported");
+ }
+
+ @Override
+ public ParcelFileDescriptor onOpenMedia(String mediaId, Bundle extras,
+ CancellationSignal signal) throws FileNotFoundException {
+ throw new UnsupportedOperationException("onOpenMedia not supported");
+ }
+
+ @Override
+ public Bundle onGetMediaCollectionInfo(Bundle extras) {
+ throw new UnsupportedOperationException("onGetMediaCollectionInfo not supported");
+ }
+}
diff --git a/tests/src/com/android/providers/media/cloudproviders/CloudProviderNoPermission.java b/tests/src/com/android/providers/media/cloudproviders/CloudProviderNoPermission.java
new file mode 100644
index 0000000..9426b5d
--- /dev/null
+++ b/tests/src/com/android/providers/media/cloudproviders/CloudProviderNoPermission.java
@@ -0,0 +1,64 @@
+/*
+ * 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.cloudproviders;
+
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.provider.CloudMediaProvider;
+
+import java.io.FileNotFoundException;
+
+/**
+ * Implements a placeholder {@link CloudMediaProvider}.
+ */
+public class CloudProviderNoPermission extends CloudMediaProvider {
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor onQueryMedia(Bundle extras) {
+ throw new UnsupportedOperationException("onQueryMedia not supported");
+ }
+
+ @Override
+ public Cursor onQueryDeletedMedia(Bundle extras) {
+ throw new UnsupportedOperationException("onQueryDeletedMedia not supported");
+ }
+
+ @Override
+ public AssetFileDescriptor onOpenPreview(String mediaId, Point size, Bundle extras,
+ CancellationSignal signal) throws FileNotFoundException {
+ throw new UnsupportedOperationException("onOpenPreview not supported");
+ }
+
+ @Override
+ public ParcelFileDescriptor onOpenMedia(String mediaId, Bundle extras,
+ CancellationSignal signal) throws FileNotFoundException {
+ throw new UnsupportedOperationException("onOpenMedia not supported");
+ }
+
+ @Override
+ public Bundle onGetMediaCollectionInfo(Bundle extras) {
+ throw new UnsupportedOperationException("onGetMediaCollectionInfo not supported");
+ }
+}
diff --git a/tests/src/com/android/providers/media/cloudproviders/CloudProviderPrimary.java b/tests/src/com/android/providers/media/cloudproviders/CloudProviderPrimary.java
new file mode 100644
index 0000000..a007139
--- /dev/null
+++ b/tests/src/com/android/providers/media/cloudproviders/CloudProviderPrimary.java
@@ -0,0 +1,93 @@
+/*
+ * 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.cloudproviders;
+
+import static android.provider.CloudMediaProviderContract.MediaCollectionInfo;
+import static com.android.providers.media.PickerProviderMediaGenerator.MediaGenerator;
+
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.provider.CloudMediaProvider;
+
+import com.android.providers.media.PickerProviderMediaGenerator;
+import com.android.providers.media.photopicker.data.CloudProviderQueryExtras;
+
+import java.io.FileNotFoundException;
+
+/**
+ * Implements a cloud {@link CloudMediaProvider} interface over items generated with
+ * {@link MediaGenerator}
+ */
+public class CloudProviderPrimary extends CloudMediaProvider {
+ private static final String AUTHORITY =
+ "com.android.providers.media.photopicker.tests.cloud_primary";
+
+ private final MediaGenerator mMediaGenerator =
+ PickerProviderMediaGenerator.getMediaGenerator(AUTHORITY);
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor onQueryMedia(Bundle extras) {
+ final CloudProviderQueryExtras queryExtras =
+ CloudProviderQueryExtras.fromCloudMediaBundle(extras);
+
+ return mMediaGenerator.getMedia(queryExtras.getGeneration(), queryExtras.getAlbumId(),
+ queryExtras.getMimeType(), queryExtras.getSizeBytes());
+ }
+
+ @Override
+ public Cursor onQueryDeletedMedia(Bundle extras) {
+ final CloudProviderQueryExtras queryExtras =
+ CloudProviderQueryExtras.fromCloudMediaBundle(extras);
+
+ return mMediaGenerator.getDeletedMedia(queryExtras.getGeneration());
+ }
+
+ @Override
+ public Cursor onQueryAlbums(Bundle extras) {
+ final CloudProviderQueryExtras queryExtras =
+ CloudProviderQueryExtras.fromCloudMediaBundle(extras);
+
+ return mMediaGenerator.getAlbums(queryExtras.getMimeType(), queryExtras.getSizeBytes(),
+ /* isLocal */ false);
+ }
+
+ @Override
+ public AssetFileDescriptor onOpenPreview(String mediaId, Point size, Bundle extras,
+ CancellationSignal signal) throws FileNotFoundException {
+ throw new UnsupportedOperationException("onOpenPreview not supported");
+ }
+
+ @Override
+ public ParcelFileDescriptor onOpenMedia(String mediaId, Bundle extras,
+ CancellationSignal signal) throws FileNotFoundException {
+ throw new UnsupportedOperationException("onOpenMedia not supported");
+ }
+
+ @Override
+ public Bundle onGetMediaCollectionInfo(Bundle extras) {
+ return mMediaGenerator.getMediaCollectionInfo();
+ }
+}
diff --git a/tests/src/com/android/providers/media/cloudproviders/CloudProviderSecondary.java b/tests/src/com/android/providers/media/cloudproviders/CloudProviderSecondary.java
new file mode 100644
index 0000000..abcb92f
--- /dev/null
+++ b/tests/src/com/android/providers/media/cloudproviders/CloudProviderSecondary.java
@@ -0,0 +1,93 @@
+/*
+ * 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.cloudproviders;
+
+import static android.provider.CloudMediaProviderContract.MediaCollectionInfo;
+import static com.android.providers.media.PickerProviderMediaGenerator.MediaGenerator;
+
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.provider.CloudMediaProvider;
+
+import com.android.providers.media.PickerProviderMediaGenerator;
+import com.android.providers.media.photopicker.data.CloudProviderQueryExtras;
+
+import java.io.FileNotFoundException;
+
+/**
+ * Implements a cloud {@link CloudMediaProvider} interface over items generated with
+ * {@link MediaGenerator}
+ */
+public class CloudProviderSecondary extends CloudMediaProvider {
+ private static final String AUTHORITY =
+ "com.android.providers.media.photopicker.tests.cloud_secondary";
+
+ private final MediaGenerator mMediaGenerator =
+ PickerProviderMediaGenerator.getMediaGenerator(AUTHORITY);
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor onQueryMedia(Bundle extras) {
+ final CloudProviderQueryExtras queryExtras =
+ CloudProviderQueryExtras.fromCloudMediaBundle(extras);
+
+ return mMediaGenerator.getMedia(queryExtras.getGeneration(), queryExtras.getAlbumId(),
+ queryExtras.getMimeType(), queryExtras.getSizeBytes());
+ }
+
+ @Override
+ public Cursor onQueryDeletedMedia(Bundle extras) {
+ final CloudProviderQueryExtras queryExtras =
+ CloudProviderQueryExtras.fromCloudMediaBundle(extras);
+
+ return mMediaGenerator.getDeletedMedia(queryExtras.getGeneration());
+ }
+
+ @Override
+ public Cursor onQueryAlbums(Bundle extras) {
+ final CloudProviderQueryExtras queryExtras =
+ CloudProviderQueryExtras.fromCloudMediaBundle(extras);
+
+ return mMediaGenerator.getAlbums(queryExtras.getMimeType(), queryExtras.getSizeBytes(),
+ /* isLocal */ false);
+ }
+
+ @Override
+ public AssetFileDescriptor onOpenPreview(String mediaId, Point size, Bundle extras,
+ CancellationSignal signal) throws FileNotFoundException {
+ throw new UnsupportedOperationException("onOpenPreview not supported");
+ }
+
+ @Override
+ public ParcelFileDescriptor onOpenMedia(String mediaId, Bundle extras,
+ CancellationSignal signal) throws FileNotFoundException {
+ throw new UnsupportedOperationException("onOpenMedia not supported");
+ }
+
+ @Override
+ public Bundle onGetMediaCollectionInfo(Bundle extras) {
+ return mMediaGenerator.getMediaCollectionInfo();
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/AutoFitRecyclerViewTest.java b/tests/src/com/android/providers/media/photopicker/AutoFitRecyclerViewTest.java
new file mode 100644
index 0000000..77810a1
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/AutoFitRecyclerViewTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.view.View;
+
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.test.InstrumentationRegistry;
+
+import com.android.providers.media.photopicker.ui.AutoFitRecyclerView;
+
+import org.junit.Test;
+
+public class AutoFitRecyclerViewTest {
+
+ @Test
+ public void testMeasure_spanCountIsRounded() {
+ final int defaultSpanCount = 2;
+ final int size = 520;
+ final int columnWidth = 200;
+ final int measureSpec = View.MeasureSpec.makeMeasureSpec(size, View.MeasureSpec.EXACTLY);
+ final Context context = InstrumentationRegistry.getTargetContext();
+ AutoFitRecyclerView recyclerView = new AutoFitRecyclerView(context);
+ final GridLayoutManager gridLayoutManager = new GridLayoutManager(context,
+ defaultSpanCount);
+
+ assertThat(gridLayoutManager.getSpanCount()).isEqualTo(defaultSpanCount);
+
+ recyclerView.setLayoutManager(gridLayoutManager);
+ recyclerView.setColumnWidth(columnWidth);
+
+ recyclerView.onMeasure(measureSpec, measureSpec);
+
+ // The calculated count is 2.6. The result of Math.round should be 3
+ assertThat(gridLayoutManager.getSpanCount()).isEqualTo(3);
+ }
+
+ @Test
+ public void testSetMinimumSpanCount_resultSmallerThanMinSpanCount() {
+ final int defaultSpanCount = 1;
+ final int minSpanCount = 3;
+ final int size = 1200;
+ final int columnWidth = size;
+ final int measureSpec = View.MeasureSpec.makeMeasureSpec(size, View.MeasureSpec.EXACTLY);
+ final Context context = InstrumentationRegistry.getTargetContext();
+ AutoFitRecyclerView recyclerView = new AutoFitRecyclerView(context);
+ final GridLayoutManager gridLayoutManager = new GridLayoutManager(context,
+ defaultSpanCount);
+
+ assertThat(gridLayoutManager.getSpanCount()).isEqualTo(defaultSpanCount);
+
+ recyclerView.setLayoutManager(gridLayoutManager);
+ recyclerView.setMinimumSpanCount(minSpanCount);
+ recyclerView.setColumnWidth(columnWidth);
+
+ recyclerView.onMeasure(measureSpec, measureSpec);
+
+ // The column width equals the measured width, the calculated count is 1. It is smaller than
+ // minSpanCount, we expected the span count equals minSpanCount.
+ assertThat(gridLayoutManager.getSpanCount()).isEqualTo(minSpanCount);
+ }
+
+ @Test
+ public void testSetMinimumSpanCount_resultLargerThanMinSpanCount() {
+ final int defaultSpanCount = 1;
+ final int minSpanCount = 3;
+ final int size = 1200;
+ final int columnWidth = 300;
+ final int expectedSpanCount = size / columnWidth;
+ final int measureSpec = View.MeasureSpec.makeMeasureSpec(size, View.MeasureSpec.EXACTLY);
+ final Context context = InstrumentationRegistry.getTargetContext();
+ AutoFitRecyclerView recyclerView = new AutoFitRecyclerView(context);
+ final GridLayoutManager gridLayoutManager = new GridLayoutManager(context,
+ defaultSpanCount);
+
+ assertThat(gridLayoutManager.getSpanCount()).isEqualTo(defaultSpanCount);
+
+ recyclerView.setLayoutManager(gridLayoutManager);
+ recyclerView.setMinimumSpanCount(minSpanCount);
+ recyclerView.setColumnWidth(columnWidth);
+
+ recyclerView.onMeasure(measureSpec, measureSpec);
+
+ // The calculated count is 4. It is larger than minSpanCount. Verify the span count equals
+ // expectedSpanCount.
+ assertThat(gridLayoutManager.getSpanCount()).isEqualTo(expectedSpanCount);
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/ItemsProviderTest.java b/tests/src/com/android/providers/media/photopicker/ItemsProviderTest.java
new file mode 100644
index 0000000..7608c92
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/ItemsProviderTest.java
@@ -0,0 +1,853 @@
+/*
+ * 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;
+
+import static android.provider.CloudMediaProviderContract.AlbumColumns;
+import static android.provider.CloudMediaProviderContract.AlbumColumns.ALBUM_ID_CAMERA;
+import static android.provider.CloudMediaProviderContract.AlbumColumns.ALBUM_ID_DOWNLOADS;
+import static android.provider.CloudMediaProviderContract.AlbumColumns.ALBUM_ID_FAVORITES;
+import static android.provider.CloudMediaProviderContract.AlbumColumns.ALBUM_ID_SCREENSHOTS;
+import static android.provider.CloudMediaProviderContract.AlbumColumns.ALBUM_ID_VIDEOS;
+import static android.provider.CloudMediaProviderContract.MediaColumns;
+import static android.provider.MediaStore.VOLUME_EXTERNAL;
+
+import static com.android.providers.media.util.MimeUtils.isImageMimeType;
+import static com.android.providers.media.util.MimeUtils.isVideoMimeType;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.Manifest;
+import android.app.UiAutomation;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.provider.MediaStore;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.providers.media.photopicker.data.ItemsProvider;
+import com.android.providers.media.photopicker.data.model.Category;
+import com.android.providers.media.photopicker.data.model.UserId;
+import com.android.providers.media.scan.MediaScannerTest.IsolatedContext;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ItemsProviderTest {
+
+ /**
+ * To help avoid flaky tests, give ourselves a unique nonce to be used for
+ * all filesystem paths, so that we don't risk conflicting with previous
+ * test runs.
+ */
+ private static final String NONCE = String.valueOf(System.nanoTime());
+ private static final String TAG = "ItemsProviderTest";
+ private static final String VIDEO_FILE_NAME = TAG + "_file_" + NONCE + ".mp4";
+ private static final String IMAGE_FILE_NAME = TAG + "_file_" + NONCE + ".jpg";
+ private static final String HIDDEN_DIR_NAME = TAG + "_hidden_dir_" + NONCE;
+
+ private ContentResolver mIsolatedResolver;
+ private ItemsProvider mItemsProvider;
+
+ @Before
+ public void setUp() {
+ final UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation();
+
+ uiAutomation.adoptShellPermissionIdentity(Manifest.permission.LOG_COMPAT_CHANGE,
+ Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
+ Manifest.permission.READ_DEVICE_CONFIG,
+ Manifest.permission.INTERACT_ACROSS_USERS);
+
+ // Remove sync delay to avoid flaky tests
+ final String setSyncDelayCommand =
+ "device_config put storage pickerdb.default_sync_delay_ms 0";
+ uiAutomation.executeShellCommand(setSyncDelayCommand);
+
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final Context isolatedContext
+ = new IsolatedContext(context, "databases", /*asFuseThread*/ false);
+ mIsolatedResolver = isolatedContext.getContentResolver();
+ mItemsProvider = new ItemsProvider(isolatedContext);
+
+ // Wait for MediaStore to be Idle to reduce flakes caused by database updates
+ MediaStore.waitForIdle(mIsolatedResolver);
+ }
+
+ /**
+ * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
+ * {@link #ALBUM_ID_CAMERA}.
+ */
+ @Test
+ public void testGetCategories_camera() throws Exception {
+ Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
+ assertThat(c.getCount()).isEqualTo(0);
+
+ // Create 1 image file in Camera dir to test
+ // {@link ItemsProvider#getCategories(String, UserId)}.
+ final File cameraDir = getCameraDir();
+ File imageFile = assertCreateNewImage(cameraDir);
+ try {
+ assertGetCategoriesMatchSingle(ALBUM_ID_CAMERA, /* numberOfItems */ 1);
+ } finally {
+ imageFile.delete();
+ }
+ }
+
+ /**
+ * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
+ * {@link #ALBUM_ID_CAMERA}.
+ */
+ @Test
+ public void testGetCategories_not_camera() throws Exception {
+ Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
+ assertThat(c.getCount()).isEqualTo(0);
+
+ // negative test case: image file which should not be returned in Camera category
+ final File picturesDir = getPicturesDir();
+ File nonCameraImageFile = assertCreateNewImage(picturesDir);
+ try {
+ assertGetCategoriesMatchSingle(ALBUM_ID_CAMERA, /* numberOfItems */ 0);
+ } finally {
+ nonCameraImageFile.delete();
+ }
+ }
+
+ /**
+ * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
+ * {@link #ALBUM_ID_VIDEOS}.
+ */
+ @Test
+ public void testGetCategories_videos() throws Exception {
+ Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
+ assertThat(c.getCount()).isEqualTo(0);
+
+ // Create 1 video file in Movies dir to test
+ // {@link ItemsProvider#getCategories(String, UserId)}.
+ final File moviesDir = getMoviesDir();
+ File videoFile = assertCreateNewVideo(moviesDir);
+ try {
+ assertGetCategoriesMatchSingle(ALBUM_ID_VIDEOS, /* numberOfItems */ 1);
+ } finally {
+ videoFile.delete();
+ }
+ }
+
+ /**
+ * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
+ * {@link #ALBUM_ID_VIDEOS}.
+ */
+ @Test
+ public void testGetCategories_not_videos() throws Exception {
+ Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
+ assertThat(c.getCount()).isEqualTo(0);
+
+ // negative test case: image file which should not be returned in Videos category
+ final File picturesDir = getPicturesDir();
+ File imageFile = assertCreateNewImage(picturesDir);
+ try {
+ assertGetCategoriesMatchSingle(ALBUM_ID_VIDEOS, /* numberOfItems */ 0);
+ } finally {
+ imageFile.delete();
+ }
+ }
+
+ /**
+ * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
+ * {@link #ALBUM_ID_SCREENSHOTS}.
+ */
+ @Test
+ public void testGetCategories_screenshots() throws Exception {
+ Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
+ assertThat(c.getCount()).isEqualTo(0);
+
+ // Create 1 image file in Screenshots dir to test
+ // {@link ItemsProvider#getCategories(String, UserId)}
+ final File screenshotsDir = getScreenshotsDir();
+ File imageFile = assertCreateNewImage(screenshotsDir);
+ // Create 1 image file in Screenshots dir of Downloads dir
+ // {@link ItemsProvider#getCategories(String, UserId)}
+ final File screenshotsDirInDownloadsDir = getScreenshotsDirFromDownloadsDir();
+ File imageFileInScreenshotDirInDownloads =
+ assertCreateNewImage(screenshotsDirInDownloadsDir);
+ try {
+ assertGetCategoriesMatchMultiple(ALBUM_ID_SCREENSHOTS,
+ ALBUM_ID_DOWNLOADS, /* numberOfItemsInScreenshots */ 2,
+ /* numberOfItemsInDownloads */ 1);
+ } finally {
+ imageFile.delete();
+ imageFileInScreenshotDirInDownloads.delete();
+ }
+ }
+
+ /**
+ * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
+ * {@link #ALBUM_ID_SCREENSHOTS}.
+ */
+ @Test
+ public void testGetCategories_not_screenshots() throws Exception {
+ Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
+ assertThat(c.getCount()).isEqualTo(0);
+
+ // negative test case: image file which should not be returned in Screenshots category
+ final File cameraDir = getCameraDir();
+ File imageFile = assertCreateNewImage(cameraDir);
+ try {
+ assertGetCategoriesMatchSingle(ALBUM_ID_SCREENSHOTS, /* numberOfItems */ 0);
+ } finally {
+ imageFile.delete();
+ }
+ }
+
+ /**
+ * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
+ * {@link AlbumColumns#ALBUM_ID_FAVORITES}.
+ */
+ @Test
+ public void testGetCategories_favorites() throws Exception {
+ Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
+ assertThat(c.getCount()).isEqualTo(0);
+
+ // positive test case: image file which should be returned in favorites category
+ final File picturesDir = getPicturesDir();
+ final File imageFile = assertCreateNewImage(picturesDir);
+ setIsFavorite(imageFile);
+ try {
+ assertGetCategoriesMatchSingle(ALBUM_ID_FAVORITES, /* numberOfItems */1);
+ } finally {
+ imageFile.delete();
+ }
+ }
+
+ /**
+ * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
+ * {@link AlbumColumns#ALBUM_ID_FAVORITES}.
+ */
+ @Test
+ public void testGetCategories_not_favorites() throws Exception {
+ Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
+ assertThat(c.getCount()).isEqualTo(0);
+
+ // negative test case: image file which should not be returned in favorites category
+ final File picturesDir = getPicturesDir();
+ final File nonFavImageFile = assertCreateNewImage(picturesDir);
+ try {
+ assertGetCategoriesMatchSingle(ALBUM_ID_FAVORITES, /* numberOfItems */ 0);
+ } finally {
+ nonFavImageFile.delete();
+ }
+ }
+
+ /**
+ * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
+ * {@link #ALBUM_ID_DOWNLOADS}.
+ */
+ @Test
+ public void testGetCategories_downloads() throws Exception {
+ Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
+ assertThat(c.getCount()).isEqualTo(0);
+
+ // Create 1 image file in Downloads dir to test
+ // {@link ItemsProvider#getCategories(String, UserId)}.
+ final File downloadsDir = getDownloadsDir();
+ final File imageFile = assertCreateNewImage(downloadsDir);
+ try {
+ assertGetCategoriesMatchSingle(ALBUM_ID_DOWNLOADS, /* numberOfItems */ 1);
+ } finally {
+ imageFile.delete();
+ }
+ }
+
+ /**
+ * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
+ * {@link #ALBUM_ID_DOWNLOADS}.
+ */
+ @Test
+ public void testGetCategories_not_downloads() throws Exception {
+ Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
+ assertThat(c.getCount()).isEqualTo(0);
+
+ // negative test case: image file which should not be returned in Downloads category
+ final File picturesDir = getPicturesDir();
+ final File nonDownloadsImageFile = assertCreateNewImage(picturesDir);
+ try {
+ assertGetCategoriesMatchSingle(ALBUM_ID_DOWNLOADS, /* numberOfItems */ 0);
+ } finally {
+ nonDownloadsImageFile.delete();
+ }
+ }
+
+ /**
+ * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
+ * {@link #ALBUM_ID_CAMERA} and {@link #ALBUM_ID_VIDEOS}.
+ */
+ @Test
+ public void testGetCategories_camera_and_videos() throws Exception {
+ Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
+ assertThat(c.getCount()).isEqualTo(0);
+
+ // Create 1 video file in Camera dir to test
+ // {@link ItemsProvider#getCategories(String, UserId)}.
+ final File cameraDir = getCameraDir();
+ File videoFile = assertCreateNewVideo(cameraDir);
+ try {
+ assertGetCategoriesMatchMultiple(ALBUM_ID_CAMERA, ALBUM_ID_VIDEOS,
+ /* numberOfItemsInCamera */ 1,
+ /* numberOfItemsInVideos */ 1);
+ } finally {
+ videoFile.delete();
+ }
+ }
+
+ /**
+ * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
+ * {@link AlbumColumns#ALBUM_ID_SCREENSHOTS} and {@link AlbumColumns#ALBUM_ID_FAVORITES}.
+ */
+ @Test
+ public void testGetCategories_screenshots_and_favorites() throws Exception {
+ Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
+ assertThat(c.getCount()).isEqualTo(0);
+
+ // Create 1 image file in Screenshots dir to test
+ // {@link ItemsProvider#getCategories(String, UserId)}
+ final File screenshotsDir = getScreenshotsDir();
+ File imageFile = assertCreateNewImage(screenshotsDir);
+ setIsFavorite(imageFile);
+ try {
+ assertGetCategoriesMatchMultiple(ALBUM_ID_SCREENSHOTS,
+ ALBUM_ID_FAVORITES,
+ /* numberOfItemsInScreenshots */ 1,
+ /* numberOfItemsInFavorites */ 1);
+ } finally {
+ imageFile.delete();
+ }
+ }
+
+ /**
+ * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
+ * {@link AlbumColumns#ALBUM_ID_DOWNLOADS} and {@link AlbumColumns#ALBUM_ID_FAVORITES}.
+ */
+ @Test
+ public void testGetCategories_downloads_and_favorites() throws Exception {
+ Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
+ assertThat(c.getCount()).isEqualTo(0);
+
+ // Create 1 image file in Screenshots dir to test
+ // {@link ItemsProvider#getCategories(String, UserId)}
+ final File downloadsDir = getDownloadsDir();
+ File imageFile = assertCreateNewImage(downloadsDir);
+ setIsFavorite(imageFile);
+ try {
+ assertGetCategoriesMatchMultiple(ALBUM_ID_DOWNLOADS,
+ ALBUM_ID_FAVORITES,
+ /* numberOfItemsInScreenshots */ 1,
+ /* numberOfItemsInFavorites */ 1);
+ } finally {
+ imageFile.delete();
+ }
+ }
+
+ /**
+ * Tests {@link ItemsProvider#getItems(String, int, int, String, UserId)} to return all
+ * images and videos.
+ */
+ @Test
+ public void testGetItems() throws Exception {
+ // Create 1 image and 1 video file to test
+ // {@link ItemsProvider#getItems(String, int, int, String, UserId)}.
+ // Both files should be returned.
+ File imageFile = assertCreateNewImage();
+ File videoFile = assertCreateNewVideo();
+ try {
+ final Cursor res = mItemsProvider.getItems(Category.DEFAULT, /* offset */ 0,
+ /* limit */ -1, /* mimeType */ null, /* userId */ null);
+ assertThat(res).isNotNull();
+ assertThat(res.getCount()).isEqualTo(2);
+
+ assertThatOnlyImagesVideos(res);
+ // Reset the cursor back. Cursor#moveToPosition(-1) will reset the position to -1,
+ // but since there is no such valid cursor position, it returns false.
+ assertThat(res.moveToPosition(-1)).isFalse();
+ assertThatAllImagesVideos(res.getCount());
+ } finally {
+ imageFile.delete();
+ videoFile.delete();
+ }
+ }
+
+ @Test
+ public void testGetItems_sortOrder() throws Exception {
+ try {
+ final long timeNow = System.nanoTime() / 1000;
+ final Uri imageFileDateNowPlus1Uri = prepareFileAndGetUri(
+ new File(getDownloadsDir(), "latest_" + IMAGE_FILE_NAME), timeNow + 1000);
+ final Uri imageFileDateNowUri
+ = prepareFileAndGetUri(new File(getDcimDir(), IMAGE_FILE_NAME), timeNow);
+ final Uri videoFileDateNowUri
+ = prepareFileAndGetUri(new File(getCameraDir(), VIDEO_FILE_NAME), timeNow);
+
+ // This is the list of uris based on the expected sort order of items returned by
+ // ItemsProvider#getItems
+ List<Uri> uris = new ArrayList<>();
+ // This is the latest image file
+ uris.add(imageFileDateNowPlus1Uri);
+ // Video file was scanned after image file, hence has higher _id than image file
+ uris.add(videoFileDateNowUri);
+ uris.add(imageFileDateNowUri);
+
+ try (Cursor cursor = mItemsProvider.getItems(Category.DEFAULT, /* offset */ 0,
+ /* limit */ -1, /* mimeType */ null, /* userId */ null)) {
+ assertThat(cursor).isNotNull();
+
+ final int expectedCount = uris.size();
+ assertThat(cursor.getCount()).isEqualTo(expectedCount);
+
+ int rowNum = 0;
+ assertThat(cursor.moveToFirst()).isTrue();
+ final int idColumnIndex = cursor.getColumnIndexOrThrow(MediaColumns.ID);
+ while (rowNum < expectedCount) {
+ assertWithMessage("id at row:" + rowNum + " is expected to be"
+ + " same as id in " + uris.get(rowNum))
+ .that(String.valueOf(cursor.getLong(idColumnIndex)))
+ .isEqualTo(uris.get(rowNum).getLastPathSegment());
+ cursor.moveToNext();
+ rowNum++;
+ }
+ }
+ } finally {
+ deleteAllFilesNoThrow();
+ }
+ }
+
+ /**
+ * Tests {@link {@link ItemsProvider#getItems(String, int, int, String, UserId)}} does not
+ * return hidden images/videos.
+ */
+ @Test
+ public void testGetItems_nonMedia() throws Exception {
+ // Create 1 image and 1 video file in a hidden dir to test
+ // {@link ItemsProvider#getItems(String, int, int, String, UserId)}.
+ // Both should not be returned.
+ File hiddenDir = createHiddenDir();
+ File imageFileHidden = assertCreateNewImage(hiddenDir);
+ File videoFileHidden = assertCreateNewVideo(hiddenDir);
+ try {
+ final Cursor res = mItemsProvider.getItems(Category.DEFAULT, /* offset */ 0,
+ /* limit */ -1, /* mimeType */ null, /* userId */ null);
+ assertThat(res).isNotNull();
+ assertThat(res.getCount()).isEqualTo(0);
+ } finally {
+ imageFileHidden.delete();
+ videoFileHidden.delete();
+ hiddenDir.delete();
+ }
+ }
+
+ /**
+ * Tests {@link ItemsProvider#getItems(String, int, int, String, UserId)} to return all
+ * images and videos based on the mimeType. Image mimeType should only return images.
+ */
+ @Test
+ public void testGetItemsImages() throws Exception {
+ // Create 1 image and 1 video file to test
+ // {@link ItemsProvider#getItems(String, int, int, String, UserId)}.
+ // Only 1 should be returned.
+ File imageFile = assertCreateNewImage();
+ File videoFile = assertCreateNewVideo();
+ try {
+ final Cursor res = mItemsProvider.getItems(Category.DEFAULT, /* offset */ 0,
+ /* limit */ -1, /* mimeType */ "image/*", /* userId */ null);
+ assertThat(res).isNotNull();
+ assertThat(res.getCount()).isEqualTo(1);
+
+ assertThatOnlyImages(res);
+ assertThatAllImages(res.getCount());
+ } finally {
+ imageFile.delete();
+ videoFile.delete();
+ }
+ }
+
+ /**
+ * Tests {@link ItemsProvider#getItems(String, int, int, String, UserId)} to return all
+ * images and videos based on the mimeType. Image mimeType should only return images.
+ */
+ @Test
+ public void testGetItemsImages_png() throws Exception {
+ // Create a jpg file image. Tests negative use case, this should not be returned below.
+ File imageFile = assertCreateNewImage();
+ try {
+ final Cursor res = mItemsProvider.getItems(Category.DEFAULT, /* offset */ 0,
+ /* limit */ -1, /* mimeType */ "image/png", /* userId */ null);
+ assertThat(res).isNotNull();
+ assertThat(res.getCount()).isEqualTo(0);
+ } finally {
+ imageFile.delete();
+ }
+ }
+
+ /**
+ * Tests {@link ItemsProvider#getItems(String, int, int, String, UserId)} does not return
+ * hidden images/videos.
+ */
+ @Test
+ public void testGetItemsImages_nonMedia() throws Exception {
+ // Create 1 image and 1 video file in a hidden dir to test
+ // {@link ItemsProvider#getItems(String, int, int, String)}.
+ // Both should not be returned.
+ File hiddenDir = createHiddenDir();
+ File imageFileHidden = assertCreateNewImage(hiddenDir);
+ File videoFileHidden = assertCreateNewVideo(hiddenDir);
+ try {
+ final Cursor res = mItemsProvider.getItems(Category.DEFAULT, /* offset */ 0,
+ /* limit */ -1, /* mimeType */ "image/*", /* userId */ null);
+ assertThat(res).isNotNull();
+ assertThat(res.getCount()).isEqualTo(0);
+ } finally {
+ imageFileHidden.delete();
+ videoFileHidden.delete();
+ hiddenDir.delete();
+ }
+ }
+
+ /**
+ * Tests {@link ItemsProvider#getItems(String, int, int, String, UserId)} to return all
+ * images and videos based on the mimeType. Video mimeType should only return videos.
+ */
+ @Test
+ public void testGetItemsVideos() throws Exception {
+ // Create 1 image and 1 video file to test
+ // {@link ItemsProvider#getItems(String, int, int, String)}.
+ // Only 1 should be returned.
+ File imageFile = assertCreateNewImage();
+ File videoFile = assertCreateNewVideo();
+ try {
+ final Cursor res = mItemsProvider.getItems(Category.DEFAULT, /* offset */ 0,
+ /* limit */ -1, /* mimeType */ "video/*", /* userId */ null);
+ assertThat(res).isNotNull();
+ assertThat(res.getCount()).isEqualTo(1);
+
+ assertThatOnlyVideos(res);
+ assertThatAllVideos(res.getCount());
+ } finally {
+ imageFile.delete();
+ videoFile.delete();
+ }
+ }
+
+ /**
+ * Tests {@link ItemsProvider#getItems(String, int, int, String, UserId)} to return all
+ * images and videos based on the mimeType. Image mimeType should only return images.
+ */
+ @Test
+ public void testGetItemsVideos_mp4() throws Exception {
+ // Create a mp4 video file. Tests positive use case, this should be returned below.
+ File videoFile = assertCreateNewVideo();
+ try {
+ final Cursor res = mItemsProvider.getItems(Category.DEFAULT, /* offset */ 0,
+ /* limit */ -1, /* mimeType */ "video/mp4", /* userId */ null);
+ assertThat(res).isNotNull();
+ assertThat(res.getCount()).isEqualTo(1);
+ } finally {
+ videoFile.delete();
+ }
+ }
+
+ /**
+ * Tests {@link ItemsProvider#getItems(String, int, int, String, UserId)} does not return
+ * hidden images/videos.
+ */
+ @Test
+ public void testGetItemsVideos_nonMedia() throws Exception {
+ // Create 1 image and 1 video file in a hidden dir to test the API.
+ // Both should not be returned.
+ File hiddenDir = createHiddenDir();
+ File imageFileHidden = assertCreateNewImage(hiddenDir);
+ File videoFileHidden = assertCreateNewVideo(hiddenDir);
+ try {
+ final Cursor res = mItemsProvider.getItems(Category.DEFAULT, /* offset */ 0,
+ /* limit */ -1, /* mimeType */ "video/*", /* userId */ null);
+ assertThat(res).isNotNull();
+ assertThat(res.getCount()).isEqualTo(0);
+ } finally {
+ imageFileHidden.delete();
+ videoFileHidden.delete();
+ hiddenDir.delete();
+ }
+ }
+
+ private void assertGetCategoriesMatchSingle(String expectedCategoryName,
+ int expectedNumberOfItems) throws Exception {
+ if (expectedNumberOfItems == 0) {
+ assertCategoriesNoMatch(expectedCategoryName);
+ return;
+ }
+
+ Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
+ assertThat(c).isNotNull();
+ assertThat(c.getCount()).isEqualTo(1);
+
+ // Assert that only expected category is returned and has expectedNumberOfItems items in it
+ assertThat(c.moveToFirst()).isTrue();
+ final int nameColumnIndex = c.getColumnIndexOrThrow(AlbumColumns.DISPLAY_NAME);
+ final int numOfItemsColumnIndex = c.getColumnIndexOrThrow(AlbumColumns.MEDIA_COUNT);
+ final int coverIdIndex = c.getColumnIndexOrThrow(AlbumColumns.MEDIA_COVER_ID);
+
+ final String categoryName = c.getString(nameColumnIndex);
+ final int numOfItems = c.getInt(numOfItemsColumnIndex);
+ final Uri coverUri = ItemsProvider.getItemsUri(c.getString(coverIdIndex),
+ PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY, UserId.CURRENT_USER);
+
+ assertThat(categoryName).isEqualTo(expectedCategoryName);
+ assertThat(numOfItems).isEqualTo(expectedNumberOfItems);
+ assertCategoryUriIsValid(coverUri);
+ }
+
+ private void assertCategoryUriIsValid(Uri uri) throws Exception {
+ try (AssetFileDescriptor fd1 = mIsolatedResolver.openTypedAssetFile(uri, "image/*",
+ null, null)) {
+ assertThat(fd1).isNotNull();
+ }
+ try (ParcelFileDescriptor fd2 = mIsolatedResolver.openFileDescriptor(uri, "r")) {
+ assertThat(fd2).isNotNull();
+ }
+ }
+
+ private void assertCategoriesNoMatch(String expectedCategoryName) {
+ Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
+ while (c != null && c.moveToNext()) {
+ final int nameColumnIndex = c.getColumnIndexOrThrow(AlbumColumns.DISPLAY_NAME);
+ final String categoryName = c.getString(nameColumnIndex);
+ assertThat(categoryName).isNotEqualTo(expectedCategoryName);
+ }
+ }
+
+ private void assertGetCategoriesMatchMultiple(String category1, String category2,
+ int numberOfItems1, int numberOfItems2) {
+ Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
+ assertThat(c).isNotNull();
+ assertThat(c.getCount()).isEqualTo(2);
+
+ // Assert that category1 and category2 is returned and has numberOfItems1 and
+ // numberOfItems2 items in them respectively.
+ boolean isCategory1Returned = false;
+ boolean isCategory2Returned = false;
+ while (c.moveToNext()) {
+ final int nameColumnIndex = c.getColumnIndexOrThrow(AlbumColumns.DISPLAY_NAME);
+ final int numOfItemsColumnIndex = c.getColumnIndexOrThrow(
+ AlbumColumns.MEDIA_COUNT);
+
+ final String categoryName = c.getString(nameColumnIndex);
+ final int numOfItems = c.getInt(numOfItemsColumnIndex);
+
+
+ if (categoryName.equals(category1)) {
+ isCategory1Returned = true;
+ assertThat(numOfItems).isEqualTo(numberOfItems1);
+ } else if (categoryName.equals(category2)) {
+ isCategory2Returned = true;
+ assertThat(numOfItems).isEqualTo(numberOfItems2);
+ }
+ }
+
+ assertThat(isCategory1Returned).isTrue();
+ assertThat(isCategory2Returned).isTrue();
+ }
+
+ private void setIsFavorite(File file) {
+ final Uri uri = MediaStore.scanFile(mIsolatedResolver, file);
+ final ContentValues values = new ContentValues();
+ values.put(MediaStore.MediaColumns.IS_FAVORITE, 1);
+ // Assert that 1 row corresponding to this file is updated.
+ assertThat(mIsolatedResolver.update(uri, values, null)).isEqualTo(1);
+ // Wait for MediaStore to be Idle to reduce flakes caused by database updates
+ MediaStore.waitForIdle(mIsolatedResolver);
+ }
+
+ private void assertThatOnlyImagesVideos(Cursor c) throws Exception {
+ while (c.moveToNext()) {
+ int mimeTypeColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.MIME_TYPE);
+ String mimeType = c.getString(mimeTypeColumn);
+ assertThat(isImageMimeType(mimeType) || isVideoMimeType(mimeType)).isTrue();
+ }
+ }
+
+ private void assertThatOnlyImages(Cursor c) throws Exception {
+ while (c.moveToNext()) {
+ int mimeTypeColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.MIME_TYPE);
+ String mimeType = c.getString(mimeTypeColumn);
+ assertThat(isImageMimeType(mimeType)).isTrue();
+ }
+ }
+
+ private void assertThatOnlyVideos(Cursor c) throws Exception {
+ while (c.moveToNext()) {
+ int mimeTypeColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.MIME_TYPE);
+ String mimeType = c.getString(mimeTypeColumn);
+ assertThat(isVideoMimeType(mimeType)).isTrue();
+ }
+ }
+
+ private void assertThatAllImagesVideos(int count) {
+ int countOfImages = getCountOfMediaStoreImages();
+ int countOfVideos = getCountOfMediaStoreVideos();
+ assertThat(count).isEqualTo(countOfImages + countOfVideos);
+ }
+
+ private void assertThatAllImages(int count) {
+ int countOfImages = getCountOfMediaStoreImages();
+ assertThat(count).isEqualTo(countOfImages);
+ }
+
+ private void assertThatAllVideos(int count) {
+ int countOfVideos = getCountOfMediaStoreVideos();
+ assertThat(count).isEqualTo(countOfVideos);
+ }
+
+ private int getCountOfMediaStoreImages() {
+ try (Cursor c = mIsolatedResolver.query(
+ MediaStore.Images.Media.getContentUri(VOLUME_EXTERNAL), null, null, null)) {
+ assertThat(c.moveToFirst()).isTrue();
+ return c.getCount();
+ }
+ }
+
+ private int getCountOfMediaStoreVideos() {
+ try (Cursor c = mIsolatedResolver.query(
+ MediaStore.Video.Media.getContentUri(VOLUME_EXTERNAL), null, null, null)) {
+ assertThat(c.moveToFirst()).isTrue();
+ return c.getCount();
+ }
+ }
+
+ private File assertCreateNewVideo(File dir) throws Exception {
+ return assertCreateNewFile(dir, VIDEO_FILE_NAME);
+ }
+
+ private File assertCreateNewImage(File dir) throws Exception {
+ return assertCreateNewFile(dir, IMAGE_FILE_NAME);
+ }
+
+ private File assertCreateNewVideo() throws Exception {
+ return assertCreateNewFile(getDownloadsDir(), VIDEO_FILE_NAME);
+ }
+
+ private File assertCreateNewImage() throws Exception {
+ return assertCreateNewFile(getDownloadsDir(), IMAGE_FILE_NAME);
+ }
+
+ private File assertCreateNewFile(File parentDir, String fileName) throws Exception {
+ final File file = new File(parentDir, fileName);
+ prepareFileAndGetUri(file, /* lastModifiedTime */ -1);
+
+ return file;
+ }
+
+ private Uri prepareFileAndGetUri(File file, long lastModifiedTime) throws IOException {
+ ensureParentExists(file.getParentFile());
+
+ assertThat(file.createNewFile()).isTrue();
+
+ // Write 1 byte because 0byte files are not valid in the picker db
+ try (FileOutputStream fos = new FileOutputStream(file)) {
+ fos.write(1);
+ }
+
+ if (lastModifiedTime != -1) {
+ file.setLastModified(lastModifiedTime);
+ }
+
+ final Uri uri = MediaStore.scanFile(mIsolatedResolver, file);
+ assertWithMessage("Uri obtained by scanning file " + file)
+ .that(uri).isNotNull();
+ // Wait for picker db sync
+ MediaStore.waitForIdle(mIsolatedResolver);
+
+ return uri;
+ }
+
+ private void ensureParentExists(File parent) {
+ if (!parent.exists()) {
+ parent.mkdirs();
+ }
+ assertThat(parent.exists()).isTrue();
+ }
+
+ private File getDownloadsDir() {
+ return new File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_DOWNLOADS);
+ }
+
+ private File getDcimDir() {
+ return new File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_DCIM);
+ }
+
+ private File getPicturesDir() {
+ return new File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_PICTURES);
+ }
+
+ private File getMoviesDir() {
+ return new File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_MOVIES);
+ }
+
+ private File getCameraDir() {
+ return new File(getDcimDir(), "Camera");
+ }
+
+ private File getScreenshotsDir() {
+ return new File(getPicturesDir(), Environment.DIRECTORY_SCREENSHOTS);
+ }
+
+ private File getScreenshotsDirFromDownloadsDir() {
+ return new File(getDownloadsDir(), Environment.DIRECTORY_SCREENSHOTS);
+ }
+
+ private File createHiddenDir() throws Exception {
+ File parentDir = new File(Environment.getExternalStorageDirectory(),
+ Environment.DIRECTORY_DOWNLOADS);
+ File dir = new File(parentDir, HIDDEN_DIR_NAME);
+ dir.mkdirs();
+ File nomedia = new File(dir, ".nomedia");
+ nomedia.createNewFile();
+
+ MediaStore.scanFile(mIsolatedResolver, nomedia);
+
+ return dir;
+ }
+
+ private void deleteAllFilesNoThrow() {
+ try (Cursor c = mIsolatedResolver.query(
+ MediaStore.Files.getContentUri(VOLUME_EXTERNAL),
+ new String[] {MediaStore.MediaColumns.DATA}, null, null)) {
+ while(c.moveToNext()) {
+ (new File(c.getString(
+ c.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA)))).delete();
+ }
+ }
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/LocalProvider.java b/tests/src/com/android/providers/media/photopicker/LocalProvider.java
new file mode 100644
index 0000000..3916c9d
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/LocalProvider.java
@@ -0,0 +1,92 @@
+/*
+ * 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;
+
+import static android.provider.CloudMediaProviderContract.MediaCollectionInfo;
+import static com.android.providers.media.PickerProviderMediaGenerator.MediaGenerator;
+
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.provider.CloudMediaProvider;
+
+import com.android.providers.media.PickerProviderMediaGenerator;
+import com.android.providers.media.photopicker.data.CloudProviderQueryExtras;
+
+import java.io.FileNotFoundException;
+
+/**
+ * Implements the a local {@link CloudMediaProvider} interface over items generated with
+ * {@link MediaGenerator}
+ */
+public class LocalProvider extends CloudMediaProvider {
+ public static final String AUTHORITY = "com.android.providers.media.photopicker.tests.local";
+
+ private final MediaGenerator mMediaGenerator =
+ PickerProviderMediaGenerator.getMediaGenerator(AUTHORITY);
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor onQueryMedia(Bundle extras) {
+ final CloudProviderQueryExtras queryExtras =
+ CloudProviderQueryExtras.fromCloudMediaBundle(extras);
+
+ return mMediaGenerator.getMedia(queryExtras.getGeneration(), queryExtras.getAlbumId(),
+ queryExtras.getMimeType(), queryExtras.getSizeBytes());
+ }
+
+ @Override
+ public Cursor onQueryDeletedMedia(Bundle extras) {
+ final CloudProviderQueryExtras queryExtras =
+ CloudProviderQueryExtras.fromCloudMediaBundle(extras);
+
+ return mMediaGenerator.getDeletedMedia(queryExtras.getGeneration());
+ }
+
+ @Override
+ public Cursor onQueryAlbums(Bundle extras) {
+ final CloudProviderQueryExtras queryExtras =
+ CloudProviderQueryExtras.fromCloudMediaBundle(extras);
+
+ return mMediaGenerator.getAlbums(queryExtras.getMimeType(), queryExtras.getSizeBytes(),
+ /* isLocal */ true);
+ }
+
+ @Override
+ public AssetFileDescriptor onOpenPreview(String mediaId, Point size, Bundle extras,
+ CancellationSignal signal) throws FileNotFoundException {
+ throw new UnsupportedOperationException("onOpenPreview not supported");
+ }
+
+ @Override
+ public ParcelFileDescriptor onOpenMedia(String mediaId, Bundle extras,
+ CancellationSignal signal) throws FileNotFoundException {
+ throw new UnsupportedOperationException("onOpenMedia not supported");
+ }
+
+ @Override
+ public Bundle onGetMediaCollectionInfo(Bundle extras) {
+ return mMediaGenerator.getMediaCollectionInfo();
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/PickerDataLayerTest.java b/tests/src/com/android/providers/media/photopicker/PickerDataLayerTest.java
new file mode 100644
index 0000000..6931cd6
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/PickerDataLayerTest.java
@@ -0,0 +1,631 @@
+/*
+ * 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;
+
+import static android.provider.CloudMediaProviderContract.AlbumColumns.ALBUM_ID_FAVORITES;
+import static android.provider.CloudMediaProviderContract.AlbumColumns.ALBUM_ID_VIDEOS;
+
+import static com.android.providers.media.PickerProviderMediaGenerator.MediaGenerator;
+import static com.android.providers.media.photopicker.data.PickerDbFacade.QueryFilterBuilder.LONG_DEFAULT;
+import static com.android.providers.media.photopicker.data.PickerDbFacade.QueryFilterBuilder.STRING_DEFAULT;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.provider.CloudMediaProviderContract.AlbumColumns;
+import android.provider.CloudMediaProviderContract.MediaColumns;
+import android.provider.MediaStore;
+import android.util.Pair;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.modules.utils.BackgroundThread;
+import com.android.providers.media.PickerProviderMediaGenerator;
+import com.android.providers.media.photopicker.data.PickerDatabaseHelper;
+import com.android.providers.media.photopicker.data.PickerDbFacade;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class PickerDataLayerTest {
+ private static final String TAG = "PickerDataLayerTest";
+
+ private static final String LOCAL_PROVIDER_AUTHORITY =
+ "com.android.providers.media.photopicker.tests.local";
+ private static final String CLOUD_PRIMARY_PROVIDER_AUTHORITY =
+ "com.android.providers.media.photopicker.tests.cloud_primary";
+ private static final String CLOUD_SECONDARY_PROVIDER_AUTHORITY =
+ "com.android.providers.media.photopicker.tests.cloud_secondary";
+
+ private final MediaGenerator mLocalMediaGenerator =
+ PickerProviderMediaGenerator.getMediaGenerator(LOCAL_PROVIDER_AUTHORITY);
+ private final MediaGenerator mCloudPrimaryMediaGenerator =
+ PickerProviderMediaGenerator.getMediaGenerator(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ private final MediaGenerator mCloudSecondaryMediaGenerator =
+ PickerProviderMediaGenerator.getMediaGenerator(CLOUD_SECONDARY_PROVIDER_AUTHORITY);
+
+ private static final int DB_VERSION_1 = 1;
+ private static final String DB_NAME = "test_db";
+
+ private static final String LOCAL_ID_1 = "1";
+ private static final String LOCAL_ID_2 = "2";
+
+ private static final String CLOUD_ID_1 = "1";
+ private static final String CLOUD_ID_2 = "2";
+
+ private static final String ALBUM_ID_1 = "1";
+ private static final String ALBUM_ID_2 = "2";
+
+ private static final String MIME_TYPE_DEFAULT = STRING_DEFAULT;
+ private static final long SIZE_BYTES_DEFAULT = LONG_DEFAULT;
+
+ private static final Pair<String, String> LOCAL_ONLY_1 = Pair.create(LOCAL_ID_1, null);
+ private static final Pair<String, String> LOCAL_ONLY_2 = Pair.create(LOCAL_ID_2, null);
+ private static final Pair<String, String> CLOUD_ONLY_1 = Pair.create(null, CLOUD_ID_1);
+ private static final Pair<String, String> CLOUD_ONLY_2 = Pair.create(null, CLOUD_ID_2);
+
+ private static final String COLLECTION_1 = "1";
+
+ private static final String IMAGE_MIME_TYPE = "image/jpeg";
+ private static final String VIDEO_MIME_TYPE = "video/mp4";
+ private static final long SIZE_BYTES = 50;
+
+ private Context mContext;
+ private PickerDatabaseHelper mDbHelper;
+ private PickerDbFacade mFacade;
+ private PickerDataLayer mDataLayer;
+ private PickerSyncController mController;
+
+ @Before
+ public void setUp() {
+ mLocalMediaGenerator.resetAll();
+ mCloudPrimaryMediaGenerator.resetAll();
+ mCloudSecondaryMediaGenerator.resetAll();
+
+ mLocalMediaGenerator.setMediaCollectionId(COLLECTION_1);
+ mCloudPrimaryMediaGenerator.setMediaCollectionId(COLLECTION_1);
+ mCloudSecondaryMediaGenerator.setMediaCollectionId(COLLECTION_1);
+
+ mContext = InstrumentationRegistry.getTargetContext();
+
+ // Delete db so it's recreated on next access and previous test state is cleared
+ final File dbPath = mContext.getDatabasePath(DB_NAME);
+ dbPath.delete();
+
+ mDbHelper = new PickerDatabaseHelper(mContext, DB_NAME, DB_VERSION_1);
+ mFacade = new PickerDbFacade(mContext, LOCAL_PROVIDER_AUTHORITY, mDbHelper);
+ final String allowedCloudProviders = CLOUD_PRIMARY_PROVIDER_AUTHORITY + ","
+ + CLOUD_SECONDARY_PROVIDER_AUTHORITY;
+ mController = new PickerSyncController(mContext, mFacade, LOCAL_PROVIDER_AUTHORITY,
+ allowedCloudProviders, /* syncDelay */ 0);
+ mDataLayer = new PickerDataLayer(mContext, mFacade, mController);
+
+ // Set cloud provider to null to discard
+ mFacade.setCloudProvider(null);
+ }
+
+ @Test
+ public void testFetchMediaNoFilter() {
+ mController.setCloudProvider(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_1);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_1);
+
+ try (Cursor cr = mDataLayer.fetchMedia(buildDefaultQueryArgs())) {
+ assertThat(cr.getCount()).isEqualTo(2);
+
+ assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ }
+ }
+
+ @Test
+ public void testFetchMediaFavorites() {
+ mController.setCloudProvider(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_1, /* albumId */ null, VIDEO_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ true);
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_2, /* albumId */ null, IMAGE_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ false);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_1, /* albumId */ null, IMAGE_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES - 1,
+ /* isFavorite */ true);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_2, /* albumId */ null, IMAGE_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ false);
+
+ final Bundle defaultQueryArgs = buildDefaultQueryArgs();
+
+ try (Cursor cr = mDataLayer.fetchMedia(defaultQueryArgs)) {
+ assertThat(cr.getCount()).isEqualTo(4);
+ }
+
+ final Bundle favoriteQueryArgs = buildQueryArgs(ALBUM_ID_FAVORITES,
+ LOCAL_PROVIDER_AUTHORITY, MIME_TYPE_DEFAULT, SIZE_BYTES_DEFAULT);
+
+ try (Cursor cr = mDataLayer.fetchMedia(favoriteQueryArgs)) {
+ assertThat(cr.getCount()).isEqualTo(2);
+
+ assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ }
+ }
+
+ @Test
+ public void testFetchMediaFavoritesMimeTypeFilter() {
+ mController.setCloudProvider(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_1, /* albumId */ null, VIDEO_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ true);
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_2, /* albumId */ null, IMAGE_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ false);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_1, /* albumId */ null, IMAGE_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES - 1,
+ /* isFavorite */ true);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_2, /* albumId */ null, IMAGE_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ false);
+
+ final Bundle defaultQueryArgs = buildDefaultQueryArgs();
+
+ try (Cursor cr = mDataLayer.fetchMedia(defaultQueryArgs)) {
+ assertThat(cr.getCount()).isEqualTo(4);
+ }
+
+ final Bundle favoriteMimeTypeQueryArgs = buildQueryArgs(ALBUM_ID_FAVORITES,
+ LOCAL_PROVIDER_AUTHORITY, VIDEO_MIME_TYPE, SIZE_BYTES_DEFAULT);
+
+ try (Cursor cr = mDataLayer.fetchMedia(favoriteMimeTypeQueryArgs)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ }
+ }
+
+ @Test
+ public void testFetchMediaFavoritesSizeFilter() {
+ mController.setCloudProvider(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_1, /* albumId */ null, VIDEO_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ true);
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_2, /* albumId */ null, IMAGE_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ false);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_1, /* albumId */ null, IMAGE_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES - 1,
+ /* isFavorite */ true);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_2, /* albumId */ null, IMAGE_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ false);
+
+ final Bundle defaultQueryArgs = buildDefaultQueryArgs();
+
+ try (Cursor cr = mDataLayer.fetchMedia(defaultQueryArgs)) {
+ assertThat(cr.getCount()).isEqualTo(4);
+ }
+
+ final Bundle favoriteSizeQueryArgs = buildQueryArgs(ALBUM_ID_FAVORITES,
+ LOCAL_PROVIDER_AUTHORITY, MIME_TYPE_DEFAULT, SIZE_BYTES - 1);
+
+ try (Cursor cr = mDataLayer.fetchMedia(favoriteSizeQueryArgs)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+ }
+
+ @Test
+ public void testFetchMediaFavoritesMimeTypeAndSizeFilter() {
+ mController.setCloudProvider(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_1, /* albumId */ null, VIDEO_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ true);
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_2, /* albumId */ null, IMAGE_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ false);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_1, /* albumId */ null, IMAGE_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES - 1,
+ /* isFavorite */ true);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_2, /* albumId */ null, IMAGE_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ false);
+
+ final Bundle defaultQueryArgs = buildDefaultQueryArgs();
+
+ try (Cursor cr = mDataLayer.fetchMedia(defaultQueryArgs)) {
+ assertThat(cr.getCount()).isEqualTo(4);
+ }
+
+ final Bundle favoriteSizeAndMimeTypeQueryArgs = buildQueryArgs(ALBUM_ID_FAVORITES,
+ LOCAL_PROVIDER_AUTHORITY, VIDEO_MIME_TYPE, SIZE_BYTES - 1);
+
+ try (Cursor cr = mDataLayer.fetchMedia(favoriteSizeAndMimeTypeQueryArgs)) {
+ assertThat(cr.getCount()).isEqualTo(0);
+ }
+ }
+
+ @Test
+ public void testFetchMediaMimeTypeFilter() {
+ mController.setCloudProvider(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_1, /* albumId */ null, IMAGE_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ false);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_1, /* albumId */ null, VIDEO_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ false);
+
+ final Bundle queryArgs = buildQueryArgs(IMAGE_MIME_TYPE, SIZE_BYTES_DEFAULT);
+
+ try (Cursor cr = mDataLayer.fetchMedia(queryArgs)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ }
+ }
+
+ @Test
+ public void testFetchMediaSizeFilter() {
+ mController.setCloudProvider(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_1, /* albumId */ null, IMAGE_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES - 1,
+ /* isFavorite */ false);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_1, /* albumId */ null, IMAGE_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ false);
+
+ final Bundle queryArgs = buildQueryArgs(IMAGE_MIME_TYPE, SIZE_BYTES - 1);
+
+ try (Cursor cr = mDataLayer.fetchMedia(queryArgs)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ }
+ }
+
+ @Test
+ public void testFetchMediaMimeTypeAndSizeFilter() {
+ mController.setCloudProvider(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_1, /* albumId */ null, IMAGE_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES - 1,
+ /* isFavorite */ false);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_1, /* albumId */ null, VIDEO_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES - 1,
+ /* isFavorite */ false);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_2, /* albumId */ null, VIDEO_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ false);
+
+ final Bundle queryArgs = buildQueryArgs(VIDEO_MIME_TYPE, SIZE_BYTES - 1);
+
+ try (Cursor cr = mDataLayer.fetchMedia(queryArgs)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+ }
+
+ @Test
+ public void testFetchAlbumMedia() {
+ mController.setCloudProvider(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ mLocalMediaGenerator.createAlbum(ALBUM_ID_1);
+ mCloudPrimaryMediaGenerator.createAlbum(ALBUM_ID_2);
+
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_1, ALBUM_ID_1, IMAGE_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ false);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_1, ALBUM_ID_2, VIDEO_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ false);
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_2, /* albumId */ null, VIDEO_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ true);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_2, /* albumdId */ null, IMAGE_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ true);
+
+ final Bundle defaultQueryArgs = buildDefaultQueryArgs();
+
+ try (Cursor cr = mDataLayer.fetchAlbums(defaultQueryArgs)) {
+ assertThat(cr.getCount()).isEqualTo(4);
+
+ assertAlbumCursor(cr, ALBUM_ID_FAVORITES, LOCAL_PROVIDER_AUTHORITY);
+ assertAlbumCursor(cr, ALBUM_ID_VIDEOS, LOCAL_PROVIDER_AUTHORITY);
+ assertAlbumCursor(cr, ALBUM_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ assertAlbumCursor(cr, ALBUM_ID_2, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+
+ try (Cursor cr = mDataLayer.fetchMedia(defaultQueryArgs)) {
+ assertThat(cr.getCount()).isEqualTo(4);
+
+ assertCursor(cr, CLOUD_ID_2, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ assertCursor(cr, LOCAL_ID_2, LOCAL_PROVIDER_AUTHORITY);
+ assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ }
+
+ final Bundle localAlbumQueryArgs = buildQueryArgs(ALBUM_ID_1,
+ LOCAL_PROVIDER_AUTHORITY, MIME_TYPE_DEFAULT, SIZE_BYTES_DEFAULT);
+
+ final Bundle cloudAlbumQueryArgs = buildQueryArgs(ALBUM_ID_2,
+ CLOUD_PRIMARY_PROVIDER_AUTHORITY, MIME_TYPE_DEFAULT, SIZE_BYTES_DEFAULT);
+
+ final Bundle favoriteAlbumQueryArgs = buildQueryArgs(ALBUM_ID_FAVORITES,
+ LOCAL_PROVIDER_AUTHORITY, MIME_TYPE_DEFAULT, SIZE_BYTES_DEFAULT);
+
+ try (Cursor cr = mDataLayer.fetchMedia(localAlbumQueryArgs)) {
+ assertWithMessage("Local album count").that(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ }
+
+ try (Cursor cr = mDataLayer.fetchMedia(cloudAlbumQueryArgs)) {
+ assertWithMessage("Cloud album count").that(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+
+ try (Cursor cr = mDataLayer.fetchMedia(favoriteAlbumQueryArgs)) {
+ assertWithMessage("Favorite album count").that(cr.getCount()).isEqualTo(2);
+
+ assertCursor(cr, CLOUD_ID_2, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ assertCursor(cr, LOCAL_ID_2, LOCAL_PROVIDER_AUTHORITY);
+ }
+ }
+
+ @Test
+ public void testFetchAlbumMediaMimeTypeFilter() {
+ mController.setCloudProvider(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ mLocalMediaGenerator.createAlbum(ALBUM_ID_1);
+ mCloudPrimaryMediaGenerator.createAlbum(ALBUM_ID_2);
+
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_1, ALBUM_ID_1, IMAGE_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ false);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_1, ALBUM_ID_2, VIDEO_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ false);
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_2, ALBUM_ID_1, VIDEO_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ false);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_2, ALBUM_ID_2, IMAGE_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ false);
+
+ final Bundle mimeTypeQueryArgs = buildQueryArgs(IMAGE_MIME_TYPE, SIZE_BYTES_DEFAULT);
+
+ try (Cursor cr = mDataLayer.fetchAlbums(mimeTypeQueryArgs)) {
+ assertThat(cr.getCount()).isEqualTo(2);
+
+ assertAlbumCursor(cr, ALBUM_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ assertAlbumCursor(cr, ALBUM_ID_2, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+
+ final Bundle localAlbumAndMimeTypeQueryArgs = buildQueryArgs(ALBUM_ID_1,
+ LOCAL_PROVIDER_AUTHORITY, IMAGE_MIME_TYPE, SIZE_BYTES_DEFAULT);
+
+ final Bundle cloudAlbumAndMimeTypeQueryArgs = buildQueryArgs(ALBUM_ID_2,
+ CLOUD_PRIMARY_PROVIDER_AUTHORITY, IMAGE_MIME_TYPE, SIZE_BYTES_DEFAULT);
+
+ try (Cursor cr = mDataLayer.fetchMedia(localAlbumAndMimeTypeQueryArgs)) {
+ assertWithMessage("Local album count").that(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ }
+
+ try (Cursor cr = mDataLayer.fetchMedia(cloudAlbumAndMimeTypeQueryArgs)) {
+ assertWithMessage("Cloud album count").that(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, CLOUD_ID_2, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+ }
+
+ @Test
+ public void testFetchAlbumMediaSizeFilter() {
+ mController.setCloudProvider(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ mLocalMediaGenerator.createAlbum(ALBUM_ID_1);
+ mCloudPrimaryMediaGenerator.createAlbum(ALBUM_ID_2);
+
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_1, ALBUM_ID_1, IMAGE_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ false);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_1, ALBUM_ID_2, VIDEO_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES - 1,
+ /* isFavorite */ false);
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_2, ALBUM_ID_1, VIDEO_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES - 1,
+ /* isFavorite */ false);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_2, ALBUM_ID_2, IMAGE_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ false);
+
+ final Bundle sizeQueryArgs = buildQueryArgs(MIME_TYPE_DEFAULT, SIZE_BYTES - 1);
+
+ try (Cursor cr = mDataLayer.fetchAlbums(sizeQueryArgs)) {
+ assertThat(cr.getCount()).isEqualTo(3);
+
+ assertAlbumCursor(cr, ALBUM_ID_VIDEOS, LOCAL_PROVIDER_AUTHORITY);
+ assertAlbumCursor(cr, ALBUM_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ assertAlbumCursor(cr, ALBUM_ID_2, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+
+ final Bundle localAlbumAndSizeQueryArgs = buildQueryArgs(ALBUM_ID_1,
+ LOCAL_PROVIDER_AUTHORITY, MIME_TYPE_DEFAULT, SIZE_BYTES -1);
+
+ final Bundle cloudAlbumAndSizeQueryArgs = buildQueryArgs(ALBUM_ID_2,
+ CLOUD_PRIMARY_PROVIDER_AUTHORITY, MIME_TYPE_DEFAULT, SIZE_BYTES -1);
+
+ try (Cursor cr = mDataLayer.fetchMedia(localAlbumAndSizeQueryArgs)) {
+ assertWithMessage("Local album count").that(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, LOCAL_ID_2, LOCAL_PROVIDER_AUTHORITY);
+ }
+
+ try (Cursor cr = mDataLayer.fetchMedia(cloudAlbumAndSizeQueryArgs)) {
+ assertWithMessage("Cloud album count").that(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+ }
+
+ @Test
+ public void testFetchAlbumMediaMimeTypeAndSizeFilter() {
+ mController.setCloudProvider(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ mLocalMediaGenerator.createAlbum(ALBUM_ID_1);
+ mCloudPrimaryMediaGenerator.createAlbum(ALBUM_ID_2);
+
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_1, ALBUM_ID_1, VIDEO_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ false);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_1, ALBUM_ID_2, VIDEO_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES - 1,
+ /* isFavorite */ false);
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_2, ALBUM_ID_1, VIDEO_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES - 1,
+ /* isFavorite */ false);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_2, ALBUM_ID_2, VIDEO_MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_NONE, SIZE_BYTES, /* isFavorite */ false);
+
+ final Bundle mimeTypeAndSizeQueryArgs = buildQueryArgs(VIDEO_MIME_TYPE, SIZE_BYTES -1);
+
+ final Bundle cloudAlbumAndMimeTypeQueryArgs = buildQueryArgs(ALBUM_ID_2,
+ CLOUD_PRIMARY_PROVIDER_AUTHORITY, VIDEO_MIME_TYPE, SIZE_BYTES - 1);
+
+ try (Cursor cr = mDataLayer.fetchAlbums(mimeTypeAndSizeQueryArgs)) {
+ assertWithMessage("Merged and Local album count").that(cr.getCount()).isEqualTo(3);
+
+ assertAlbumCursor(cr, ALBUM_ID_VIDEOS, LOCAL_PROVIDER_AUTHORITY);
+ assertAlbumCursor(cr, ALBUM_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ assertAlbumCursor(cr, ALBUM_ID_2, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+
+ try (Cursor cr = mDataLayer.fetchMedia(cloudAlbumAndMimeTypeQueryArgs)) {
+ assertWithMessage("Cloud album count").that(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+ }
+
+ @Test
+ public void testFetchCloudAccountInfo() {
+ // Cloud provider is not set so cloud account info is null
+ assertThat(mDataLayer.fetchCloudAccountInfo()).isNull();
+
+ // Set cloud provider
+ mFacade.setCloudProvider(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ // Still null since cloud provider doesn't return account info yet
+ assertThat(mDataLayer.fetchCloudAccountInfo()).isNull();
+
+ // Fake cloud provider cloud account info
+ final String expectedName = "bar";
+ final Intent expectedIntent = new Intent("foo");
+ mCloudPrimaryMediaGenerator.setAccountInfo(expectedName, expectedIntent);
+
+ // Verify account info
+ final PickerDataLayer.AccountInfo info = mDataLayer.fetchCloudAccountInfo();
+ assertThat(info).isNotNull();
+ assertThat(info.accountName).isEqualTo(expectedName);
+ assertThat(info.accountConfigurationIntent).isEqualTo(expectedIntent);
+ }
+
+ private static void waitForIdle() {
+ final CountDownLatch latch = new CountDownLatch(1);
+ BackgroundThread.getExecutor().execute(() -> {
+ latch.countDown();
+ });
+ try {
+ latch.await(30, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new IllegalStateException(e);
+ }
+
+ }
+
+ private static Bundle buildDefaultQueryArgs() {
+ return buildQueryArgs(MIME_TYPE_DEFAULT, SIZE_BYTES_DEFAULT);
+ }
+
+ private static Bundle buildQueryArgs(String mimeType, long sizeBytes) {
+ final Bundle queryArgs = new Bundle();
+
+ queryArgs.putString(MediaStore.QUERY_ARG_MIME_TYPE, mimeType);
+ queryArgs.putLong(MediaStore.QUERY_ARG_SIZE_BYTES, sizeBytes);
+
+ return queryArgs;
+ }
+
+ private static Bundle buildQueryArgs(String albumId, String albumAuthority, String mimeType,
+ long sizeBytes) {
+ final Bundle queryArgs = buildQueryArgs(mimeType, sizeBytes);
+
+ queryArgs.putString(MediaStore.QUERY_ARG_ALBUM_ID, albumId);
+ queryArgs.putString(MediaStore.QUERY_ARG_ALBUM_AUTHORITY, albumAuthority);
+
+ return queryArgs;
+ }
+
+ private static void addMedia(MediaGenerator generator, Pair<String, String> media) {
+ generator.addMedia(media.first, media.second);
+ }
+
+ private static void addMedia(MediaGenerator generator, Pair<String, String> media,
+ String albumId, String mimeType, int standardMimeTypeExtension, long sizeBytes,
+ boolean isFavorite) {
+ generator.addMedia(media.first, media.second, albumId, mimeType,
+ standardMimeTypeExtension, sizeBytes, isFavorite);
+ }
+
+ private static void deleteMedia(MediaGenerator generator, Pair<String, String> media) {
+ generator.deleteMedia(media.first, media.second);
+ }
+
+ private Cursor queryMedia() {
+ return mFacade.queryMediaForUi(
+ new PickerDbFacade.QueryFilterBuilder(1000).build());
+ }
+
+ private void assertEmptyCursor() {
+ try (Cursor cr = queryMedia()) {
+ assertThat(cr.getCount()).isEqualTo(0);
+ }
+ }
+
+ private static void assertAlbumCursor(Cursor cursor, String id, String expectedAuthority) {
+ cursor.moveToNext();
+ assertThat(cursor.getString(cursor.getColumnIndex(AlbumColumns.ID)))
+ .isEqualTo(id);
+ final int authorityIdx = cursor.getColumnIndex(AlbumColumns.AUTHORITY);
+ String authority = null;
+ if (authorityIdx >= 0) {
+ // Cursor from picker db has authority as a column
+ authority = cursor.getString(authorityIdx);
+ }
+ if (authority == null) {
+ // Cursor from provider directly doesn't have an authority column but will
+ // have the authority set as an extra
+ final Bundle bundle = cursor.getExtras();
+ authority = bundle.getString(MediaStore.EXTRA_CLOUD_PROVIDER);
+ }
+
+ assertThat(authority).isEqualTo(expectedAuthority);
+ }
+
+ private static void assertCursor(Cursor cursor, String id, String expectedAuthority) {
+ cursor.moveToNext();
+ assertThat(cursor.getString(cursor.getColumnIndex(MediaColumns.ID)))
+ .isEqualTo(id);
+ assertThat(cursor.getString(cursor.getColumnIndex(MediaColumns.AUTHORITY)))
+ .isEqualTo(expectedAuthority);
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/PickerSyncControllerTest.java b/tests/src/com/android/providers/media/photopicker/PickerSyncControllerTest.java
new file mode 100644
index 0000000..53d6837
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/PickerSyncControllerTest.java
@@ -0,0 +1,1077 @@
+/*
+ * 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;
+
+import static com.android.providers.media.PickerProviderMediaGenerator.MediaGenerator;
+import static com.android.providers.media.photopicker.PickerSyncController.CloudProviderInfo;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.SystemClock;
+import android.os.storage.StorageManager;
+import android.provider.CloudMediaProviderContract.MediaColumns;
+import android.provider.MediaStore;
+import android.util.Pair;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.modules.utils.BackgroundThread;
+import com.android.providers.media.PickerProviderMediaGenerator;
+import com.android.providers.media.R;
+import com.android.providers.media.photopicker.data.PickerDatabaseHelper;
+import com.android.providers.media.photopicker.data.PickerDbFacade;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class PickerSyncControllerTest {
+ private static final String TAG = "PickerSyncControllerTest";
+
+ private static final String LOCAL_PROVIDER_AUTHORITY =
+ "com.android.providers.media.photopicker.tests.local";
+ private static final String CLOUD_PRIMARY_PROVIDER_AUTHORITY =
+ "com.android.providers.media.photopicker.tests.cloud_primary";
+ private static final String CLOUD_SECONDARY_PROVIDER_AUTHORITY =
+ "com.android.providers.media.photopicker.tests.cloud_secondary";
+ private static final String PACKAGE_NAME = "com.android.providers.media.tests";
+
+ private final MediaGenerator mLocalMediaGenerator =
+ PickerProviderMediaGenerator.getMediaGenerator(LOCAL_PROVIDER_AUTHORITY);
+ private final MediaGenerator mCloudPrimaryMediaGenerator =
+ PickerProviderMediaGenerator.getMediaGenerator(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ private final MediaGenerator mCloudSecondaryMediaGenerator =
+ PickerProviderMediaGenerator.getMediaGenerator(CLOUD_SECONDARY_PROVIDER_AUTHORITY);
+
+ private static final String LOCAL_ID_1 = "1";
+ private static final String LOCAL_ID_2 = "2";
+
+ private static final String CLOUD_ID_1 = "1";
+ private static final String CLOUD_ID_2 = "2";
+ private static final String CLOUD_ID_3 = "3";
+
+ private static final String ALBUM_ID_1 = "1";
+ private static final String ALBUM_ID_2 = "2";
+
+ private static final Pair<String, String> LOCAL_ONLY_1 = Pair.create(LOCAL_ID_1, null);
+ private static final Pair<String, String> LOCAL_ONLY_2 = Pair.create(LOCAL_ID_2, null);
+ private static final Pair<String, String> CLOUD_ONLY_1 = Pair.create(null, CLOUD_ID_1);
+ private static final Pair<String, String> CLOUD_ONLY_2 = Pair.create(null, CLOUD_ID_2);
+ private static final Pair<String, String> CLOUD_ONLY_3 = Pair.create(null, CLOUD_ID_3);
+ private static final Pair<String, String> CLOUD_AND_LOCAL_1
+ = Pair.create(LOCAL_ID_1, CLOUD_ID_1);
+
+ private static final String COLLECTION_1 = "1";
+ private static final String COLLECTION_2 = "2";
+
+ private static final long SYNC_DELAY_MS = 1000;
+
+ private static final int DB_VERSION_1 = 1;
+ private static final int DB_VERSION_2 = 2;
+ private static final String DB_NAME = "test_db";
+
+ private Context mContext;
+ private PickerDatabaseHelper mDbHelper;
+ private PickerDbFacade mFacade;
+ private PickerSyncController mController;
+
+ @Before
+ public void setUp() {
+ mLocalMediaGenerator.resetAll();
+ mCloudPrimaryMediaGenerator.resetAll();
+ mCloudSecondaryMediaGenerator.resetAll();
+
+ mLocalMediaGenerator.setMediaCollectionId(COLLECTION_1);
+ mCloudPrimaryMediaGenerator.setMediaCollectionId(COLLECTION_1);
+ mCloudSecondaryMediaGenerator.setMediaCollectionId(COLLECTION_1);
+
+ mContext = InstrumentationRegistry.getTargetContext();
+
+ // Delete db so it's recreated on next access and previous test state is cleared
+ final File dbPath = mContext.getDatabasePath(DB_NAME);
+ dbPath.delete();
+
+ mDbHelper = new PickerDatabaseHelper(mContext, DB_NAME, DB_VERSION_1);
+ mFacade = new PickerDbFacade(mContext, LOCAL_PROVIDER_AUTHORITY, mDbHelper);
+
+ final String allowedCloudProviders = CLOUD_PRIMARY_PROVIDER_AUTHORITY + ","
+ + CLOUD_SECONDARY_PROVIDER_AUTHORITY;
+ mController = new PickerSyncController(mContext, mFacade, LOCAL_PROVIDER_AUTHORITY,
+ allowedCloudProviders, /* syncDelay */ 0);
+
+ // Set cloud provider to null to avoid trying to sync it during other tests
+ // that might be using an IsolatedContext
+ setCloudProviderAndSyncAllMedia(null);
+ }
+
+ @Test
+ public void testSyncAllMediaLocalOnly() {
+ // 1. Do nothing
+ mController.syncAllMedia();
+ assertEmptyCursorFromMediaQuery();
+
+ // 2. Add local only media
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_1);
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_2);
+
+ mController.syncAllMedia();
+ try (Cursor cr = queryMedia()) {
+ assertThat(cr.getCount()).isEqualTo(2);
+
+ assertCursor(cr, LOCAL_ID_2, LOCAL_PROVIDER_AUTHORITY);
+ assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ }
+
+ // 3. Delete one local-only media
+ deleteMedia(mLocalMediaGenerator, LOCAL_ONLY_1);
+ mController.syncAllMedia();
+
+ try (Cursor cr = queryMedia()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, LOCAL_ID_2, LOCAL_PROVIDER_AUTHORITY);
+ }
+
+ // 4. Reset media without version bump
+ mLocalMediaGenerator.resetAll();
+ mController.syncAllMedia();
+
+ try (Cursor cr = queryMedia()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, LOCAL_ID_2, LOCAL_PROVIDER_AUTHORITY);
+ }
+
+ // 5. Bump version
+ mLocalMediaGenerator.setMediaCollectionId(COLLECTION_2);
+ mController.syncAllMedia();
+
+ assertEmptyCursorFromMediaQuery();
+ }
+
+ @Test
+ public void testSyncAllAlbumMediaLocalOnly() {
+ // 1. Do nothing
+ mController.syncAlbumMedia(ALBUM_ID_1, true);
+ assertEmptyCursorFromMediaQuery();
+
+ // 2. Add local only media
+ addAlbumMedia(mLocalMediaGenerator, LOCAL_ONLY_1.first, LOCAL_ONLY_1.second, ALBUM_ID_1);
+ addAlbumMedia(mLocalMediaGenerator, LOCAL_ONLY_2.first, LOCAL_ONLY_2.second, ALBUM_ID_1);
+
+ mController.syncAlbumMedia(ALBUM_ID_1, true);
+
+ try (Cursor cr = queryAlbumMedia(ALBUM_ID_1, true)) {
+ assertThat(cr.getCount()).isEqualTo(2);
+
+ assertCursor(cr, LOCAL_ID_2, LOCAL_PROVIDER_AUTHORITY);
+ assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ }
+
+ // 3. Syncs only given album's media
+ addAlbumMedia(mLocalMediaGenerator, LOCAL_ONLY_1.first, LOCAL_ONLY_1.second, ALBUM_ID_2);
+
+ mController.syncAlbumMedia(ALBUM_ID_1, true);
+
+ try (Cursor cr = queryAlbumMedia(ALBUM_ID_1, true)) {
+ assertThat(cr.getCount()).isEqualTo(2);
+
+ assertCursor(cr, LOCAL_ID_2, LOCAL_PROVIDER_AUTHORITY);
+ assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ }
+
+ // 4. Syncing and querying another Album, gets you only items from that album
+ mController.syncAlbumMedia(ALBUM_ID_2, true);
+
+ try (Cursor cr = queryAlbumMedia(ALBUM_ID_2, true)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ }
+
+ // 5. Reset media without version bump, still resets as we always do a full sync for albums.
+ mLocalMediaGenerator.resetAll();
+ mController.syncAlbumMedia(ALBUM_ID_1, true);
+
+ assertEmptyCursorFromAlbumMediaQuery(ALBUM_ID_1, true);
+ try (Cursor cr = queryAlbumMedia(ALBUM_ID_2, true)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ }
+
+ // 6. Sync another album after reset and check that is empty too.
+ mController.syncAlbumMedia(ALBUM_ID_2, true);
+
+ assertEmptyCursorFromAlbumMediaQuery(ALBUM_ID_2, true);
+ }
+
+ @Test
+ public void testSyncAllMediaCloudOnly() {
+ // 1. Add media before setting primary cloud provider
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_1);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_2);
+ mController.syncAllMedia();
+ assertEmptyCursorFromMediaQuery();
+
+ // 2. Set secondary cloud provider
+ setCloudProviderAndSyncAllMedia(CLOUD_SECONDARY_PROVIDER_AUTHORITY);
+
+ // 3. Set primary cloud provider
+ setCloudProviderAndSyncAllMedia(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ try (Cursor cr = queryMedia()) {
+ assertThat(cr.getCount()).isEqualTo(2);
+
+ assertCursor(cr, CLOUD_ID_2, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+
+ // 4. Set secondary cloud provider again
+ setCloudProviderAndSyncAllMedia(CLOUD_SECONDARY_PROVIDER_AUTHORITY);
+ assertEmptyCursorFromMediaQuery();
+
+ // 5. Set primary cloud provider once again
+ setCloudProviderAndSyncAllMedia(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ try (Cursor cr = queryMedia()) {
+ assertThat(cr.getCount()).isEqualTo(2);
+
+ assertCursor(cr, CLOUD_ID_2, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+
+ // 6. Clear cloud provider
+ setCloudProviderAndSyncAllMedia(/* authority */ null);
+ assertEmptyCursorFromMediaQuery();
+ }
+
+ @Test
+ public void testSyncAllMediaResetsAlbumMedia() {
+ // 1. Set primary cloud provider
+ setCloudProviderAndSyncAllMedia(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ assertEmptyCursorFromAlbumMediaQuery(ALBUM_ID_1, false);
+
+ // 2. Add album_media
+ addAlbumMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_1.first, CLOUD_ONLY_1.second,
+ ALBUM_ID_1);
+ addAlbumMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_2.first, CLOUD_ONLY_2.second,
+ ALBUM_ID_1);
+ mController.syncAlbumMedia(ALBUM_ID_1, false);
+
+ // 3. Assert non-empty album_media
+ try (Cursor cr = queryAlbumMedia(ALBUM_ID_1, false)) {
+ assertThat(cr.getCount()).isEqualTo(2);
+ }
+
+ // 4. Sync all media and assert empty album_media
+ mController.syncAllMedia();
+ assertEmptyCursorFromAlbumMediaQuery(ALBUM_ID_1, false);
+ }
+
+ @Test
+ public void testSyncAllAlbumMediaCloudOnly() {
+ // 1. Add media before setting primary cloud provider
+ addAlbumMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_1.first, CLOUD_ONLY_1.second,
+ ALBUM_ID_1);
+ addAlbumMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_2.first, CLOUD_ONLY_2.second,
+ ALBUM_ID_1);
+ mController.syncAlbumMedia(ALBUM_ID_1, false);
+ assertEmptyCursorFromAlbumMediaQuery(ALBUM_ID_1, false);
+
+ // 2. Set secondary cloud provider
+ setCloudProviderAndSyncAllMedia(CLOUD_SECONDARY_PROVIDER_AUTHORITY);
+ mController.syncAlbumMedia(ALBUM_ID_1, false);
+ assertEmptyCursorFromAlbumMediaQuery(ALBUM_ID_1, false);
+
+ // 3. Set primary cloud provider
+ setCloudProviderAndSyncAllMedia(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ mController.syncAlbumMedia(ALBUM_ID_1, false);
+
+ try (Cursor cr = queryAlbumMedia(ALBUM_ID_1, false)) {
+ assertThat(cr.getCount()).isEqualTo(2);
+
+ assertCursor(cr, CLOUD_ID_2, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+
+ // 4. Set secondary cloud provider again
+ setCloudProviderAndSyncAllMedia(CLOUD_SECONDARY_PROVIDER_AUTHORITY);
+ mController.syncAlbumMedia(ALBUM_ID_1, false);
+ assertEmptyCursorFromAlbumMediaQuery(ALBUM_ID_1, false);
+
+ // 5. Set primary cloud provider once again
+ setCloudProviderAndSyncAllMedia(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ mController.syncAlbumMedia(ALBUM_ID_1, false);
+ try (Cursor cr = queryAlbumMedia(ALBUM_ID_1, false)) {
+ assertThat(cr.getCount()).isEqualTo(2);
+
+ assertCursor(cr, CLOUD_ID_2, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+
+ // 6. Clear cloud provider
+ setCloudProviderAndSyncAllMedia(/* authority */ null);
+ mController.syncAlbumMedia(ALBUM_ID_1, false);
+ assertEmptyCursorFromAlbumMediaQuery(ALBUM_ID_1, false);
+ }
+
+ @Test
+ public void testSyncAllAlbumMediaCloudAndLocal() {
+ // 1. Add media before setting primary cloud provider
+ addAlbumMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_1.first, CLOUD_ONLY_1.second,
+ ALBUM_ID_1);
+ addAlbumMedia(mLocalMediaGenerator, LOCAL_ONLY_1.first, LOCAL_ONLY_1.second,
+ ALBUM_ID_1);
+ addAlbumMedia(mLocalMediaGenerator, LOCAL_ONLY_2.first, LOCAL_ONLY_2.second,
+ ALBUM_ID_2);
+
+ mController.syncAlbumMedia(ALBUM_ID_1, false);
+ assertEmptyCursorFromAlbumMediaQuery(ALBUM_ID_1, false);
+
+ // 2. Set secondary cloud provider
+ setCloudProviderAndSyncAllMedia(CLOUD_SECONDARY_PROVIDER_AUTHORITY);
+ mController.syncAlbumMedia(ALBUM_ID_1, false);
+ assertEmptyCursorFromAlbumMediaQuery(ALBUM_ID_1, false);
+
+ // 3. Set primary cloud provider
+ setCloudProviderAndSyncAllMedia(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ mController.syncAlbumMedia(ALBUM_ID_1, false);
+ try (Cursor cr = queryAlbumMedia(ALBUM_ID_1, false)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+
+ // 4. Set secondary cloud provider again
+ setCloudProviderAndSyncAllMedia(CLOUD_SECONDARY_PROVIDER_AUTHORITY);
+
+ mController.syncAlbumMedia(ALBUM_ID_1, false);
+ assertEmptyCursorFromAlbumMediaQuery(ALBUM_ID_1, false);
+
+ // 4. Set primary cloud provider again
+ setCloudProviderAndSyncAllMedia(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ // 4a. Sync the first album and query local albums
+ mController.syncAlbumMedia(ALBUM_ID_1, true);
+ try (Cursor cr = queryAlbumMedia(ALBUM_ID_1, true)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ }
+
+ // 4b. Sync the second album
+ mController.syncAlbumMedia(ALBUM_ID_2, true);
+ try (Cursor cr = queryAlbumMedia(ALBUM_ID_2, true)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, LOCAL_ID_2, LOCAL_PROVIDER_AUTHORITY);
+ }
+
+ // 5. Sync and query cloud albums
+ mController.syncAlbumMedia(ALBUM_ID_1, false);
+ try (Cursor cr = queryAlbumMedia(ALBUM_ID_1, false)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+
+ // 6. Clear cloud provider
+ setCloudProviderAndSyncAllMedia(/* authority */ null);
+ mController.syncAlbumMedia(ALBUM_ID_1, false);
+ assertEmptyCursorFromAlbumMediaQuery(ALBUM_ID_1, false);
+ }
+
+ @Test
+ public void testCloudResetSync() {
+ setCloudProviderAndSyncAllMedia(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ // 1. Do nothing
+ assertEmptyCursorFromMediaQuery();
+
+ // 2. Add cloud-only item
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_1);
+
+ mController.syncAllMedia();
+ try (Cursor cr = queryMedia()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+
+ // 3. Set invalid cloud version
+ mCloudPrimaryMediaGenerator.setMediaCollectionId(/* version */ null);
+ mController.syncAllMedia();
+ assertEmptyCursorFromMediaQuery();
+
+ // 4. Set valid cloud version
+ mCloudPrimaryMediaGenerator.setMediaCollectionId(COLLECTION_1);
+ mController.syncAllMedia();
+
+ try (Cursor cr = queryMedia()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+ }
+
+
+ @Test
+ public void testCloudResetAlbumMediaSync() {
+ setCloudProviderAndSyncAllMedia(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ // 1. Do nothing
+ mController.syncAlbumMedia(ALBUM_ID_1, false);
+ assertEmptyCursorFromAlbumMediaQuery(ALBUM_ID_1, false);
+
+ // 2. Add cloud-only item
+ addAlbumMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_1.first, CLOUD_ONLY_1.second,
+ ALBUM_ID_1);
+
+ mController.syncAlbumMedia(ALBUM_ID_1, false);
+ try (Cursor cr = queryAlbumMedia(ALBUM_ID_1, false)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+
+ // 3. Reset Cloud provider
+ mCloudPrimaryMediaGenerator.resetAll();
+ mController.syncAlbumMedia(ALBUM_ID_1, false);
+ assertEmptyCursorFromAlbumMediaQuery(ALBUM_ID_1, false);
+
+ // 4. Add cloud-only item
+ addAlbumMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_1.first, CLOUD_ONLY_1.second,
+ ALBUM_ID_1);
+
+ mController.syncAlbumMedia(ALBUM_ID_1, false);
+ try (Cursor cr = queryAlbumMedia(ALBUM_ID_1, false)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+
+ // 5. Unset Cloud provider
+ setCloudProviderAndSyncAllMedia(null);
+ mController.syncAlbumMedia(ALBUM_ID_1, false);
+ assertEmptyCursorFromAlbumMediaQuery(ALBUM_ID_1, false);
+ }
+
+ @Test
+ public void testSyncAllMediaCloudAndLocal() {
+ // 1. Do nothing
+ assertEmptyCursorFromMediaQuery();
+
+ // 2. Set primary cloud provider and add 2 items: cloud+local and local-only
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_1);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_AND_LOCAL_1);
+
+ setCloudProviderAndSyncAllMedia(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ try (Cursor cr = queryMedia()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ }
+
+ // 3. Delete local-only item
+ deleteMedia(mLocalMediaGenerator, LOCAL_ONLY_1);
+ mController.syncAllMedia();
+
+ try (Cursor cr = queryMedia()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+
+ // 4. Re-add local-only item
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_1);
+ mController.syncAllMedia();
+
+ try (Cursor cr = queryMedia()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ }
+
+ // 5. Delete cloud+local item
+ deleteMedia(mCloudPrimaryMediaGenerator, CLOUD_AND_LOCAL_1);
+ mController.syncAllMedia();
+
+ try (Cursor cr = queryMedia()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ }
+
+ // 6. Delete local-only item
+ deleteMedia(mLocalMediaGenerator, LOCAL_ONLY_1);
+ mController.syncAllMedia();
+
+ assertEmptyCursorFromMediaQuery();
+ }
+
+ @Test
+ public void testSetCloudProvider() {
+ //1. Get local provider assertion out of the way
+ assertThat(mController.getLocalProvider()).isEqualTo(LOCAL_PROVIDER_AUTHORITY);
+
+ // Assert that no cloud provider set on facade
+ assertThat(mFacade.getCloudProvider()).isNull();
+
+ // 2. Can set cloud provider
+ assertThat(mController.setCloudProvider(CLOUD_PRIMARY_PROVIDER_AUTHORITY)).isTrue();
+ assertThat(mController.getCloudProvider()).isEqualTo(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ // Assert that setting cloud provider clears facade cloud provider
+ // And after syncing, the latest provider is set on the facade
+ assertThat(mFacade.getCloudProvider()).isNull();
+ mController.syncAllMedia();
+ assertThat(mFacade.getCloudProvider()).isEqualTo(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ // 3. Can clear cloud provider
+ assertThat(setCloudProviderAndSyncAllMedia(null)).isTrue();
+ assertThat(mController.getCloudProvider()).isNull();
+
+ // Assert that setting cloud provider clears facade cloud provider
+ // And after syncing, the latest provider is set on the facade
+ assertThat(mFacade.getCloudProvider()).isNull();
+ mController.syncAllMedia();
+ assertThat(mFacade.getCloudProvider()).isNull();
+
+ // 4. Can set cloud proivder
+ assertThat(mController.setCloudProvider(CLOUD_PRIMARY_PROVIDER_AUTHORITY)).isTrue();
+ assertThat(mController.getCloudProvider()).isEqualTo(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ // Assert that setting cloud provider clears facade cloud provider
+ // And after syncing, the latest provider is set on the facade
+ assertThat(mFacade.getCloudProvider()).isNull();
+ mController.syncAllMedia();
+ assertThat(mFacade.getCloudProvider()).isEqualTo(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ // Invalid cloud provider is ignored
+ assertThat(setCloudProviderAndSyncAllMedia(LOCAL_PROVIDER_AUTHORITY)).isFalse();
+ assertThat(mController.getCloudProvider()).isEqualTo(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ // Assert that unsuccessfully setting cloud provider doesn't clear facade cloud provider
+ // And after syncing, nothing changes
+ assertThat(mFacade.getCloudProvider()).isEqualTo(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ mController.syncAllMedia();
+ assertThat(mFacade.getCloudProvider()).isEqualTo(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+
+ @Test
+ public void testGetSupportedCloudProviders() {
+ List<CloudProviderInfo> providers = mController.getSupportedCloudProviders();
+
+ CloudProviderInfo primaryInfo = new CloudProviderInfo(CLOUD_PRIMARY_PROVIDER_AUTHORITY,
+ PACKAGE_NAME,
+ Process.myUid());
+ CloudProviderInfo secondaryInfo = new CloudProviderInfo(CLOUD_SECONDARY_PROVIDER_AUTHORITY,
+ PACKAGE_NAME,
+ Process.myUid());
+
+ assertThat(providers).containsExactly(primaryInfo, secondaryInfo);
+ }
+
+ @Test
+ public void testGetSupportedCloudProviders_useAllowList() {
+ CloudProviderInfo primaryInfo = new CloudProviderInfo(CLOUD_PRIMARY_PROVIDER_AUTHORITY,
+ PACKAGE_NAME,
+ Process.myUid());
+ CloudProviderInfo secondaryInfo = new CloudProviderInfo(
+ CLOUD_SECONDARY_PROVIDER_AUTHORITY,
+ PACKAGE_NAME,
+ Process.myUid());
+
+ // 1. Allow list is subset of existing providers list
+ PickerSyncController controller = new PickerSyncController(mContext, mFacade,
+ LOCAL_PROVIDER_AUTHORITY, CLOUD_PRIMARY_PROVIDER_AUTHORITY, SYNC_DELAY_MS);
+ List<CloudProviderInfo> providers = controller.getSupportedCloudProviders();
+ assertThat(providers).containsExactly(primaryInfo);
+
+ String allowedCloudProviders = CLOUD_PRIMARY_PROVIDER_AUTHORITY + ","
+ + CLOUD_SECONDARY_PROVIDER_AUTHORITY;
+ controller = new PickerSyncController(mContext, mFacade,
+ LOCAL_PROVIDER_AUTHORITY, allowedCloudProviders, SYNC_DELAY_MS);
+ providers = controller.getSupportedCloudProviders();
+ assertThat(providers).containsExactly(primaryInfo, secondaryInfo);
+
+ allowedCloudProviders = CLOUD_PRIMARY_PROVIDER_AUTHORITY
+ + "," + CLOUD_SECONDARY_PROVIDER_AUTHORITY
+ + "," + CLOUD_PRIMARY_PROVIDER_AUTHORITY + "invalid";
+ controller = new PickerSyncController(mContext, mFacade,
+ LOCAL_PROVIDER_AUTHORITY, allowedCloudProviders, SYNC_DELAY_MS);
+ providers = controller.getSupportedCloudProviders();
+ assertThat(providers).containsExactly(primaryInfo, secondaryInfo);
+ }
+
+ @Test
+ public void testNotifyPackageRemoval() {
+ assertThat(mController.setCloudProvider(CLOUD_PRIMARY_PROVIDER_AUTHORITY)).isTrue();
+ assertThat(mController.getCloudProvider()).isEqualTo(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ // Assert passing wrong package name doesn't clear the current cloud provider
+ mController.notifyPackageRemoval(PACKAGE_NAME + "invalid");
+ assertThat(mController.getCloudProvider()).isEqualTo(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ // Assert passing the current cloud provider package name clears the current cloud provider
+ mController.notifyPackageRemoval(PACKAGE_NAME);
+ assertThat(mController.getCloudProvider()).isNull();
+ }
+
+ @Test
+ public void testSelectDefaultCloudProvider_NoDefaultAuthority() {
+ PickerSyncController controller = createControllerWithDefaultProvider("");
+ assertThat(controller.getCloudProvider()).isNull();
+ }
+
+ @Test
+ public void testSelectDefaultCloudProivder_DefaultAuthoritySet() {
+ PickerSyncController controller = createControllerWithDefaultProvider(
+ CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ assertThat(controller.getCloudProvider()).isEqualTo(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+
+ @Test
+ public void testIsProviderAuthorityEnabled() {
+ assertThat(mController.isProviderEnabled(LOCAL_PROVIDER_AUTHORITY)).isTrue();
+ assertThat(mController.isProviderEnabled(CLOUD_PRIMARY_PROVIDER_AUTHORITY)).isFalse();
+ assertThat(mController.isProviderEnabled(CLOUD_SECONDARY_PROVIDER_AUTHORITY)).isFalse();
+
+ setCloudProviderAndSyncAllMedia(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ assertThat(mController.isProviderEnabled(LOCAL_PROVIDER_AUTHORITY)).isTrue();
+ assertThat(mController.isProviderEnabled(CLOUD_PRIMARY_PROVIDER_AUTHORITY)).isTrue();
+ assertThat(mController.isProviderEnabled(CLOUD_SECONDARY_PROVIDER_AUTHORITY)).isFalse();
+ }
+
+ @Test
+ public void testIsProviderUidEnabled() {
+ assertThat(mController.isProviderEnabled(LOCAL_PROVIDER_AUTHORITY, Process.myUid()))
+ .isTrue();
+ assertThat(mController.isProviderEnabled(LOCAL_PROVIDER_AUTHORITY, 1000)).isFalse();
+ }
+
+ @Test
+ public void testNotifyMediaEvent() {
+ PickerSyncController controller = new PickerSyncController(mContext, mFacade,
+ LOCAL_PROVIDER_AUTHORITY, "", SYNC_DELAY_MS);
+
+ // 1. Add media and notify
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_1);
+ controller.notifyMediaEvent();
+ waitForIdle();
+ assertEmptyCursorFromMediaQuery();
+
+ // 2. Sleep for delay
+ SystemClock.sleep(SYNC_DELAY_MS);
+ waitForIdle();
+
+ try (Cursor cr = queryMedia()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ }
+ }
+
+ @Test
+ public void testSyncAfterDbCreate() {
+ PickerDatabaseHelper dbHelper = new PickerDatabaseHelper(mContext, DB_NAME, DB_VERSION_1);
+ PickerDbFacade facade = new PickerDbFacade(mContext, LOCAL_PROVIDER_AUTHORITY,
+ dbHelper);
+ PickerSyncController controller = new PickerSyncController(mContext, facade,
+ LOCAL_PROVIDER_AUTHORITY, "", /* syncDelay */ 0);
+
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_1);
+ controller.syncAllMedia();
+
+ try (Cursor cr = queryMedia(facade)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ }
+
+ dbHelper.close();
+
+ // Delete db so it's recreated on next access
+ final File dbPath = mContext.getDatabasePath(DB_NAME);
+ dbPath.delete();
+
+ facade = new PickerDbFacade(mContext, LOCAL_PROVIDER_AUTHORITY, dbHelper);
+ controller = new PickerSyncController(mContext, facade,
+ LOCAL_PROVIDER_AUTHORITY, "", /* syncDelay */ 0);
+
+ // Initially empty db
+ try (Cursor cr = queryMedia(facade)) {
+ assertThat(cr.getCount()).isEqualTo(0);
+ }
+
+ controller.syncAllMedia();
+
+ // Fully synced db
+ try (Cursor cr = queryMedia(facade)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ }
+ }
+
+ @Test
+ public void testSyncAfterDbUpgrade() {
+ PickerDatabaseHelper dbHelperV1 = new PickerDatabaseHelper(mContext, DB_NAME, DB_VERSION_1);
+ PickerDbFacade facade = new PickerDbFacade(mContext, LOCAL_PROVIDER_AUTHORITY,
+ dbHelperV1);
+ PickerSyncController controller = new PickerSyncController(mContext, facade,
+ LOCAL_PROVIDER_AUTHORITY, "", SYNC_DELAY_MS);
+
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_1);
+ controller.syncAllMedia();
+
+ try (Cursor cr = queryMedia(facade)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ }
+
+ // Upgrade db version
+ dbHelperV1.close();
+ PickerDatabaseHelper dbHelperV2 = new PickerDatabaseHelper(mContext, DB_NAME, DB_VERSION_2);
+ facade = new PickerDbFacade(mContext, LOCAL_PROVIDER_AUTHORITY, dbHelperV2);
+ controller = new PickerSyncController(mContext, facade,
+ LOCAL_PROVIDER_AUTHORITY, "", SYNC_DELAY_MS);
+
+ // Initially empty db
+ try (Cursor cr = queryMedia(facade)) {
+ assertThat(cr.getCount()).isEqualTo(0);
+ }
+
+ controller.syncAllMedia();
+
+ // Fully synced db
+ try (Cursor cr = queryMedia(facade)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ }
+ }
+
+ @Test
+ public void testSyncAfterDbDowngrade() {
+ PickerDatabaseHelper dbHelperV2 = new PickerDatabaseHelper(mContext, DB_NAME, DB_VERSION_2);
+ PickerDbFacade facade = new PickerDbFacade(mContext, LOCAL_PROVIDER_AUTHORITY,
+ dbHelperV2);
+ PickerSyncController controller = new PickerSyncController(mContext, facade,
+ LOCAL_PROVIDER_AUTHORITY, "", SYNC_DELAY_MS);
+
+ addMedia(mLocalMediaGenerator, LOCAL_ONLY_1);
+ controller.syncAllMedia();
+
+ try (Cursor cr = queryMedia(facade)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ }
+
+ // Downgrade db version
+ dbHelperV2.close();
+ PickerDatabaseHelper dbHelperV1 = new PickerDatabaseHelper(mContext, DB_NAME, DB_VERSION_1);
+ facade = new PickerDbFacade(mContext, LOCAL_PROVIDER_AUTHORITY,
+ dbHelperV1);
+ controller = new PickerSyncController(mContext, facade,
+ LOCAL_PROVIDER_AUTHORITY, "", SYNC_DELAY_MS);
+
+ // Initially empty db
+ try (Cursor cr = queryMedia(facade)) {
+ assertThat(cr.getCount()).isEqualTo(0);
+ }
+
+ controller.syncAllMedia();
+
+ // Fully synced db
+ try (Cursor cr = queryMedia(facade)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
+ }
+ }
+
+ @Test
+ public void testAllMediaSyncValidationFailure_incorrectMediaCollectionId() {
+ // 1. Set cloud provider
+ setCloudProviderAndSyncAllMedia(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ // 2. Force the next 2 syncs (including retry) to have correct extra_media_collection_id
+ mCloudPrimaryMediaGenerator.setNextCursorExtras(2, COLLECTION_1,
+ /* honoredSyncGeneration */ true, /* honoredAlbumId */ false);
+
+ // 4. Add cloud media
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_1);
+
+ // 5. Sync and verify media
+ mController.syncAllMedia();
+ try (Cursor cr = queryMedia()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+
+ // 6. Force the next sync (without retry) to have incorrect extra_media_collection_id
+ mCloudPrimaryMediaGenerator.setNextCursorExtras(1, COLLECTION_2,
+ /* honoredSyncGeneration */ true, /* honoredAlbumId */ false);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_2);
+
+ // 7. Sync and verify media after retry succeeded
+ mController.syncAllMedia();
+ try (Cursor cr = queryMedia()) {
+ assertThat(cr.getCount()).isEqualTo(2);
+
+ assertCursor(cr, CLOUD_ID_2, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+
+ // 8. Force the next 2 syncs (including retry) to have incorrect extra_media_collection_id
+ mCloudPrimaryMediaGenerator.setNextCursorExtras(2, COLLECTION_2,
+ /* honoredSyncGeneration */ true, /* honoredAlbumId */ false);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_3);
+
+ // 9. Sync and verify media was reset
+ mController.syncAllMedia();
+ assertEmptyCursorFromMediaQuery();
+ }
+
+ @Test
+ public void testAllMediaSyncValidationRecovery_missingSyncGenerationHonoredArg() {
+ // 1. Set cloud provider
+ setCloudProviderAndSyncAllMedia(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ // 2. Force the next 2 syncs (including retry) to have correct extra_media_collection_id
+ mCloudPrimaryMediaGenerator.setNextCursorExtras(2, COLLECTION_1,
+ /* honoredSyncGeneration */ true, /* honoredAlbumId */ false);
+
+ // 3. Add cloud media
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_1);
+
+ // 4. Sync and verify media
+ mController.syncAllMedia();
+ try (Cursor cr = queryMedia()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+
+ // 5. Force the next sync (without retry) to have incorrect extra_honored_args
+ mCloudPrimaryMediaGenerator.setNextCursorExtras(1, COLLECTION_1,
+ /* honoredSyncGeneration */ false, /* honoredAlbumId */ false);
+ addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_2);
+
+ // 6. Sync and verify media after retry succeeded
+ mController.syncAllMedia();
+ try (Cursor cr = queryMedia()) {
+ assertThat(cr.getCount()).isEqualTo(2);
+
+ assertCursor(cr, CLOUD_ID_2, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+ }
+
+ @Test
+ public void testAlbumMediaSyncValidationFailure_missingAlbumIdHonoredArg() {
+ // 1. Set cloud provider
+ setCloudProviderAndSyncAllMedia(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ // 2. Force the next sync to have correct extra_media_collection_id
+ mCloudPrimaryMediaGenerator.setNextCursorExtras(1, COLLECTION_1,
+ /* honoredSyncGeneration */ false, /* honoredAlbumId */ true);
+
+ // 3. Add cloud album_media
+ addAlbumMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_1.first, CLOUD_ONLY_1.second,
+ ALBUM_ID_1);
+
+ // 4. Sync and verify album_media
+ mController.syncAlbumMedia(ALBUM_ID_1, false);
+ try (Cursor cr = queryAlbumMedia(ALBUM_ID_1, false)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+
+ // 5. Force the next sync to have incorrect extra_album_id
+ mCloudPrimaryMediaGenerator.setNextCursorExtras(1, COLLECTION_1,
+ /* honoredSyncGeneration */ false, /* honoredAlbumId */ false);
+
+ // 6. Sync and verify album_media is empty
+ mController.syncAlbumMedia(ALBUM_ID_1, false);
+ assertEmptyCursorFromAlbumMediaQuery(ALBUM_ID_1, false);
+ }
+
+ @Test
+ public void testUserPrefsAfterDbUpgrade() {
+ PickerDatabaseHelper dbHelperV1 = new PickerDatabaseHelper(mContext, DB_NAME, DB_VERSION_1);
+ PickerDbFacade facade = new PickerDbFacade(mContext, LOCAL_PROVIDER_AUTHORITY,
+ dbHelperV1);
+ PickerSyncController controller = new PickerSyncController(mContext, facade,
+ LOCAL_PROVIDER_AUTHORITY, CLOUD_PRIMARY_PROVIDER_AUTHORITY, SYNC_DELAY_MS);
+
+ controller.setCloudProvider(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ assertThat(controller.getCloudProvider()).isEqualTo(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ // Downgrade db version
+ dbHelperV1.close();
+ PickerDatabaseHelper dbHelperV2 = new PickerDatabaseHelper(mContext, DB_NAME, DB_VERSION_2);
+ facade = new PickerDbFacade(mContext, LOCAL_PROVIDER_AUTHORITY,
+ dbHelperV2);
+ controller = new PickerSyncController(mContext, facade,
+ LOCAL_PROVIDER_AUTHORITY, CLOUD_PRIMARY_PROVIDER_AUTHORITY, SYNC_DELAY_MS);
+
+ assertThat(controller.getCloudProvider()).isEqualTo(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+ }
+
+ private static void waitForIdle() {
+ final CountDownLatch latch = new CountDownLatch(1);
+ BackgroundThread.getExecutor().execute(() -> {
+ latch.countDown();
+ });
+ try {
+ latch.await(30, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new IllegalStateException(e);
+ }
+
+ }
+
+ private static Bundle buildQueryArgs(String mimeType, long sizeBytes) {
+ final Bundle queryArgs = new Bundle();
+
+ queryArgs.putString(MediaStore.QUERY_ARG_MIME_TYPE, mimeType);
+ queryArgs.putLong(MediaStore.QUERY_ARG_SIZE_BYTES, sizeBytes);
+
+ return queryArgs;
+ }
+
+ private static Bundle buildQueryArgs(String albumId, String albumAuthority, String mimeType,
+ long sizeBytes) {
+ final Bundle queryArgs = buildQueryArgs(mimeType, sizeBytes);
+
+ queryArgs.putString(MediaStore.QUERY_ARG_ALBUM_ID, albumId);
+ queryArgs.putString(MediaStore.QUERY_ARG_ALBUM_AUTHORITY, albumAuthority);
+
+ return queryArgs;
+ }
+
+ private static void addMedia(MediaGenerator generator, Pair<String, String> media) {
+ generator.addMedia(media.first, media.second);
+ }
+
+ private static void addAlbumMedia(MediaGenerator generator, String localId, String cloudId,
+ String albumId) {
+ generator.addAlbumMedia(localId, cloudId, albumId);
+ }
+
+ private static void addMedia(MediaGenerator generator, Pair<String, String> media,
+ String albumId, String mimeType, int standardMimeTypeExtension, long sizeBytes,
+ boolean isFavorite) {
+ generator.addMedia(media.first, media.second, albumId, mimeType, standardMimeTypeExtension,
+ sizeBytes, isFavorite);
+ }
+
+ private static void deleteMedia(MediaGenerator generator, Pair<String, String> media) {
+ generator.deleteMedia(media.first, media.second);
+ }
+
+ private static Cursor queryMedia(PickerDbFacade facade) {
+ return facade.queryMediaForUi(
+ new PickerDbFacade.QueryFilterBuilder(1000).build());
+ }
+
+ private boolean setCloudProviderAndSyncAllMedia(String authority) {
+ final boolean res = mController.setCloudProvider(authority);
+ mController.syncAllMedia();
+
+ return res;
+ }
+
+ private Cursor queryMedia() {
+ return mFacade.queryMediaForUi(
+ new PickerDbFacade.QueryFilterBuilder(1000).build());
+ }
+
+ private Cursor queryAlbumMedia(String albumId, boolean isLocal) {
+ final String authority = isLocal ? LOCAL_PROVIDER_AUTHORITY
+ : CLOUD_PRIMARY_PROVIDER_AUTHORITY;
+ return mFacade.queryAlbumMediaForUi(
+ new PickerDbFacade.QueryFilterBuilder(1000).setAlbumId(albumId).build(), authority);
+ }
+
+ private void assertEmptyCursorFromMediaQuery() {
+ try (Cursor cr = queryMedia()) {
+ assertThat(cr.getCount()).isEqualTo(0);
+ }
+ }
+
+ private void assertEmptyCursorFromAlbumMediaQuery(String albumId, boolean isLocal) {
+ try (Cursor cr = queryAlbumMedia(albumId, isLocal)) {
+ assertThat(cr.getCount()).isEqualTo(0);
+ }
+ }
+
+ private PickerSyncController createControllerWithDefaultProvider(String defaultProvider) {
+ Context mockContext = mock(Context.class);
+ Resources mockResources = mock(Resources.class);
+
+ when(mockContext.getResources()).thenReturn(mockResources);
+ when(mockContext.getPackageManager()).thenReturn(mContext.getPackageManager());
+ 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));
+ });
+ when(mockResources.getString(R.string.config_default_cloud_provider_authority))
+ .thenReturn(defaultProvider);
+
+ final String allowedCloudProviders = CLOUD_PRIMARY_PROVIDER_AUTHORITY + ","
+ + CLOUD_SECONDARY_PROVIDER_AUTHORITY;
+
+ return new PickerSyncController(mockContext, mFacade,
+ LOCAL_PROVIDER_AUTHORITY, allowedCloudProviders, SYNC_DELAY_MS);
+ }
+
+ private static void assertCursor(Cursor cursor, String id, String expectedAuthority) {
+ cursor.moveToNext();
+ assertThat(cursor.getString(cursor.getColumnIndex(MediaColumns.ID)))
+ .isEqualTo(id);
+ assertThat(cursor.getString(cursor.getColumnIndex( MediaColumns.AUTHORITY)))
+ .isEqualTo(expectedAuthority);
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/UnreliableVolumeFacadeTest.java b/tests/src/com/android/providers/media/photopicker/UnreliableVolumeFacadeTest.java
new file mode 100644
index 0000000..c33c9a8
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/UnreliableVolumeFacadeTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+
+import static org.junit.Assert.*;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.providers.media.photopicker.data.UnreliableVolumeDatabaseHelper;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class UnreliableVolumeFacadeTest {
+ private static final int BATCH_SIZE = 100;
+ private static final long SIZE_BYTES = 7000;
+ private static final String DISPLAY_NAME = "random test image";
+ private static final long DATE_MODIFIED = 1623852851911L;
+ private static final String MIME_TYPE = "image/jpg";
+ private static final String DATA_PREFIX = "mnt/media_rw/A678954/";
+
+ private static UnreliableVolumeFacade mFacade;
+
+ @Before
+ public void setUp() {
+ Context context = InstrumentationRegistry.getTargetContext();
+ mFacade = new UnreliableVolumeFacade(context);
+ }
+
+ @After
+ public void tearDown() {
+ mFacade.deleteMedia();
+ }
+
+ @Test
+ public void queryAllMedia() {
+ int counter = mFacade.insertMedia(generateContentDb());
+ try (Cursor cr = mFacade.queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(counter);
+ cr.moveToFirst();
+ assertThat(cr.getString(1)).isEqualTo(DATA_PREFIX + "0");
+ cr.moveToNext();
+ assertThat(cr.getString(1)).isEqualTo(DATA_PREFIX + "1");
+ }
+ }
+
+ @Test
+ public void testUniqueConstraint() {
+ List<ContentValues> values = generateContentDb();
+ int initialCounter = mFacade.insertMedia(values);
+ int sameInsertionAttempt = mFacade.insertMedia(values);
+ assertEquals(100, initialCounter);
+ assertEquals(0, sameInsertionAttempt);
+ try (Cursor cr = mFacade.queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(initialCounter);
+ }
+ }
+
+ @Test
+ public void deleteAllMedia() {
+ mFacade.deleteMedia();
+ try (Cursor cr = mFacade.queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(0);
+ }
+ }
+
+ @Test
+ public void queryMediaId() {
+ List<ContentValues> values = generateContentDb();
+ int counter = mFacade.insertMedia(values);
+ String uriString = "content://media/external/file/10";
+ Uri uri = Uri.parse(uriString);
+ try (Cursor cr = mFacade.queryMediaId(uri)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ String id = "10";
+ assertThat(cr.getString(0)).isEqualTo(id);
+ }
+ }
+
+ private static ContentValues generateAndGetContentValues(int index) {
+ ContentValues values = new ContentValues();
+ values.put(UnreliableVolumeDatabaseHelper.MediaColumns.DATE_MODIFIED, DATE_MODIFIED);
+ values.put(UnreliableVolumeDatabaseHelper.MediaColumns.SIZE_BYTES, SIZE_BYTES);
+ values.put(UnreliableVolumeDatabaseHelper.MediaColumns.DISPLAY_NAME, DISPLAY_NAME);
+ values.put(UnreliableVolumeDatabaseHelper.MediaColumns._DATA, DATA_PREFIX + index);
+ values.put(UnreliableVolumeDatabaseHelper.MediaColumns.MIME_TYPE, MIME_TYPE);
+
+ return values;
+ }
+
+ public List<ContentValues> generateContentDb() {
+ List<ContentValues> list = new ArrayList<>();
+ for (int i = 0; i < BATCH_SIZE; i++) {
+ list.add(generateAndGetContentValues(i));
+ }
+ return list;
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/data/ExternalDbFacadeTest.java b/tests/src/com/android/providers/media/photopicker/data/ExternalDbFacadeTest.java
new file mode 100644
index 0000000..3a37efa
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/data/ExternalDbFacadeTest.java
@@ -0,0 +1,918 @@
+/*
+ * 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.data;
+
+import static android.content.ContentResolver.EXTRA_HONORED_ARGS;
+import static android.provider.CloudMediaProviderContract.AlbumColumns.ALBUM_ID_CAMERA;
+import static android.provider.CloudMediaProviderContract.AlbumColumns.ALBUM_ID_DOWNLOADS;
+import static android.provider.CloudMediaProviderContract.AlbumColumns.ALBUM_ID_SCREENSHOTS;
+import static android.provider.CloudMediaProviderContract.EXTRA_ALBUM_ID;
+import static android.provider.CloudMediaProviderContract.EXTRA_MEDIA_COLLECTION_ID;
+import static android.provider.CloudMediaProviderContract.EXTRA_SYNC_GENERATION;
+import static android.provider.CloudMediaProviderContract.MediaCollectionInfo;
+import static android.provider.MediaStore.Files.FileColumns._SPECIAL_FORMAT_GIF;
+import static android.provider.MediaStore.Files.FileColumns._SPECIAL_FORMAT_NONE;
+
+import static com.android.providers.media.photopicker.data.ExternalDbFacade.COLUMN_OLD_ID;
+import static com.android.providers.media.photopicker.data.ExternalDbFacade.TABLE_DELETED_MEDIA;
+import static com.android.providers.media.photopicker.data.ExternalDbFacade.TABLE_FILES;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.provider.CloudMediaProviderContract;
+import android.provider.MediaStore;
+import android.provider.MediaStore.Files.FileColumns;
+import android.provider.MediaStore.MediaColumns;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.providers.media.DatabaseHelper;
+import com.android.providers.media.VolumeCache;
+import com.android.providers.media.scan.MediaScannerTest.IsolatedContext;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+
+@RunWith(AndroidJUnit4.class)
+public class ExternalDbFacadeTest {
+ private static final String TAG = "ExternalDbFacadeTest";
+
+ private static final long ID1 = 1;
+ private static final long ID2 = 2;
+ private static final long ID3 = 3;
+ private static final long ID4 = 4;
+ private static final long ID5 = 5;
+ private static final long DATE_TAKEN_MS1 = 1624886050566L;
+ private static final long DATE_TAKEN_MS2 = 1624886050567L;
+ private static final long DATE_TAKEN_MS3 = 1624886050568L;
+ private static final long DATE_TAKEN_MS4 = 1624886050569L;
+ private static final long DATE_TAKEN_MS5 = 1624886050570L;
+ private static final long GENERATION_MODIFIED1 = 1;
+ private static final long GENERATION_MODIFIED2 = 2;
+ private static final long GENERATION_MODIFIED3 = 3;
+ private static final long GENERATION_MODIFIED4 = 4;
+ 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 VIDEO_MIME_TYPE = "video/mp4";
+ private static final long DURATION_MS = 5;
+ private static final int IS_FAVORITE = 0;
+
+ private static Context sIsolatedContext;
+
+ @Before
+ public void setUp() {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ sIsolatedContext = new IsolatedContext(context, TAG, /*asFuseThread*/ false);
+ }
+
+ @Test
+ public void testDeletedMedia_addAndRemove() throws Exception {
+ try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
+ ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper,
+ mock(VolumeCache.class));
+
+ assertThat(facade.addDeletedMedia(ID1)).isTrue();
+ assertThat(facade.addDeletedMedia(ID2)).isTrue();
+
+ try (Cursor cursor = facade.queryDeletedMedia(/* generation */ 0)) {
+ assertThat(cursor.getCount()).isEqualTo(2);
+
+ ArrayList<Long> ids = new ArrayList<>();
+ while (cursor.moveToNext()) {
+ ids.add(cursor.getLong(0));
+ }
+
+ assertThat(ids).contains(ID1);
+ assertThat(ids).contains(ID2);
+ }
+
+ // Filter by generation should only return ID2
+ try (Cursor cursor = facade.queryDeletedMedia(/* generation */ 1)) {
+ assertThat(cursor.getCount()).isEqualTo(1);
+
+ cursor.moveToFirst();
+ assertThat(cursor.getLong(0)).isEqualTo(ID2);
+ }
+
+ // Adding ids again should succeed but bump generation_modified of ID1 and ID2
+ assertThat(facade.addDeletedMedia(ID1)).isTrue();
+ assertThat(facade.addDeletedMedia(ID2)).isTrue();
+
+ // Filter by generation again, now returns both ids since their generation_modified was
+ // bumped
+ try (Cursor cursor = facade.queryDeletedMedia(/* generation */ 1)) {
+ assertThat(cursor.getCount()).isEqualTo(2);
+ }
+
+ // Remove ID2 should succeed
+ assertThat(facade.removeDeletedMedia(ID2)).isTrue();
+ // Remove ID2 again should fail
+ assertThat(facade.removeDeletedMedia(ID2)).isFalse();
+
+ // Verify only ID1 left
+ try (Cursor cursor = facade.queryDeletedMedia(/* generation */ 0)) {
+ assertThat(cursor.getCount()).isEqualTo(1);
+
+ cursor.moveToFirst();
+ assertThat(cursor.getLong(0)).isEqualTo(ID1);
+ }
+ }
+ }
+
+ @Test
+ public void testDeletedMedia_onInsert() throws Exception {
+ try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
+ ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper,
+ mock(VolumeCache.class));
+
+ assertThat(facade.onFileInserted(FileColumns.MEDIA_TYPE_VIDEO, /* isPending */ false))
+ .isTrue();
+ assertThat(facade.onFileInserted(FileColumns.MEDIA_TYPE_IMAGE, /* isPending */ false))
+ .isTrue();
+ assertDeletedMediaEmpty(facade);
+
+ assertThat(facade.onFileInserted(FileColumns.MEDIA_TYPE_AUDIO, /* isPending */ false))
+ .isFalse();
+ assertThat(facade.onFileInserted(FileColumns.MEDIA_TYPE_NONE, /* isPending */ false))
+ .isFalse();
+ assertThat(facade.onFileInserted(FileColumns.MEDIA_TYPE_IMAGE, /* isPending */ true))
+ .isFalse();
+ assertDeletedMediaEmpty(facade);
+ }
+ }
+
+ @Test
+ public void testDeletedMedia_onUpdate_mediaType() throws Exception {
+ try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
+ ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper,
+ mock(VolumeCache.class));
+
+ // Non-media -> non-media: no-op
+ assertThat(facade.onFileUpdated(ID1,
+ FileColumns.MEDIA_TYPE_NONE, FileColumns.MEDIA_TYPE_NONE,
+ /* oldIsTrashed */ false, /* newIsTrashed */ false,
+ /* oldIsPending */ false, /* newIsPending */ false,
+ /* oldIsFavorite */ false, /* newIsFavorite */ false,
+ /* oldSpecialFormat */ _SPECIAL_FORMAT_NONE,
+ /* newSpecialFormat */ _SPECIAL_FORMAT_NONE)).isFalse();
+ assertDeletedMediaEmpty(facade);
+
+ // Media -> non-media: added to deleted_media
+ assertThat(facade.onFileUpdated(ID1,
+ FileColumns.MEDIA_TYPE_IMAGE, FileColumns.MEDIA_TYPE_NONE,
+ /* oldIsTrashed */ false, /* newIsTrashed */ false,
+ /* oldIsPending */ false, /* newIsPending */ false,
+ /* oldIsFavorite */ false, /* newIsFavorite */ false,
+ /* oldSpecialFormat */ _SPECIAL_FORMAT_NONE,
+ /* newSpecialFormat */ _SPECIAL_FORMAT_NONE)).isTrue();
+ assertDeletedMedia(facade, ID1);
+
+ // Non-media -> non-media: no-op
+ assertThat(facade.onFileUpdated(ID1,
+ FileColumns.MEDIA_TYPE_NONE, FileColumns.MEDIA_TYPE_NONE,
+ /* oldIsTrashed */ false, /* newIsTrashed */ false,
+ /* oldIsPending */ false, /* newIsPending */ false,
+ /* oldIsFavorite */ false, /* newIsFavorite */ false,
+ /* oldSpecialFormat */ _SPECIAL_FORMAT_NONE,
+ /* newSpecialFormat */ _SPECIAL_FORMAT_NONE)).isFalse();
+ assertDeletedMedia(facade, ID1);
+
+ // Non-media -> media: remove from deleted_media
+ assertThat(facade.onFileUpdated(ID1,
+ FileColumns.MEDIA_TYPE_NONE, FileColumns.MEDIA_TYPE_IMAGE,
+ /* oldIsTrashed */ false, /* newIsTrashed */ false,
+ /* oldIsPending */ false, /* newIsPending */ false,
+ /* oldIsFavorite */ false, /* newIsFavorite */ false,
+ /* oldSpecialFormat */ _SPECIAL_FORMAT_NONE,
+ /* newSpecialFormat */ _SPECIAL_FORMAT_NONE)).isTrue();
+ assertDeletedMediaEmpty(facade);
+
+ // Non-media -> media: no-op
+ assertThat(facade.onFileUpdated(ID1,
+ FileColumns.MEDIA_TYPE_NONE, FileColumns.MEDIA_TYPE_NONE,
+ /* oldIsTrashed */ false, /* newIsTrashed */ false,
+ /* oldIsPending */ false, /* newIsPending */ false,
+ /* oldIsFavorite */ false, /* newIsFavorite */ false,
+ /* oldSpecialFormat */ _SPECIAL_FORMAT_NONE,
+ /* newSpecialFormat */ _SPECIAL_FORMAT_NONE)).isFalse();
+ assertDeletedMediaEmpty(facade);
+ }
+ }
+
+ @Test
+ public void testDeletedMedia_onUpdate_trashed() throws Exception {
+ try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
+ ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper,
+ mock(VolumeCache.class));
+
+ // Was trashed but is now neither trashed nor pending
+ assertThat(facade.onFileUpdated(ID1,
+ FileColumns.MEDIA_TYPE_IMAGE, FileColumns.MEDIA_TYPE_IMAGE,
+ /* oldIsTrashed */ true, /* newIsTrashed */ false,
+ /* oldIsPending */ false, /* newIsPending */ false,
+ /* oldIsFavorite */ false, /* newIsFavorite */ false,
+ /* oldSpecialFormat */ _SPECIAL_FORMAT_NONE,
+ /* newSpecialFormat */ _SPECIAL_FORMAT_NONE)).isTrue();
+ assertDeletedMediaEmpty(facade);
+
+ // Was not trashed but is now trashed
+ assertThat(facade.onFileUpdated(ID1,
+ FileColumns.MEDIA_TYPE_IMAGE, FileColumns.MEDIA_TYPE_IMAGE,
+ /* oldIsTrashed */ false, /* newIsTrashed */ true,
+ /* oldIsPending */ false, /* newIsPending */ false,
+ /* oldIsFavorite */ false, /* newIsFavorite */ false,
+ /* oldSpecialFormat */ _SPECIAL_FORMAT_NONE,
+ /* newSpecialFormat */ _SPECIAL_FORMAT_NONE)).isTrue();
+ assertDeletedMedia(facade, ID1);
+
+ // Was trashed but is now neither trashed nor pending
+ assertThat(facade.onFileUpdated(ID1,
+ FileColumns.MEDIA_TYPE_IMAGE, FileColumns.MEDIA_TYPE_IMAGE,
+ /* oldIsTrashed */ true, /* newIsTrashed */ false,
+ /* oldIsPending */ false, /* newIsPending */ false,
+ /* oldIsFavorite */ false, /* newIsFavorite */ false,
+ /* oldSpecialFormat */ _SPECIAL_FORMAT_NONE,
+ /* newSpecialFormat */ _SPECIAL_FORMAT_NONE)).isTrue();
+ assertDeletedMediaEmpty(facade);
+ }
+ }
+
+ @Test
+ public void testDeletedMedia_onUpdate_pending() throws Exception {
+ try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
+ ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper,
+ mock(VolumeCache.class));
+
+ // Was pending but is now neither trashed nor pending
+ assertThat(facade.onFileUpdated(ID1,
+ FileColumns.MEDIA_TYPE_IMAGE, FileColumns.MEDIA_TYPE_IMAGE,
+ /* oldIsTrashed */ false, /* newIsTrashed */ false,
+ /* oldIsPending */ true, /* newIsPending */ false,
+ /* oldIsFavorite */ false, /* newIsFavorite */ false,
+ /* oldSpecialFormat */ _SPECIAL_FORMAT_NONE,
+ /* newSpecialFormat */ _SPECIAL_FORMAT_NONE)).isTrue();
+ assertDeletedMediaEmpty(facade);
+
+ // Was not pending but is now pending
+ assertThat(facade.onFileUpdated(ID1,
+ FileColumns.MEDIA_TYPE_IMAGE, FileColumns.MEDIA_TYPE_IMAGE,
+ /* oldIsTrashed */ false, /* newIsTrashed */ false,
+ /* oldIsPending */ false, /* newIsPending */ true,
+ /* oldIsFavorite */ false, /* newIsFavorite */ false,
+ /* oldSpecialFormat */ _SPECIAL_FORMAT_NONE,
+ /* newSpecialFormat */ _SPECIAL_FORMAT_NONE)).isTrue();
+ assertDeletedMedia(facade, ID1);
+
+ // Was pending but is now neither trashed nor pending
+ assertThat(facade.onFileUpdated(ID1,
+ FileColumns.MEDIA_TYPE_IMAGE, FileColumns.MEDIA_TYPE_IMAGE,
+ /* oldIsTrashed */ false, /* newIsTrashed */ false,
+ /* oldIsPending */ true, /* newIsPending */ false,
+ /* oldIsFavorite */ false, /* newIsFavorite */ false,
+ /* oldSpecialFormat */ _SPECIAL_FORMAT_NONE,
+ /* newSpecialFormat */ _SPECIAL_FORMAT_NONE)).isTrue();
+ assertDeletedMediaEmpty(facade);
+ }
+ }
+
+ @Test
+ public void testOnUpdate_visibleFavorite() throws Exception {
+ try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
+ ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper,
+ mock(VolumeCache.class));
+
+ // Was favorite but is now not favorited
+ assertThat(facade.onFileUpdated(ID1,
+ FileColumns.MEDIA_TYPE_IMAGE, FileColumns.MEDIA_TYPE_IMAGE,
+ /* oldIsTrashed */ false, /* newIsTrashed */ false,
+ /* oldIsPending */ false, /* newIsPending */ false,
+ /* oldIsFavorite */ true, /* newIsFavorite */ false,
+ /* oldSpecialFormat */ _SPECIAL_FORMAT_NONE,
+ /* newSpecialFormat */ _SPECIAL_FORMAT_NONE)).isTrue();
+
+ // Was not favorite but is now favorited
+ assertThat(facade.onFileUpdated(ID1,
+ FileColumns.MEDIA_TYPE_IMAGE, FileColumns.MEDIA_TYPE_IMAGE,
+ /* oldIsTrashed */ false, /* newIsTrashed */ false,
+ /* oldIsPending */ false, /* newIsPending */ false,
+ /* oldIsFavorite */ false, /* newIsFavorite */ true,
+ /* oldSpecialFormat */ _SPECIAL_FORMAT_NONE,
+ /* newSpecialFormat */ _SPECIAL_FORMAT_NONE)).isTrue();
+ }
+ }
+
+ @Test
+ public void testOnUpdate_hiddenFavorite() throws Exception {
+ try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
+ ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper,
+ mock(VolumeCache.class));
+
+ // Was favorite but is now not favorited
+ assertThat(facade.onFileUpdated(ID1,
+ FileColumns.MEDIA_TYPE_IMAGE, FileColumns.MEDIA_TYPE_IMAGE,
+ /* oldIsTrashed */ true, /* newIsTrashed */ true,
+ /* oldIsPending */ false, /* newIsPending */ false,
+ /* oldIsFavorite */ true, /* newIsFavorite */ false,
+ /* oldSpecialFormat */ _SPECIAL_FORMAT_NONE,
+ /* newSpecialFormat */ _SPECIAL_FORMAT_NONE)).isFalse();
+
+ // Was not favorite but is now favorited
+ assertThat(facade.onFileUpdated(ID1,
+ FileColumns.MEDIA_TYPE_IMAGE, FileColumns.MEDIA_TYPE_IMAGE,
+ /* oldIsTrashed */ false, /* newIsTrashed */ false,
+ /* oldIsPending */ true, /* newIsPending */ true,
+ /* oldIsFavorite */ false, /* newIsFavorite */ true,
+ /* oldSpecialFormat */ _SPECIAL_FORMAT_NONE,
+ /* newSpecialFormat */ _SPECIAL_FORMAT_NONE)).isFalse();
+ }
+ }
+
+ @Test
+ public void testOnUpdate_visibleSpecialFormat() throws Exception {
+ try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
+ ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper,
+ mock(VolumeCache.class));
+
+ // Was _SPECIAL_FORMAT_NONE but is now _SPECIAL_FORMAT_GIF
+ assertThat(facade.onFileUpdated(ID1,
+ FileColumns.MEDIA_TYPE_IMAGE, FileColumns.MEDIA_TYPE_IMAGE,
+ /* oldIsTrashed */ false, /* newIsTrashed */ false,
+ /* oldIsPending */ false, /* newIsPending */ false,
+ /* oldIsFavorite */ false, /* newIsFavorite */ false,
+ /* oldSpecialFormat */ _SPECIAL_FORMAT_NONE,
+ /* newSpecialFormat */ _SPECIAL_FORMAT_GIF)).isTrue();
+
+ // Was _SPECIAL_FORMAT_GIF but is now _SPECIAL_FORMAT_NONE
+ assertThat(facade.onFileUpdated(ID1,
+ FileColumns.MEDIA_TYPE_IMAGE, FileColumns.MEDIA_TYPE_IMAGE,
+ /* oldIsTrashed */ false, /* newIsTrashed */ false,
+ /* oldIsPending */ false, /* newIsPending */ false,
+ /* oldIsFavorite */ false, /* newIsFavorite */ false,
+ /* oldSpecialFormat */ _SPECIAL_FORMAT_GIF,
+ /* newSpecialFormat */ _SPECIAL_FORMAT_NONE)).isTrue();
+ }
+ }
+
+ @Test
+ public void testOnUpdate_hiddenSpecialFormat() throws Exception {
+ try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
+ ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper,
+ mock(VolumeCache.class));
+
+ // Was _SPECIAL_FORMAT_NONE but is now _SPECIAL_FORMAT_GIF
+ assertThat(facade.onFileUpdated(ID1,
+ FileColumns.MEDIA_TYPE_IMAGE, FileColumns.MEDIA_TYPE_IMAGE,
+ /* oldIsTrashed */ true, /* newIsTrashed */ true,
+ /* oldIsPending */ false, /* newIsPending */ false,
+ /* oldIsFavorite */ false, /* newIsFavorite */ false,
+ /* oldSpecialFormat */ _SPECIAL_FORMAT_NONE,
+ /* newSpecialFormat */ _SPECIAL_FORMAT_GIF)).isFalse();
+
+ // Was _SPECIAL_FORMAT_NONE but is now _SPECIAL_FORMAT_GIF
+ assertThat(facade.onFileUpdated(ID1,
+ FileColumns.MEDIA_TYPE_IMAGE, FileColumns.MEDIA_TYPE_IMAGE,
+ /* oldIsTrashed */ false, /* newIsTrashed */ false,
+ /* oldIsPending */ true, /* newIsPending */ true,
+ /* oldIsFavorite */ false, /* newIsFavorite */ false,
+ /* oldSpecialFormat */ _SPECIAL_FORMAT_GIF,
+ /* newSpecialFormat */ _SPECIAL_FORMAT_NONE)).isFalse();
+ }
+ }
+
+ @Test
+ public void testDeletedMedia_onDelete() throws Exception {
+ try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
+ ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper,
+ mock(VolumeCache.class));
+
+ assertThat(facade.onFileDeleted(ID1, FileColumns.MEDIA_TYPE_NONE)).isFalse();
+ assertDeletedMediaEmpty(facade);
+
+ assertThat(facade.onFileDeleted(ID1, FileColumns.MEDIA_TYPE_IMAGE)).isTrue();
+ assertDeletedMedia(facade, ID1);
+
+ assertThat(facade.onFileDeleted(ID1, FileColumns.MEDIA_TYPE_NONE)).isFalse();
+ assertDeletedMedia(facade, ID1);
+ }
+ }
+
+ @Test
+ public void testQueryMedia_match() throws Exception {
+ try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
+ ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper,
+ mock(VolumeCache.class));
+
+ // Intentionally associate <date_taken_ms2 with generation_modifed1>
+ // and <date_taken_ms1 with generation_modifed2> below.
+ // This allows us verify that the sort order from queryMediaGeneration
+ // is based on date_taken and not generation_modified.
+ ContentValues cv = getContentValues(DATE_TAKEN_MS2, GENERATION_MODIFIED1);
+ helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cv));
+
+ cv.put(MediaColumns.DATE_TAKEN, DATE_TAKEN_MS1);
+ cv.put(MediaColumns.GENERATION_MODIFIED, GENERATION_MODIFIED2);
+ helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cv));
+
+ try (Cursor cursor = queryAllMedia(facade)) {
+ assertThat(cursor.getCount()).isEqualTo(2);
+ assertCursorExtras(cursor);
+
+ cursor.moveToFirst();
+ assertMediaColumns(facade, cursor, ID1, DATE_TAKEN_MS2);
+
+ cursor.moveToNext();
+ assertMediaColumns(facade, cursor, ID2, DATE_TAKEN_MS1);
+ }
+
+ try (Cursor cursor = facade.queryMedia(GENERATION_MODIFIED1,
+ /* albumId */ null, /* mimeType */ null)) {
+ assertThat(cursor.getCount()).isEqualTo(1);
+ assertCursorExtras(cursor, EXTRA_SYNC_GENERATION);
+
+ cursor.moveToFirst();
+ assertMediaColumns(facade, cursor, ID2, DATE_TAKEN_MS1);
+ }
+ }
+ }
+
+ @Test
+ public void testQueryMedia_noMatch() throws Exception {
+ ContentValues cvPending = getContentValues(DATE_TAKEN_MS1, GENERATION_MODIFIED1);
+ cvPending.put(MediaColumns.IS_PENDING, 1);
+
+ ContentValues cvTrashed = getContentValues(DATE_TAKEN_MS2, GENERATION_MODIFIED2);
+ cvTrashed.put(MediaColumns.IS_TRASHED, 1);
+
+ try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
+ ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper,
+ mock(VolumeCache.class));
+
+ helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cvPending));
+ helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cvTrashed));
+
+ try (Cursor cursor = queryAllMedia(facade)) {
+ assertThat(cursor.getCount()).isEqualTo(0);
+ }
+ }
+ }
+
+ @Test
+ public void testQueryMedia_withDateModified() throws Exception {
+ try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
+ ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper,
+ mock(VolumeCache.class));
+ long dateModifiedSeconds1 = DATE_TAKEN_MS1 / 1000;
+ long dateModifiedSeconds2 = DATE_TAKEN_MS2 / 1000;
+ // Intentionally associate <dateModifiedSeconds2 with generation_modifed1>
+ // and <dateModifiedSeconds1 with generation_modifed2> below.
+ // This allows us verify that the sort order from queryMediaGeneration
+ // is based on date_taken and not generation_modified.
+ ContentValues cv = getContentValues(DATE_TAKEN_MS2, GENERATION_MODIFIED1);
+ cv.remove(MediaColumns.DATE_TAKEN);
+ cv.put(MediaColumns.DATE_MODIFIED, dateModifiedSeconds2);
+ helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cv));
+
+ cv.put(MediaColumns.DATE_MODIFIED, dateModifiedSeconds1);
+ cv.put(MediaColumns.GENERATION_MODIFIED, GENERATION_MODIFIED2);
+ helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cv));
+
+ try (Cursor cursor = queryAllMedia(facade)) {
+ assertThat(cursor.getCount()).isEqualTo(2);
+
+ cursor.moveToFirst();
+ assertMediaColumns(facade, cursor, ID1, dateModifiedSeconds2 * 1000);
+
+ cursor.moveToNext();
+ assertMediaColumns(facade, cursor, ID2, dateModifiedSeconds1 * 1000);
+ }
+
+ try (Cursor cursor = facade.queryMedia(GENERATION_MODIFIED1,
+ /* albumId */ null, /* mimeType */ null)) {
+ assertThat(cursor.getCount()).isEqualTo(1);
+
+ cursor.moveToFirst();
+ assertMediaColumns(facade, cursor, ID2, dateModifiedSeconds1 * 1000);
+ }
+ }
+ }
+
+ @Test
+ public void testQueryMedia_withMimeType() throws Exception {
+ try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
+ ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper,
+ mock(VolumeCache.class));
+
+ // Insert image
+ ContentValues cv = getContentValues(DATE_TAKEN_MS1, GENERATION_MODIFIED1);
+ helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cv));
+
+ try (Cursor cursor = queryAllMedia(facade)) {
+ assertThat(cursor.getCount()).isEqualTo(1);
+
+ cursor.moveToFirst();
+ assertMediaColumns(facade, cursor, ID1, DATE_TAKEN_MS1);
+ }
+
+ try (Cursor cursor = facade.queryMedia(/* generation */ 0,
+ /* albumId */ null, VIDEO_MIME_TYPE)) {
+ assertThat(cursor.getCount()).isEqualTo(0);
+ }
+
+ try (Cursor cursor = facade.queryMedia(/* generation */ 0,
+ /* albumId */ null, IMAGE_MIME_TYPE)) {
+ assertThat(cursor.getCount()).isEqualTo(1);
+
+ cursor.moveToFirst();
+ assertMediaColumns(facade, cursor, ID1, DATE_TAKEN_MS1);
+ }
+ }
+ }
+
+ @Test
+ public void testQueryMedia_withAlbum() throws Exception {
+ try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
+ ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper,
+ mock(VolumeCache.class));
+
+ initMediaInAllAlbums(helper);
+
+ try (Cursor cursor = queryAllMedia(facade)) {
+ assertThat(cursor.getCount()).isEqualTo(3);
+ }
+
+ try (Cursor cursor = facade.queryMedia(/* generation */ -1,
+ ALBUM_ID_CAMERA, /* mimeType */ null)) {
+ assertThat(cursor.getCount()).isEqualTo(1);
+ assertCursorExtras(cursor, EXTRA_ALBUM_ID);
+
+ cursor.moveToFirst();
+ assertMediaColumns(facade, cursor, ID1, DATE_TAKEN_MS1);
+ }
+
+ try (Cursor cursor = facade.queryMedia(/* generation */ -1,
+ ALBUM_ID_SCREENSHOTS, /* mimeType */ null)) {
+ assertThat(cursor.getCount()).isEqualTo(1);
+ assertCursorExtras(cursor, EXTRA_ALBUM_ID);
+
+ cursor.moveToFirst();
+ assertMediaColumns(facade, cursor, ID2, DATE_TAKEN_MS2);
+ }
+
+ try (Cursor cursor = facade.queryMedia(/* generation */ -1,
+ ALBUM_ID_DOWNLOADS, /* mimeType */ null)) {
+ assertThat(cursor.getCount()).isEqualTo(1);
+ assertCursorExtras(cursor, EXTRA_ALBUM_ID);
+
+ cursor.moveToFirst();
+ assertMediaColumns(facade, cursor, ID3, DATE_TAKEN_MS3);
+ }
+ }
+ }
+
+ @Test
+ public void testQueryMedia_withAlbumAndMimeType() throws Exception {
+ try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
+ ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper,
+ mock(VolumeCache.class));
+
+ // Insert image
+ ContentValues cv = getContentValues(DATE_TAKEN_MS1, GENERATION_MODIFIED1);
+ cv.put(MediaColumns.RELATIVE_PATH, ExternalDbFacade.RELATIVE_PATH_CAMERA);
+ helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cv));
+
+ try (Cursor cursor = queryAllMedia(facade)) {
+ assertThat(cursor.getCount()).isEqualTo(1);
+
+ cursor.moveToFirst();
+ assertMediaColumns(facade, cursor, ID1, DATE_TAKEN_MS1);
+ }
+
+ try (Cursor cursor = facade.queryMedia(/* generation */ 0,
+ ALBUM_ID_SCREENSHOTS, IMAGE_MIME_TYPE)) {
+ assertThat(cursor.getCount()).isEqualTo(0);
+ }
+
+ try (Cursor cursor = facade.queryMedia(/* generation */ 0,
+ ALBUM_ID_CAMERA, VIDEO_MIME_TYPE)) {
+ assertThat(cursor.getCount()).isEqualTo(0);
+ }
+
+ try (Cursor cursor = facade.queryMedia(/* generation */ 0,
+ ALBUM_ID_CAMERA, IMAGE_MIME_TYPE)) {
+ assertThat(cursor.getCount()).isEqualTo(1);
+
+ cursor.moveToFirst();
+ assertMediaColumns(facade, cursor, ID1, DATE_TAKEN_MS1);
+ }
+ }
+ }
+
+ @Test
+ public void testGetMediaCollectionInfoFiltering() throws Exception {
+ try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
+ ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper,
+ mock(VolumeCache.class));
+
+ ContentValues cv = getContentValues(DATE_TAKEN_MS1, GENERATION_MODIFIED1);
+ helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cv));
+
+ cv.put(MediaColumns.DATE_TAKEN, DATE_TAKEN_MS2);
+ cv.put(MediaColumns.GENERATION_MODIFIED, GENERATION_MODIFIED2);
+ helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cv));
+
+ Bundle bundle = facade.getMediaCollectionInfo(/* generation */ 0);
+ assertMediaCollectionInfo(facade, bundle, /* generation */ 2);
+
+ bundle = facade.getMediaCollectionInfo(GENERATION_MODIFIED1);
+ assertMediaCollectionInfo(facade, bundle, /* generation */ 2);
+
+ bundle = facade.getMediaCollectionInfo(GENERATION_MODIFIED2);
+ assertMediaCollectionInfo(facade, bundle, /* generation */ 0);
+ }
+ }
+
+ @Test
+ public void testGetMediaCollectionInfoVolumeNames() throws Exception {
+ VolumeCache mockVolumeCache = mock(VolumeCache.class);
+ try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
+ ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper,
+ mockVolumeCache);
+
+ HashSet<String> volumes = new HashSet<>();
+ volumes.add("foo");
+ volumes.add("bar");
+ when(mockVolumeCache.getExternalVolumeNames()).thenReturn(volumes);
+
+ final String expectedMediaCollectionId = MediaStore.getVersion(sIsolatedContext)
+ + ":" + "bar:foo";
+
+ final Bundle bundle = facade.getMediaCollectionInfo(/* generation */ 0);
+ final String mediaCollectionId = bundle.getString(
+ MediaCollectionInfo.MEDIA_COLLECTION_ID);
+
+ assertThat(mediaCollectionId).isEqualTo(expectedMediaCollectionId);
+ }
+ }
+
+ @Test
+ public void testGetMediaCollectionInfoWithDeleted() throws Exception {
+ try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
+ ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper,
+ mock(VolumeCache.class));
+
+ ContentValues cv = getContentValues(DATE_TAKEN_MS1, GENERATION_MODIFIED1);
+ helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cv));
+
+ ContentValues cvDeleted = new ContentValues();
+ cvDeleted.put(COLUMN_OLD_ID, ID2);
+ cvDeleted.put(MediaColumns.GENERATION_MODIFIED, GENERATION_MODIFIED2);
+ helper.runWithTransaction(db -> db.insert(TABLE_DELETED_MEDIA, null, cvDeleted));
+
+ Bundle bundle = facade.getMediaCollectionInfo(/* generation */ 0);
+ assertMediaCollectionInfo(facade, bundle, /* generation */ 2);
+ }
+ }
+
+ @Test
+ public void testQueryAlbumsEmpty() throws Exception {
+ try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
+ ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper,
+ mock(VolumeCache.class));
+
+ ContentValues cv = getContentValues(DATE_TAKEN_MS1, GENERATION_MODIFIED1);
+ helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cv));
+
+ try (Cursor cursor = queryAllMedia(facade)) {
+ assertThat(cursor.getCount()).isEqualTo(1);
+ }
+
+ try (Cursor cursor = facade.queryAlbums(/* mimeType */ null)) {
+ assertThat(cursor.getCount()).isEqualTo(0);
+ }
+ }
+ }
+
+ @Test
+ public void testQueryAlbums() throws Exception {
+ try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
+ ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper,
+ mock(VolumeCache.class));
+
+ initMediaInAllAlbums(helper);
+
+ try (Cursor cursor = queryAllMedia(facade)) {
+ assertThat(cursor.getCount()).isEqualTo(3);
+ }
+
+ try (Cursor cursor = facade.queryAlbums(/* mimeType */ null)) {
+ assertThat(cursor.getCount()).isEqualTo(3);
+
+ // We verify the order of the albums:
+ // Camera, Screenshots and Downloads
+ cursor.moveToNext();
+ assertAlbumColumns(facade, cursor, ALBUM_ID_CAMERA, DATE_TAKEN_MS1, /* count */ 1);
+
+ cursor.moveToNext();
+ assertAlbumColumns(facade, cursor, ALBUM_ID_SCREENSHOTS, DATE_TAKEN_MS2,
+ /* count */ 1);
+
+ cursor.moveToNext();
+ assertAlbumColumns(facade, cursor, ALBUM_ID_DOWNLOADS, DATE_TAKEN_MS3,
+ /* count */ 1);
+ }
+ }
+ }
+
+ @Test
+ public void testQueryAlbumsMimeType() throws Exception {
+ try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
+ ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper,
+ mock(VolumeCache.class));
+
+ // Insert image in camera album
+ ContentValues cv1 = getContentValues(DATE_TAKEN_MS1, GENERATION_MODIFIED1);
+ cv1.put(MediaColumns.RELATIVE_PATH, ExternalDbFacade.RELATIVE_PATH_CAMERA);
+ helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cv1));
+
+ // Insert video in camera ablum
+ 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);
+ helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cv2));
+
+ try (Cursor cursor = queryAllMedia(facade)) {
+ assertThat(cursor.getCount()).isEqualTo(2);
+ }
+
+ try (Cursor cursor = facade.queryAlbums(IMAGE_MIME_TYPE)) {
+ assertThat(cursor.getCount()).isEqualTo(1);
+
+ // We verify the order of the albums only the image in camera is shown
+ cursor.moveToNext();
+ assertAlbumColumns(facade, cursor, ALBUM_ID_CAMERA, DATE_TAKEN_MS1, /* count */ 1);
+ }
+ }
+ }
+
+ @Test
+ public void testOrderOfLocalAlbumIds() {
+ // Camera, ScreenShots, Downloads
+ assertThat(ExternalDbFacade.LOCAL_ALBUM_IDS[0]).isEqualTo(ALBUM_ID_CAMERA);
+ assertThat(ExternalDbFacade.LOCAL_ALBUM_IDS[1])
+ .isEqualTo(ALBUM_ID_SCREENSHOTS);
+ assertThat(ExternalDbFacade.LOCAL_ALBUM_IDS[2])
+ .isEqualTo(ALBUM_ID_DOWNLOADS);
+ }
+
+ private static void initMediaInAllAlbums(DatabaseHelper helper) {
+ // Insert in camera album
+ ContentValues cv1 = getContentValues(DATE_TAKEN_MS1, GENERATION_MODIFIED1);
+ cv1.put(MediaColumns.RELATIVE_PATH, ExternalDbFacade.RELATIVE_PATH_CAMERA);
+ helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cv1));
+
+ // Insert in screenshots ablum
+ ContentValues cv2 = getContentValues(DATE_TAKEN_MS2, GENERATION_MODIFIED2);
+ cv2.put(MediaColumns.RELATIVE_PATH, ExternalDbFacade.RELATIVE_PATH_SCREENSHOTS);
+ helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cv2));
+
+ // Insert in download ablum
+ ContentValues cv3 = getContentValues(DATE_TAKEN_MS3, GENERATION_MODIFIED3);
+ cv3.put(MediaColumns.IS_DOWNLOAD, 1);
+ helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cv3));
+ }
+
+ private static void assertDeletedMediaEmpty(ExternalDbFacade facade) {
+ try (Cursor cursor = facade.queryDeletedMedia(/* generation */ 0)) {
+ assertThat(cursor.getCount()).isEqualTo(0);
+ }
+ }
+
+ private static void assertDeletedMedia(ExternalDbFacade facade, long id) {
+ try (Cursor cursor = facade.queryDeletedMedia(/* generation */ 0)) {
+ assertThat(cursor.getCount()).isEqualTo(1);
+
+ cursor.moveToFirst();
+ assertThat(cursor.getLong(0)).isEqualTo(id);
+ assertThat(cursor.getColumnName(0)).isEqualTo(
+ CloudMediaProviderContract.MediaColumns.ID);
+ }
+ }
+
+ private static void assertMediaColumns(ExternalDbFacade facade, Cursor cursor, long id,
+ long dateTakenMs) {
+ assertMediaColumns(facade, cursor, id, dateTakenMs, IS_FAVORITE);
+ }
+
+ private static void assertMediaColumns(ExternalDbFacade facade, Cursor cursor, long id,
+ long dateTakenMs, int isFavorite) {
+ assertMediaColumns(facade, cursor, id, dateTakenMs, isFavorite, IMAGE_MIME_TYPE);
+ }
+
+ private static void assertMediaColumns(ExternalDbFacade facade, Cursor cursor, long id,
+ long dateTakenMs, int isFavorite, String mimeType) {
+ int idIndex = cursor.getColumnIndex(CloudMediaProviderContract.MediaColumns.ID);
+ int dateTakenIndex = cursor.getColumnIndex(
+ CloudMediaProviderContract.MediaColumns.DATE_TAKEN_MILLIS);
+ int sizeIndex = cursor.getColumnIndex(CloudMediaProviderContract.MediaColumns.SIZE_BYTES);
+ int mimeTypeIndex = cursor.getColumnIndex(
+ CloudMediaProviderContract.MediaColumns.MIME_TYPE);
+ int durationIndex = cursor.getColumnIndex(
+ CloudMediaProviderContract.MediaColumns.DURATION_MILLIS);
+ int isFavoriteIndex = cursor.getColumnIndex(
+ CloudMediaProviderContract.MediaColumns.IS_FAVORITE);
+
+ assertThat(cursor.getLong(idIndex)).isEqualTo(id);
+ assertThat(cursor.getLong(dateTakenIndex)).isEqualTo(dateTakenMs);
+ assertThat(cursor.getLong(sizeIndex)).isEqualTo(SIZE);
+ assertThat(cursor.getString(mimeTypeIndex)).isEqualTo(mimeType);
+ assertThat(cursor.getLong(durationIndex)).isEqualTo(DURATION_MS);
+ assertThat(cursor.getInt(isFavoriteIndex)).isEqualTo(isFavorite);
+ }
+
+ private static void assertCursorExtras(Cursor cursor, String... honoredArg) {
+ final Bundle bundle = cursor.getExtras();
+
+ assertThat(bundle.getString(EXTRA_MEDIA_COLLECTION_ID))
+ .isEqualTo(MediaStore.getVersion(sIsolatedContext));
+ if (honoredArg != null) {
+ assertThat(bundle.getStringArrayList(EXTRA_HONORED_ARGS))
+ .containsExactlyElementsIn(Arrays.asList(honoredArg));
+ }
+ }
+
+ private static void assertAlbumColumns(ExternalDbFacade facade, Cursor cursor,
+ String displayName, long dateTakenMs, long count) {
+ int displayNameIndex = cursor.getColumnIndex(
+ CloudMediaProviderContract.AlbumColumns.DISPLAY_NAME);
+ int idIndex = cursor.getColumnIndex(CloudMediaProviderContract.AlbumColumns.MEDIA_COVER_ID);
+ int dateTakenIndex = cursor.getColumnIndex(
+ CloudMediaProviderContract.AlbumColumns.DATE_TAKEN_MILLIS);
+ int countIndex = cursor.getColumnIndex(CloudMediaProviderContract.AlbumColumns.MEDIA_COUNT);
+
+ assertThat(cursor.getString(displayNameIndex)).isEqualTo(displayName);
+ assertThat(cursor.getString(idIndex)).isNotNull();
+ assertThat(cursor.getLong(dateTakenIndex)).isEqualTo(dateTakenMs);
+ assertThat(cursor.getLong(countIndex)).isEqualTo(count);
+ }
+
+ private static void assertMediaCollectionInfo(ExternalDbFacade facade, Bundle bundle,
+ long expectedGeneration) {
+ long generation = bundle.getLong(MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION);
+ String mediaCollectionId = bundle.getString(MediaCollectionInfo.MEDIA_COLLECTION_ID);
+
+ assertThat(generation).isEqualTo(expectedGeneration);
+ assertThat(mediaCollectionId).isEqualTo(MediaStore.getVersion(sIsolatedContext));
+ }
+
+ private static Cursor queryAllMedia(ExternalDbFacade facade) {
+ return facade.queryMedia(/* generation */ -1, /* albumId */ null,
+ /* mimeType */ null);
+ }
+
+ private static ContentValues getContentValues(long dateTakenMs, long generation) {
+ ContentValues cv = new ContentValues();
+ cv.put(MediaColumns.SIZE, SIZE);
+ cv.put(MediaColumns.DATE_TAKEN, dateTakenMs);
+ cv.put(FileColumns.MIME_TYPE, IMAGE_MIME_TYPE);
+ cv.put(FileColumns.MEDIA_TYPE, FileColumns.MEDIA_TYPE_IMAGE);
+ cv.put(MediaColumns.DURATION, DURATION_MS);
+ cv.put(MediaColumns.GENERATION_MODIFIED, generation);
+
+ return cv;
+ }
+
+ private static class TestDatabaseHelper extends DatabaseHelper {
+ public TestDatabaseHelper(Context context) {
+ super(context, TEST_CLEAN_DB, 1, false, false, null, null, null, null, null, null,
+ false);
+ }
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/data/PickerDatabaseHelperTest.java b/tests/src/com/android/providers/media/photopicker/data/PickerDatabaseHelperTest.java
new file mode 100644
index 0000000..d35d4ed
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/data/PickerDatabaseHelperTest.java
@@ -0,0 +1,450 @@
+/*
+ * 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.data;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.provider.CloudMediaProviderContract;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.providers.media.scan.MediaScannerTest.IsolatedContext;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class PickerDatabaseHelperTest {
+ private static final String TAG = "PickerDatabaseHelperTest";
+
+ private static final String TEST_PICKER_DB = "test_picker";
+ static final String MEDIA_TABLE = "media";
+ static final String ALBUM_MEDIA_TABLE = "album_media";
+
+ private static final String KEY_LOCAL_ID = "local_id";
+ private static final String KEY_CLOUD_ID = "cloud_id";
+ private static final String KEY_IS_VISIBLE = "is_visible";
+ private static final String KEY_ALBUM_ID = "album_id";
+ private static final String KEY_DATE_TAKEN_MS = "date_taken_ms";
+ private static final String KEY_SYNC_GENERATION = "sync_generation";
+ private static final String KEY_SIZE_BYTES = "size_bytes";
+ private static final String KEY_DURATION_MS = "duration_ms";
+ private static final String KEY_MIME_TYPE = "mime_type";
+ private static final String KEY_STANDARD_MIME_TYPE_EXTENSION = "standard_mime_type_extension";
+
+ private static final long LOCAL_ID = 50;
+ private static final long SIZE_BYTES = 7000;
+ private static final long DATE_TAKEN_MS = 1623852851911L;
+ private static final long GENERATION_MODIFIED = 1L;
+ private static final String CLOUD_ID = "asdfghjkl;";
+ private static final String ALBUM_ID = "testAlbum;";
+ private static final String MIME_TYPE = "video/mp4";
+ private static final int STANDARD_MIME_TYPE_EXTENSION =
+ CloudMediaProviderContract.MediaColumns.STANDARD_MIME_TYPE_EXTENSION_GIF;
+ private static final long DURATION_MS = 0;
+
+ private static Context sIsolatedContext;
+
+ @Before
+ public void setUp() {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ sIsolatedContext = new IsolatedContext(context, TAG, /*asFuseThread*/ false);
+ }
+
+ @Test
+ public void testMediaColumns() throws Exception {
+ String[] projection = new String[] {
+ KEY_LOCAL_ID,
+ KEY_CLOUD_ID,
+ KEY_IS_VISIBLE,
+ KEY_DATE_TAKEN_MS,
+ KEY_SYNC_GENERATION,
+ KEY_SIZE_BYTES,
+ KEY_DURATION_MS,
+ KEY_MIME_TYPE,
+ KEY_STANDARD_MIME_TYPE_EXTENSION
+ };
+
+ try (PickerDatabaseHelper helper = new PickerDatabaseHelperT(sIsolatedContext)) {
+ SQLiteDatabase db = helper.getWritableDatabase();
+
+ // All fields specified
+ ContentValues values = getBasicContentValues();
+ values.put(KEY_LOCAL_ID, LOCAL_ID);
+ values.put(KEY_CLOUD_ID, CLOUD_ID);
+ values.put(KEY_IS_VISIBLE, 1);
+ assertThat(db.insert(MEDIA_TABLE, null, values)).isNotEqualTo(-1);
+
+ try (Cursor cr = db.query(MEDIA_TABLE, projection, null, null, null, null, null)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ while (cr.moveToNext()) {
+ assertThat(cr.getLong(0)).isEqualTo(LOCAL_ID);
+ assertThat(cr.getString(1)).isEqualTo(CLOUD_ID);
+ assertThat(cr.getInt(2)).isEqualTo(1);
+ assertThat(cr.getLong(3)).isEqualTo(DATE_TAKEN_MS);
+ assertThat(cr.getLong(4)).isEqualTo(GENERATION_MODIFIED);
+ assertThat(cr.getLong(5)).isEqualTo(SIZE_BYTES);
+ assertThat(cr.getLong(6)).isEqualTo(DURATION_MS);
+ assertThat(cr.getString(7)).isEqualTo(MIME_TYPE);
+ assertThat(cr.getInt(8)).isEqualTo(STANDARD_MIME_TYPE_EXTENSION);
+ }
+ }
+ }
+ }
+
+
+ @Test
+ public void testAlbumMediaColumns() throws Exception {
+ String[] projection = new String[] {
+ KEY_LOCAL_ID,
+ KEY_CLOUD_ID,
+ KEY_ALBUM_ID,
+ KEY_DATE_TAKEN_MS,
+ KEY_SYNC_GENERATION,
+ KEY_SIZE_BYTES,
+ KEY_DURATION_MS,
+ KEY_MIME_TYPE,
+ KEY_STANDARD_MIME_TYPE_EXTENSION
+ };
+
+ try (PickerDatabaseHelper helper = new PickerDatabaseHelperT(sIsolatedContext)) {
+ SQLiteDatabase db = helper.getWritableDatabase();
+
+ // All fields specified
+ ContentValues values = getBasicContentValues();
+ values.put(KEY_LOCAL_ID, LOCAL_ID);
+ values.put(KEY_ALBUM_ID, ALBUM_ID);
+ assertThat(db.insert(ALBUM_MEDIA_TABLE, null, values)).isNotEqualTo(-1);
+
+ try (Cursor cr = db.query(ALBUM_MEDIA_TABLE, projection, null, null, null, null,
+ null)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ while (cr.moveToNext()) {
+ assertThat(cr.getLong(0)).isEqualTo(LOCAL_ID);
+ assertThat(cr.getString(1)).isEqualTo(null);
+ assertThat(cr.getString(2)).isEqualTo(ALBUM_ID);
+ assertThat(cr.getLong(3)).isEqualTo(DATE_TAKEN_MS);
+ assertThat(cr.getLong(4)).isEqualTo(GENERATION_MODIFIED);
+ assertThat(cr.getLong(5)).isEqualTo(SIZE_BYTES);
+ assertThat(cr.getLong(6)).isEqualTo(DURATION_MS);
+ assertThat(cr.getString(7)).isEqualTo(MIME_TYPE);
+ assertThat(cr.getInt(8)).isEqualTo(STANDARD_MIME_TYPE_EXTENSION);
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testCheck_cloudOrLocal() throws Exception {
+ try (PickerDatabaseHelper helper = new PickerDatabaseHelperT(sIsolatedContext)) {
+ SQLiteDatabase db = helper.getWritableDatabase();
+
+ // Visible but no cloud or local specified
+ ContentValues values = getBasicContentValues();
+ values.put(KEY_IS_VISIBLE, 1);
+ assertThat(db.insert(MEDIA_TABLE, null, values)).isEqualTo(-1);
+
+ // Hidden but no cloud or local specified
+ values = getBasicContentValues();
+ assertThat(db.insert(MEDIA_TABLE, null, values)).isEqualTo(-1);
+ }
+ }
+
+ @Test
+ public void testUniqueConstraint_local() throws Exception {
+ try (PickerDatabaseHelper helper = new PickerDatabaseHelperT(sIsolatedContext)) {
+ SQLiteDatabase db = helper.getWritableDatabase();
+
+ // Hidden local only
+ ContentValues values = getBasicContentValues();
+ values.put(KEY_LOCAL_ID, LOCAL_ID);
+ assertThat(db.insert(MEDIA_TABLE, null, values)).isNotEqualTo(-1);
+
+ // Another hidden local only
+ values = getBasicContentValues();
+ values.put(KEY_LOCAL_ID, LOCAL_ID);
+ assertThat(db.insert(MEDIA_TABLE, null, values)).isNotEqualTo(-1);
+
+ // Visible local only
+ values = getBasicContentValues();
+ values.put(KEY_LOCAL_ID, LOCAL_ID);
+ values.put(KEY_IS_VISIBLE, 1);
+ assertThat(db.insert(MEDIA_TABLE, null, values)).isNotEqualTo(-1);
+
+ // Another visible local only
+ values = getBasicContentValues();
+ values.put(KEY_LOCAL_ID, LOCAL_ID);
+ values.put(KEY_IS_VISIBLE, 1);
+ assertThat(db.insert(MEDIA_TABLE, null, values)).isEqualTo(-1);
+ }
+ }
+
+ @Test
+ public void testUniqueConstraintAlbumMedia() throws Exception {
+ try (PickerDatabaseHelper helper = new PickerDatabaseHelperT(sIsolatedContext)) {
+ SQLiteDatabase db = helper.getWritableDatabase();
+
+ // Local Album Media
+ ContentValues values = getBasicContentValues();
+ values.put(KEY_LOCAL_ID, LOCAL_ID);
+ values.put(KEY_ALBUM_ID, ALBUM_ID);
+ assertThat(db.insert(ALBUM_MEDIA_TABLE, null, values)).isNotEqualTo(-1);
+
+ // Another local for Album Media
+ values = getBasicContentValues();
+ values.put(KEY_LOCAL_ID, LOCAL_ID);
+ values.put(KEY_ALBUM_ID, ALBUM_ID);
+ assertThat(db.insert(ALBUM_MEDIA_TABLE, null, values)).isEqualTo(-1);
+
+ // Cloud for Album Media
+ values = getBasicContentValues();
+ values.put(KEY_CLOUD_ID, CLOUD_ID);
+ values.put(KEY_ALBUM_ID, ALBUM_ID);
+ assertThat(db.insert(ALBUM_MEDIA_TABLE, null, values)).isNotEqualTo(-1);
+
+ // Another Cloud for Album Media
+ values = getBasicContentValues();
+ values.put(KEY_CLOUD_ID, CLOUD_ID);
+ values.put(KEY_ALBUM_ID, ALBUM_ID);
+ assertThat(db.insert(ALBUM_MEDIA_TABLE, null, values)).isEqualTo(-1);
+ }
+ }
+
+ @Test
+ public void testUniqueConstraint_cloud() throws Exception {
+ try (PickerDatabaseHelper helper = new PickerDatabaseHelperT(sIsolatedContext)) {
+ SQLiteDatabase db = helper.getWritableDatabase();
+
+ // Hidden cloud only
+ ContentValues values = getBasicContentValues();
+ values.put(KEY_CLOUD_ID, CLOUD_ID);
+ assertThat(db.insert(MEDIA_TABLE, null, values)).isNotEqualTo(-1);
+
+ // Another hidden cloud only
+ values = getBasicContentValues();
+ values.put(KEY_CLOUD_ID, CLOUD_ID);
+ assertThat(db.insert(MEDIA_TABLE, null, values)).isEqualTo(-1);
+
+ // Visible cloud only
+ values = getBasicContentValues();
+ values.put(KEY_CLOUD_ID, CLOUD_ID);
+ values.put(KEY_IS_VISIBLE, 1);
+ assertThat(db.insert(MEDIA_TABLE, null, values)).isEqualTo(-1);
+ }
+ }
+
+ @Test
+ public void testUniqueConstraint_localAndCloudPlusLocal() throws Exception {
+ try (PickerDatabaseHelper helper = new PickerDatabaseHelperT(sIsolatedContext)) {
+ SQLiteDatabase db = helper.getWritableDatabase();
+
+ // Visible local only
+ ContentValues values = getBasicContentValues();
+ values.put(KEY_LOCAL_ID, LOCAL_ID);
+ values.put(KEY_IS_VISIBLE, 1);
+ assertThat(db.insert(MEDIA_TABLE, null, values)).isNotEqualTo(-1);
+
+ // Visible Cloud+Local (same local_id)
+ values = getBasicContentValues();
+ values.put(KEY_CLOUD_ID, CLOUD_ID + "1");
+ values.put(KEY_LOCAL_ID, LOCAL_ID);
+ values.put(KEY_IS_VISIBLE, 1);
+ assertThat(db.insert(MEDIA_TABLE, null, values)).isEqualTo(-1);
+
+ // Hidden Cloud+Local (same local_id)
+ values = getBasicContentValues();
+ values.put(KEY_CLOUD_ID, CLOUD_ID + "1");
+ values.put(KEY_LOCAL_ID, LOCAL_ID);
+ values.putNull(KEY_IS_VISIBLE);
+ assertThat(db.insert(MEDIA_TABLE, null, values)).isNotEqualTo(-1);
+ }
+ }
+
+ @Test
+ public void testCheck_IsVisible() throws Exception {
+ try (PickerDatabaseHelper helper = new PickerDatabaseHelperT(sIsolatedContext)) {
+ SQLiteDatabase db = helper.getWritableDatabase();
+
+ // is_visible < 1
+ ContentValues values = getBasicContentValues();
+ values.put(KEY_CLOUD_ID, CLOUD_ID);
+ values.put(KEY_IS_VISIBLE, 0);
+ assertThat(db.insert(MEDIA_TABLE, null, values)).isEqualTo(-1);
+
+ // is_visible > 1
+ values = getBasicContentValues();
+ values.put(KEY_LOCAL_ID, LOCAL_ID);
+ values.put(KEY_IS_VISIBLE, 2);
+ assertThat(db.insert(MEDIA_TABLE, null, values)).isEqualTo(-1);
+ }
+ }
+
+ @Test
+ public void testCheck_Size() throws Exception {
+ try (PickerDatabaseHelper helper = new PickerDatabaseHelperT(sIsolatedContext)) {
+ SQLiteDatabase db = helper.getWritableDatabase();
+
+ // size_bytes=NULL
+ ContentValues values = getBasicContentValues();
+ values.remove(KEY_SIZE_BYTES);
+ values.put(KEY_CLOUD_ID, CLOUD_ID);
+ assertThat(db.insert(MEDIA_TABLE, null, values)).isEqualTo(-1);
+
+ // size_bytes=0
+ values = getBasicContentValues();
+ values.put(KEY_SIZE_BYTES, 0);
+ values.put(KEY_CLOUD_ID, CLOUD_ID);
+ assertThat(db.insert(MEDIA_TABLE, null, values)).isEqualTo(-1);
+
+ // size_bytes=NULL for Album Media Table
+ values = getBasicContentValues();
+ values.remove(KEY_SIZE_BYTES);
+ values.put(KEY_CLOUD_ID, CLOUD_ID);
+ assertThat(db.insert(ALBUM_MEDIA_TABLE, null, values)).isEqualTo(-1);
+
+ // size_bytes=0 for Album Media Table
+ values = getBasicContentValues();
+ values.put(KEY_SIZE_BYTES, 0);
+ values.put(KEY_CLOUD_ID, CLOUD_ID);
+ assertThat(db.insert(ALBUM_MEDIA_TABLE, null, values)).isEqualTo(-1);
+ }
+ }
+
+ @Test
+ public void testCheck_MimeType() throws Exception {
+ try (PickerDatabaseHelper helper = new PickerDatabaseHelperT(sIsolatedContext)) {
+ SQLiteDatabase db = helper.getWritableDatabase();
+
+ // mime_type=NULL
+ ContentValues values = getBasicContentValues();
+ values.remove(KEY_MIME_TYPE);
+ values.put(KEY_CLOUD_ID, CLOUD_ID);
+ assertThat(db.insert(MEDIA_TABLE, null, values)).isEqualTo(-1);
+
+ // mime_type=NULL for Album Media
+ values = getBasicContentValues();
+ values.remove(KEY_MIME_TYPE);
+ values.put(KEY_CLOUD_ID, CLOUD_ID);
+ assertThat(db.insert(ALBUM_MEDIA_TABLE, null, values)).isEqualTo(-1);
+ }
+ }
+
+ @Test
+ public void testCheck_DateTaken() throws Exception {
+ try (PickerDatabaseHelper helper = new PickerDatabaseHelperT(sIsolatedContext)) {
+ SQLiteDatabase db = helper.getWritableDatabase();
+
+ // date_taken_ms=NULL
+ ContentValues values = getBasicContentValues();
+ values.remove(KEY_DATE_TAKEN_MS);
+ values.put(KEY_CLOUD_ID, CLOUD_ID);
+ assertThat(db.insert(MEDIA_TABLE, null, values)).isEqualTo(-1);
+
+ // date_taken_ms=-1
+ values = getBasicContentValues();
+ values.put(KEY_DATE_TAKEN_MS, -1);
+ values.put(KEY_CLOUD_ID, CLOUD_ID);
+ assertThat(db.insert(MEDIA_TABLE, null, values)).isEqualTo(-1);
+
+ // date_taken_ms=NULL for Album Media
+ values = getBasicContentValues();
+ values.remove(KEY_DATE_TAKEN_MS);
+ values.put(KEY_CLOUD_ID, CLOUD_ID);
+ assertThat(db.insert(ALBUM_MEDIA_TABLE, null, values)).isEqualTo(-1);
+
+ // date_taken_ms=-1 for Album Media
+ values = getBasicContentValues();
+ values.put(KEY_DATE_TAKEN_MS, -1);
+ values.put(KEY_CLOUD_ID, CLOUD_ID);
+ assertThat(db.insert(ALBUM_MEDIA_TABLE, null, values)).isEqualTo(-1);
+ }
+ }
+
+ @Test
+ public void testCheck_GenerationModified() throws Exception {
+ try (PickerDatabaseHelper helper = new PickerDatabaseHelperT(sIsolatedContext)) {
+ SQLiteDatabase db = helper.getWritableDatabase();
+
+ // generation_modified=NULL
+ ContentValues values = getBasicContentValues();
+ values.remove(KEY_SYNC_GENERATION);
+ values.put(KEY_CLOUD_ID, CLOUD_ID);
+ assertThat(db.insert(MEDIA_TABLE, null, values)).isEqualTo(-1);
+
+ // generation_modified=-1
+ values = getBasicContentValues();
+ values.put(KEY_SYNC_GENERATION, -1);
+ values.put(KEY_CLOUD_ID, CLOUD_ID);
+ assertThat(db.insert(MEDIA_TABLE, null, values)).isEqualTo(-1);
+
+ // generation_modified=NULL for Album Media
+ values = getBasicContentValues();
+ values.remove(KEY_SYNC_GENERATION);
+ values.put(KEY_CLOUD_ID, CLOUD_ID);
+ assertThat(db.insert(ALBUM_MEDIA_TABLE, null, values)).isEqualTo(-1);
+
+ // generation_modified=-1 for Album Media
+ values = getBasicContentValues();
+ values.put(KEY_SYNC_GENERATION, -1);
+ values.put(KEY_CLOUD_ID, CLOUD_ID);
+ assertThat(db.insert(ALBUM_MEDIA_TABLE, null, values)).isEqualTo(-1);
+ }
+ }
+
+ @Test
+ public void testCheck_Duration() throws Exception {
+ try (PickerDatabaseHelper helper = new PickerDatabaseHelperT(sIsolatedContext)) {
+ SQLiteDatabase db = helper.getWritableDatabase();
+
+ // duration=-1
+ ContentValues values = getBasicContentValues();
+ values.put(KEY_DURATION_MS, -1);
+ values.put(KEY_CLOUD_ID, CLOUD_ID);
+ assertThat(db.insert(MEDIA_TABLE, null, values)).isEqualTo(-1);
+
+ // duration=-1
+ values = getBasicContentValues();
+ values.put(KEY_DURATION_MS, -1);
+ values.put(KEY_CLOUD_ID, CLOUD_ID);
+ assertThat(db.insert(ALBUM_MEDIA_TABLE, null, values)).isEqualTo(-1);
+ }
+ }
+
+ private static class PickerDatabaseHelperT extends PickerDatabaseHelper {
+ public PickerDatabaseHelperT(Context context) {
+ super(context, TEST_PICKER_DB, 1);
+ }
+ }
+
+ private static ContentValues getBasicContentValues() {
+ ContentValues values = new ContentValues();
+ values.put(KEY_DATE_TAKEN_MS, DATE_TAKEN_MS);
+ values.put(KEY_SYNC_GENERATION, GENERATION_MODIFIED);
+ values.put(KEY_DURATION_MS, DURATION_MS);
+ values.put(KEY_MIME_TYPE, MIME_TYPE);
+ values.put(KEY_STANDARD_MIME_TYPE_EXTENSION, STANDARD_MIME_TYPE_EXTENSION);
+ values.put(KEY_SIZE_BYTES, SIZE_BYTES);
+
+ return values;
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/data/PickerDbFacadeTest.java b/tests/src/com/android/providers/media/photopicker/data/PickerDbFacadeTest.java
new file mode 100644
index 0000000..264e9bd
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/data/PickerDbFacadeTest.java
@@ -0,0 +1,1243 @@
+/*
+ * 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.data;
+
+import static android.provider.CloudMediaProviderContract.AlbumColumns.ALBUM_ID_FAVORITES;
+import static android.provider.CloudMediaProviderContract.AlbumColumns.ALBUM_ID_VIDEOS;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.provider.CloudMediaProviderContract.AlbumColumns;
+import android.provider.CloudMediaProviderContract.MediaColumns;
+import android.provider.MediaStore.PickerMediaColumns;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+@RunWith(AndroidJUnit4.class)
+public class PickerDbFacadeTest {
+ private static final long SIZE_BYTES = 7000;
+ private static final long DATE_TAKEN_MS = 1623852851911L;
+ private static final long GENERATION_MODIFIED = 1L;
+ private static final long DURATION_MS = 5;
+ private static final String LOCAL_ID = "50";
+ 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 IMAGE_MIME_TYPE = "image/jpeg";
+ private static final int STANDARD_MIME_TYPE_EXTENSION =
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION_GIF;
+
+ private static final String LOCAL_PROVIDER = "com.local.provider";
+ private static final String CLOUD_PROVIDER = "com.cloud.provider";
+
+ private PickerDbFacade mFacade;
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ File dbPath = mContext.getDatabasePath(PickerDatabaseHelper.PICKER_DATABASE_NAME);
+ dbPath.delete();
+ mFacade = new PickerDbFacade(mContext, LOCAL_PROVIDER);
+ mFacade.setCloudProvider(CLOUD_PROVIDER);
+ }
+
+ @Test
+ public void testAddLocalOnlyMedia() throws Exception {
+ Cursor cursor1 = getLocalMediaCursor(LOCAL_ID, DATE_TAKEN_MS + 1);
+ Cursor cursor2 = getLocalMediaCursor(LOCAL_ID, DATE_TAKEN_MS + 2);
+
+ assertAddMediaOperation(LOCAL_PROVIDER, cursor1, 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID, DATE_TAKEN_MS + 1);
+ }
+
+ // Test updating the same row
+ assertAddMediaOperation(LOCAL_PROVIDER, cursor2, 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID, DATE_TAKEN_MS + 2);
+ }
+ }
+
+ @Test
+ public void testAddCloudPlusLocal() throws Exception {
+ Cursor cursor = getCloudMediaCursor(CLOUD_ID, LOCAL_ID, DATE_TAKEN_MS);
+
+ assertAddMediaOperation(CLOUD_PROVIDER, cursor, 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, CLOUD_ID, DATE_TAKEN_MS);
+ }
+ }
+
+ @Test
+ public void testAddCloudOnly() throws Exception {
+ Cursor cursor1 = getCloudMediaCursor(CLOUD_ID, null, DATE_TAKEN_MS + 1);
+ Cursor cursor2 = getCloudMediaCursor(CLOUD_ID, null, DATE_TAKEN_MS + 2);
+
+ assertAddMediaOperation(CLOUD_PROVIDER, cursor1, 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, CLOUD_ID, DATE_TAKEN_MS + 1);
+ }
+
+ // Test updating the same row
+ assertAddMediaOperation(CLOUD_PROVIDER, cursor2, 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, CLOUD_ID, DATE_TAKEN_MS + 2);
+ }
+ }
+
+ @Test
+ public void testAddLocalAndCloud_Dedupe() throws Exception {
+ Cursor localCursor = getLocalMediaCursor(LOCAL_ID, DATE_TAKEN_MS);
+ Cursor cloudCursor = getCloudMediaCursor(CLOUD_ID, LOCAL_ID, DATE_TAKEN_MS + 1);
+
+ assertAddMediaOperation(LOCAL_PROVIDER, localCursor, 1);
+ assertAddMediaOperation(CLOUD_PROVIDER, cloudCursor, 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID, DATE_TAKEN_MS);
+ }
+ }
+
+ @Test
+ public void testAddCloudAndLocal_Dedupe() throws Exception {
+ Cursor localCursor = getLocalMediaCursor(LOCAL_ID, DATE_TAKEN_MS + 1);
+ Cursor cloudCursor = getCloudMediaCursor(CLOUD_ID, LOCAL_ID, DATE_TAKEN_MS);
+
+ assertAddMediaOperation(CLOUD_PROVIDER, cloudCursor, 1);
+ assertAddMediaOperation(LOCAL_PROVIDER, localCursor, 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID, DATE_TAKEN_MS + 1);
+ }
+ }
+
+ @Test
+ public void testAddLocalAlbumMedia() {
+ Cursor cursor1 = getAlbumMediaCursor(LOCAL_ID, DATE_TAKEN_MS + 1, true);
+ Cursor cursor2 = getAlbumMediaCursor(LOCAL_ID, DATE_TAKEN_MS + 2, true);
+
+ assertAddAlbumMediaOperation(LOCAL_PROVIDER, cursor1, 1, ALBUM_ID);
+
+ try (Cursor cr = queryAlbumMedia(ALBUM_ID, true)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID, DATE_TAKEN_MS + 1);
+ }
+
+ // Test updating the same row. We always do a full sync for album media files.
+ assertResetAlbumMediaOperation(LOCAL_PROVIDER, 1, ALBUM_ID);
+ assertAddAlbumMediaOperation(LOCAL_PROVIDER, cursor2, 1, ALBUM_ID);
+
+ try (Cursor cr = queryAlbumMedia(ALBUM_ID, true)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID, DATE_TAKEN_MS + 2);
+ }
+ }
+
+ @Test
+ public void testAddCloudAlbumMedia() {
+ Cursor cursor1 = getAlbumMediaCursor(CLOUD_ID, DATE_TAKEN_MS + 1, false);
+ Cursor cursor2 = getAlbumMediaCursor(CLOUD_ID, DATE_TAKEN_MS + 2, false);
+
+ assertAddAlbumMediaOperation(CLOUD_PROVIDER, cursor1, 1, ALBUM_ID);
+
+ try (Cursor cr = queryAlbumMedia(ALBUM_ID, false)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, CLOUD_ID, DATE_TAKEN_MS + 1);
+ }
+
+ // Test updating the same row. We always do a full sync for album media files.
+ assertResetAlbumMediaOperation(CLOUD_PROVIDER, 1, ALBUM_ID);
+ assertAddAlbumMediaOperation(CLOUD_PROVIDER, cursor2, 1, ALBUM_ID);
+
+ try (Cursor cr = queryAlbumMedia(ALBUM_ID, false)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, CLOUD_ID, DATE_TAKEN_MS + 2);
+ }
+ }
+
+ @Test
+ public void testRemoveLocal() throws Exception {
+ Cursor localCursor = getLocalMediaCursor(LOCAL_ID, DATE_TAKEN_MS);
+
+ assertAddMediaOperation(LOCAL_PROVIDER, localCursor, 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ }
+
+ assertRemoveMediaOperation(LOCAL_PROVIDER, getDeletedMediaCursor(LOCAL_ID), 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(0);
+ }
+ }
+
+ @Test
+ public void testRemoveLocal_promote() throws Exception {
+ Cursor localCursor = getLocalMediaCursor(LOCAL_ID, DATE_TAKEN_MS);
+ Cursor cloudCursor = getCloudMediaCursor(CLOUD_ID, LOCAL_ID, DATE_TAKEN_MS);
+
+ assertAddMediaOperation(LOCAL_PROVIDER, localCursor, 1);
+ assertAddMediaOperation(CLOUD_PROVIDER, cloudCursor, 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID, DATE_TAKEN_MS);
+ }
+
+ assertRemoveMediaOperation(LOCAL_PROVIDER, getDeletedMediaCursor(LOCAL_ID), 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, CLOUD_ID, DATE_TAKEN_MS);
+ }
+ }
+
+ @Test
+ public void testRemoveCloud() throws Exception {
+ Cursor cloudCursor = getCloudMediaCursor(CLOUD_ID, LOCAL_ID, DATE_TAKEN_MS);
+
+ assertAddMediaOperation(CLOUD_PROVIDER, cloudCursor, 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ }
+
+ assertRemoveMediaOperation(CLOUD_PROVIDER, getDeletedMediaCursor(CLOUD_ID), 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(0);
+ }
+ }
+
+ @Test
+ public void testRemoveCloud_promote() throws Exception {
+ Cursor cloudCursor1 = getCloudMediaCursor(CLOUD_ID + "1", LOCAL_ID, DATE_TAKEN_MS + 1);
+ Cursor cloudCursor2 = getCloudMediaCursor(CLOUD_ID + "2", LOCAL_ID, DATE_TAKEN_MS + 2);
+
+ try (PickerDbFacade.DbWriteOperation operation =
+ mFacade.beginAddMediaOperation(CLOUD_PROVIDER)) {
+ assertWriteOperation(operation, cloudCursor1, 1);
+ assertWriteOperation(operation, cloudCursor2, 1);
+ operation.setSuccess();
+ }
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, CLOUD_ID + "1", DATE_TAKEN_MS + 1);
+ }
+
+ assertRemoveMediaOperation(CLOUD_PROVIDER, getDeletedMediaCursor(CLOUD_ID + "1"), 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, CLOUD_ID + "2", DATE_TAKEN_MS + 2);
+ }
+ }
+
+ @Test
+ public void testRemoveHidden() throws Exception {
+ Cursor localCursor = getLocalMediaCursor(LOCAL_ID, DATE_TAKEN_MS);
+ Cursor cloudCursor = getCloudMediaCursor(CLOUD_ID, LOCAL_ID, DATE_TAKEN_MS);
+
+ assertAddMediaOperation(CLOUD_PROVIDER, cloudCursor, 1);
+ assertAddMediaOperation(LOCAL_PROVIDER, localCursor, 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID, DATE_TAKEN_MS);
+ }
+
+ assertRemoveMediaOperation(CLOUD_PROVIDER, getDeletedMediaCursor(CLOUD_ID), 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID, DATE_TAKEN_MS);
+ }
+ }
+
+
+ @Test
+ public void testLocalUpdate() throws Exception {
+ Cursor localCursor1 = getLocalMediaCursor(LOCAL_ID, DATE_TAKEN_MS + 1);
+ Cursor localCursor2 = getLocalMediaCursor(LOCAL_ID, DATE_TAKEN_MS + 2);
+
+ try (PickerDbFacade.DbWriteOperation operation =
+ mFacade.beginAddMediaOperation(LOCAL_PROVIDER)) {
+ assertWriteOperation(operation, localCursor1, 1);
+ assertWriteOperation(operation, localCursor2, 1);
+ operation.setSuccess();
+ }
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID, DATE_TAKEN_MS + 2);
+ }
+
+ assertRemoveMediaOperation(LOCAL_PROVIDER, getDeletedMediaCursor(LOCAL_ID), 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(0);
+ }
+ }
+
+ @Test
+ public void testCloudUpdate_withoutLocal() throws Exception {
+ Cursor cloudCursor1 = getCloudMediaCursor(CLOUD_ID, LOCAL_ID, DATE_TAKEN_MS + 1);
+ Cursor cloudCursor2 = getCloudMediaCursor(CLOUD_ID, LOCAL_ID, DATE_TAKEN_MS + 2);
+
+ assertAddMediaOperation(CLOUD_PROVIDER, cloudCursor1, 1);
+ assertAddMediaOperation(CLOUD_PROVIDER, cloudCursor2, 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, CLOUD_ID, DATE_TAKEN_MS + 2);
+ }
+
+ assertRemoveMediaOperation(CLOUD_PROVIDER, getDeletedMediaCursor(CLOUD_ID), 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(0);
+ }
+ }
+
+ @Test
+ public void testCloudUpdate_withLocal() throws Exception {
+ Cursor localCursor = getLocalMediaCursor(LOCAL_ID, DATE_TAKEN_MS);
+ Cursor cloudCursor1 = getCloudMediaCursor(CLOUD_ID, LOCAL_ID, DATE_TAKEN_MS + 1);
+ Cursor cloudCursor2 = getCloudMediaCursor(CLOUD_ID, LOCAL_ID, DATE_TAKEN_MS + 2);
+
+ assertAddMediaOperation(LOCAL_PROVIDER, localCursor, 1);
+ assertAddMediaOperation(CLOUD_PROVIDER, cloudCursor1, 1);
+ assertAddMediaOperation(CLOUD_PROVIDER, cloudCursor2, 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID, DATE_TAKEN_MS);
+ }
+
+ assertRemoveMediaOperation(LOCAL_PROVIDER, getDeletedMediaCursor(LOCAL_ID), 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, CLOUD_ID, DATE_TAKEN_MS + 2);
+ }
+
+ assertRemoveMediaOperation(CLOUD_PROVIDER, getDeletedMediaCursor(CLOUD_ID), 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(0);
+ }
+ }
+
+ @Test
+ public void testResetLocal() throws Exception {
+ Cursor localCursor = getLocalMediaCursor(LOCAL_ID, DATE_TAKEN_MS);
+ // Add two cloud_ids mapping to the same local_id to verify that
+ // only one gets promoted
+ Cursor cloudCursor1 = getCloudMediaCursor(CLOUD_ID + "1", LOCAL_ID, DATE_TAKEN_MS);
+ Cursor cloudCursor2 = getCloudMediaCursor(CLOUD_ID + "2", LOCAL_ID, DATE_TAKEN_MS);
+
+ assertAddMediaOperation(LOCAL_PROVIDER, localCursor, 1);
+ assertAddMediaOperation(CLOUD_PROVIDER, cloudCursor1, 1);
+ assertAddMediaOperation(CLOUD_PROVIDER, cloudCursor2, 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID, DATE_TAKEN_MS);
+ }
+
+ assertResetMediaOperation(LOCAL_PROVIDER, null, 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+
+ // Verify that local_id was deleted and either of cloudCursor1 or cloudCursor2
+ // was promoted
+ assertThat(cr.getString(1)).isNotNull();
+ }
+ }
+
+ @Test
+ public void testResetCloud() throws Exception {
+ Cursor localCursor = getLocalMediaCursor(LOCAL_ID, DATE_TAKEN_MS);
+ Cursor cloudCursor = getCloudMediaCursor(CLOUD_ID, LOCAL_ID, DATE_TAKEN_MS);
+
+ assertAddMediaOperation(LOCAL_PROVIDER, localCursor, 1);
+ assertAddMediaOperation(CLOUD_PROVIDER, cloudCursor, 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID, DATE_TAKEN_MS);
+ }
+
+ assertResetMediaOperation(CLOUD_PROVIDER, null, 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID, DATE_TAKEN_MS);
+ }
+ }
+
+ @Test
+ public void testQueryWithDateTakenFilter() throws Exception {
+ Cursor localCursor = getLocalMediaCursor(LOCAL_ID, DATE_TAKEN_MS);
+ Cursor cloudCursor = getCloudMediaCursor(CLOUD_ID, LOCAL_ID, DATE_TAKEN_MS);
+
+ assertAddMediaOperation(LOCAL_PROVIDER, localCursor, 1);
+ assertAddMediaOperation(CLOUD_PROVIDER, cloudCursor, 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ }
+
+ PickerDbFacade.QueryFilterBuilder qfbBefore = new PickerDbFacade.QueryFilterBuilder(5);
+ qfbBefore.setDateTakenBeforeMs(DATE_TAKEN_MS - 1);
+ qfbBefore.setId(5);
+ try (Cursor cr = mFacade.queryMediaForUi(qfbBefore.build())) {
+ assertThat(cr.getCount()).isEqualTo(0);
+ }
+
+ PickerDbFacade.QueryFilterBuilder qfbAfter = new PickerDbFacade.QueryFilterBuilder(5);
+ qfbAfter.setDateTakenAfterMs(DATE_TAKEN_MS + 1);
+ qfbAfter.setId(5);
+ try (Cursor cr = mFacade.queryMediaForUi(qfbAfter.build())) {
+ assertThat(cr.getCount()).isEqualTo(0);
+ }
+ }
+
+ @Test
+ public void testQueryWithIdFilter() throws Exception {
+ Cursor cursor1 = getLocalMediaCursor(LOCAL_ID + "1", DATE_TAKEN_MS);
+ Cursor cursor2 = getLocalMediaCursor(LOCAL_ID + "2", DATE_TAKEN_MS);
+
+ try (PickerDbFacade.DbWriteOperation operation =
+ mFacade.beginAddMediaOperation(LOCAL_PROVIDER)) {
+ assertWriteOperation(operation, cursor1, 1);
+ assertWriteOperation(operation, cursor2, 1);
+ operation.setSuccess();
+ }
+
+ PickerDbFacade.QueryFilterBuilder qfbBefore = new PickerDbFacade.QueryFilterBuilder(5);
+ qfbBefore.setDateTakenBeforeMs(DATE_TAKEN_MS);
+ qfbBefore.setId(2);
+ try (Cursor cr = mFacade.queryMediaForUi(qfbBefore.build())) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID + "1", DATE_TAKEN_MS);
+ }
+
+ PickerDbFacade.QueryFilterBuilder qfbAfter = new PickerDbFacade.QueryFilterBuilder(5);
+ qfbAfter.setDateTakenAfterMs(DATE_TAKEN_MS);
+ qfbAfter.setId(1);
+ try (Cursor cr = mFacade.queryMediaForUi(qfbAfter.build())) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID + "2", DATE_TAKEN_MS);
+ }
+ }
+
+ @Test
+ public void testQueryWithLimit() throws Exception {
+ Cursor cursor1 = getLocalMediaCursor(LOCAL_ID + "1", DATE_TAKEN_MS);
+ Cursor cursor2 = getCloudMediaCursor(CLOUD_ID + "2", null, DATE_TAKEN_MS);
+ Cursor cursor3 = getLocalMediaCursor(LOCAL_ID + "3", DATE_TAKEN_MS);
+
+ assertAddMediaOperation(LOCAL_PROVIDER, cursor1, 1);
+ assertAddMediaOperation(CLOUD_PROVIDER, cursor2, 1);
+ assertAddMediaOperation(LOCAL_PROVIDER, cursor3, 1);
+
+ PickerDbFacade.QueryFilterBuilder qfbBefore = new PickerDbFacade.QueryFilterBuilder(1);
+ qfbBefore.setDateTakenBeforeMs(DATE_TAKEN_MS + 1);
+ qfbBefore.setId(0);
+ try (Cursor cr = mFacade.queryMediaForUi(qfbBefore.build())) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID + "3", DATE_TAKEN_MS);
+ }
+
+ PickerDbFacade.QueryFilterBuilder qfbAfter = new PickerDbFacade.QueryFilterBuilder(1);
+ qfbAfter.setDateTakenAfterMs(DATE_TAKEN_MS - 1);
+ qfbAfter.setId(0);
+ try (Cursor cr = mFacade.queryMediaForUi(qfbAfter.build())) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID + "3", DATE_TAKEN_MS);
+ }
+
+ try (Cursor cr = mFacade.queryMediaForUi(
+ new PickerDbFacade.QueryFilterBuilder(1).build())) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID + "3", DATE_TAKEN_MS);
+ }
+ }
+
+ @Test
+ public void testQueryWithSizeFilter() throws Exception {
+ Cursor cursor1 = getMediaCursor(LOCAL_ID, DATE_TAKEN_MS, GENERATION_MODIFIED,
+ /* mediaStoreUri */ null, /* sizeBytes */ 1, VIDEO_MIME_TYPE,
+ STANDARD_MIME_TYPE_EXTENSION, /* isFavorite */ false);
+ Cursor cursor2 = getMediaCursor(CLOUD_ID, DATE_TAKEN_MS, GENERATION_MODIFIED,
+ /* mediaStoreUri */ null, /* sizeBytes */ 2, VIDEO_MIME_TYPE,
+ STANDARD_MIME_TYPE_EXTENSION, /* isFavorite */ false);
+
+ assertAddMediaOperation(LOCAL_PROVIDER, cursor1, 1);
+ assertAddMediaOperation(CLOUD_PROVIDER, cursor2, 1);
+
+ // Verify all
+ PickerDbFacade.QueryFilterBuilder qfbAll = new PickerDbFacade.QueryFilterBuilder(1000);
+ qfbAll.setSizeBytes(10);
+ try (Cursor cr = mFacade.queryMediaForUi(qfbAll.build())) {
+ assertThat(cr.getCount()).isEqualTo(2);
+ }
+
+ qfbAll.setSizeBytes(1);
+ try (Cursor cr = mFacade.queryMediaForUi(qfbAll.build())) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID, VIDEO_MIME_TYPE);
+ }
+
+ // Verify after
+ PickerDbFacade.QueryFilterBuilder qfbAfter = new PickerDbFacade.QueryFilterBuilder(1000);
+ qfbAfter.setDateTakenAfterMs(DATE_TAKEN_MS - 1);
+ qfbAfter.setId(0);
+ qfbAfter.setSizeBytes(10);
+ try (Cursor cr = mFacade.queryMediaForUi(qfbAfter.build())) {
+ assertThat(cr.getCount()).isEqualTo(2);
+ }
+
+ qfbAfter.setDateTakenAfterMs(DATE_TAKEN_MS - 1);
+ qfbAfter.setId(0);
+ qfbAfter.setSizeBytes(1);
+ try (Cursor cr = mFacade.queryMediaForUi(qfbAfter.build())) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID, VIDEO_MIME_TYPE);
+ }
+
+ // Verify before
+ PickerDbFacade.QueryFilterBuilder qfbBefore = new PickerDbFacade.QueryFilterBuilder(1000);
+ qfbBefore.setDateTakenBeforeMs(DATE_TAKEN_MS + 1);
+ qfbBefore.setId(0);
+ qfbBefore.setSizeBytes(10);
+ try (Cursor cr = mFacade.queryMediaForUi(qfbBefore.build())) {
+ assertThat(cr.getCount()).isEqualTo(2);
+ }
+
+ qfbBefore.setDateTakenBeforeMs(DATE_TAKEN_MS + 1);
+ qfbBefore.setId(0);
+ qfbBefore.setSizeBytes(1);
+ try (Cursor cr = mFacade.queryMediaForUi(qfbBefore.build())) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID, VIDEO_MIME_TYPE);
+ }
+ }
+
+ @Test
+ public void testQueryWithMimeTypeFilter() throws Exception {
+ Cursor cursor1 = getMediaCursor(LOCAL_ID, DATE_TAKEN_MS, GENERATION_MODIFIED,
+ /* mediaStoreUri */ null, SIZE_BYTES, "video/webm",
+ STANDARD_MIME_TYPE_EXTENSION, /* isFavorite */ false);
+ Cursor cursor2 = getMediaCursor(CLOUD_ID, DATE_TAKEN_MS, GENERATION_MODIFIED,
+ /* mediaStoreUri */ null, SIZE_BYTES, "video/mp4",
+ STANDARD_MIME_TYPE_EXTENSION, /* isFavorite */ false);
+
+ assertAddMediaOperation(LOCAL_PROVIDER, cursor1, 1);
+ assertAddMediaOperation(CLOUD_PROVIDER, cursor2, 1);
+
+ // Verify all
+ PickerDbFacade.QueryFilterBuilder qfbAll = new PickerDbFacade.QueryFilterBuilder(1000);
+ qfbAll.setMimeType("*/*");
+ try (Cursor cr = mFacade.queryMediaForUi(qfbAll.build())) {
+ assertThat(cr.getCount()).isEqualTo(2);
+ }
+
+ qfbAll.setMimeType("video/mp4");
+ try (Cursor cr = mFacade.queryMediaForUi(qfbAll.build())) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, CLOUD_ID, DATE_TAKEN_MS);
+ }
+
+ // Verify after
+ PickerDbFacade.QueryFilterBuilder qfbAfter = new PickerDbFacade.QueryFilterBuilder(1000);
+ qfbAfter.setDateTakenAfterMs(DATE_TAKEN_MS - 1);
+ qfbAfter.setId(0);
+ qfbAfter.setMimeType("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");
+ try (Cursor cr = mFacade.queryMediaForUi(qfbAfter.build())) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID, VIDEO_MIME_TYPE);
+ }
+
+ // Verify before
+ PickerDbFacade.QueryFilterBuilder qfbBefore = new PickerDbFacade.QueryFilterBuilder(1000);
+ qfbBefore.setDateTakenBeforeMs(DATE_TAKEN_MS + 1);
+ qfbBefore.setId(0);
+ qfbBefore.setMimeType("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");
+ try (Cursor cr = mFacade.queryMediaForUi(qfbBefore.build())) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, CLOUD_ID, DATE_TAKEN_MS);
+ }
+ }
+
+ @Test
+ public void testQueryWithSizeAndMimeTypeFilter() throws Exception {
+ Cursor cursor1 = getMediaCursor(LOCAL_ID, DATE_TAKEN_MS, GENERATION_MODIFIED,
+ /* mediaStoreUri */ null, /* sizeBytes */ 2, "video/webm",
+ STANDARD_MIME_TYPE_EXTENSION, /* isFavorite */ false);
+ Cursor cursor2 = getMediaCursor(CLOUD_ID, DATE_TAKEN_MS, GENERATION_MODIFIED,
+ /* mediaStoreUri */ null, /* sizeBytes */ 1, "video/mp4",
+ STANDARD_MIME_TYPE_EXTENSION, /* isFavorite */ false);
+
+ assertAddMediaOperation(LOCAL_PROVIDER, cursor1, 1);
+ assertAddMediaOperation(CLOUD_PROVIDER, cursor2, 1);
+
+ // mime_type and size filter matches all
+ PickerDbFacade.QueryFilterBuilder qfbAll = new PickerDbFacade.QueryFilterBuilder(1000);
+ qfbAll.setMimeType("*/*");
+ 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.setSizeBytes(1);
+ try (Cursor cr = mFacade.queryMediaForUi(qfbAll.build())) {
+ assertThat(cr.getCount()).isEqualTo(0);
+ }
+ }
+
+ @Test
+ public void testQueryMediaId() throws Exception {
+ final String[] allProjection = new String[] {
+ PickerMediaColumns.DISPLAY_NAME,
+ PickerMediaColumns.DATA,
+ PickerMediaColumns.MIME_TYPE,
+ PickerMediaColumns.DATE_TAKEN,
+ PickerMediaColumns.SIZE,
+ PickerMediaColumns.DURATION_MILLIS
+ };
+
+ final String[] oneProjection = new String[] { PickerMediaColumns.DATE_TAKEN };
+
+ Cursor localCursor = getLocalMediaCursor(LOCAL_ID, DATE_TAKEN_MS);
+ Cursor cloudCursor = getCloudMediaCursor(CLOUD_ID, /* localId */ null, DATE_TAKEN_MS);
+
+ assertAddMediaOperation(LOCAL_PROVIDER, localCursor, 1);
+ assertAddMediaOperation(CLOUD_PROVIDER, cloudCursor, 1);
+
+ // Assert all projection columns
+ try (Cursor cr = mFacade.queryMediaIdForApps(LOCAL_PROVIDER, LOCAL_ID,
+ allProjection)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ cr.moveToFirst();
+ assertMediaStoreCursor(cr, LOCAL_ID, DATE_TAKEN_MS);
+ }
+
+ // Assert one projection column
+ try (Cursor cr = mFacade.queryMediaIdForApps(CLOUD_PROVIDER, CLOUD_ID,
+ oneProjection)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ cr.moveToFirst();
+ assertThat(cr.getLong(cr.getColumnIndex(PickerMediaColumns.DATE_TAKEN)))
+ .isEqualTo(DATE_TAKEN_MS);
+ }
+ }
+
+ @Test
+ public void testSetCloudProvider() throws Exception {
+ Cursor localCursor = getLocalMediaCursor(LOCAL_ID, DATE_TAKEN_MS);
+ Cursor cloudCursor = getCloudMediaCursor(CLOUD_ID, null, DATE_TAKEN_MS);
+
+ assertAddMediaOperation(LOCAL_PROVIDER, localCursor, 1);
+ assertAddMediaOperation(CLOUD_PROVIDER, cloudCursor, 1);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(2);
+
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, CLOUD_ID, DATE_TAKEN_MS);
+
+ cr.moveToNext();
+ assertCloudMediaCursor(cr, LOCAL_ID, DATE_TAKEN_MS);
+ }
+
+ // Clearing the cloud provider hides cloud media
+ mFacade.setCloudProvider(null);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(1);
+
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID, DATE_TAKEN_MS);
+ }
+
+ // Setting the cloud provider unhides cloud media
+ mFacade.setCloudProvider(CLOUD_PROVIDER);
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(2);
+
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, CLOUD_ID, DATE_TAKEN_MS);
+
+ cr.moveToNext();
+ assertCloudMediaCursor(cr, LOCAL_ID, DATE_TAKEN_MS);
+ }
+ }
+
+ @Test
+ public void testFavorites() 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);
+ Cursor localCursor2 = getMediaCursor(LOCAL_ID + "2", DATE_TAKEN_MS, GENERATION_MODIFIED,
+ /* mediaStoreUri */ null, SIZE_BYTES, VIDEO_MIME_TYPE,
+ STANDARD_MIME_TYPE_EXTENSION, /* isFavorite */ false);
+ Cursor cloudCursor1 = getMediaCursor(CLOUD_ID + "1", DATE_TAKEN_MS, GENERATION_MODIFIED,
+ /* mediaStoreUri */ null, SIZE_BYTES, VIDEO_MIME_TYPE,
+ STANDARD_MIME_TYPE_EXTENSION, /* isFavorite */ true);
+ Cursor cloudCursor2 = getMediaCursor(CLOUD_ID + "2", DATE_TAKEN_MS, GENERATION_MODIFIED,
+ /* mediaStoreUri */ null, SIZE_BYTES, VIDEO_MIME_TYPE,
+ STANDARD_MIME_TYPE_EXTENSION, /* isFavorite */ false);
+
+ try (PickerDbFacade.DbWriteOperation operation =
+ mFacade.beginAddMediaOperation(LOCAL_PROVIDER)) {
+ assertWriteOperation(operation, localCursor1, 1);
+ assertWriteOperation(operation, localCursor2, 1);
+ operation.setSuccess();
+ }
+ try (PickerDbFacade.DbWriteOperation operation =
+ mFacade.beginAddMediaOperation(CLOUD_PROVIDER)) {
+ assertWriteOperation(operation, cloudCursor1, 1);
+ assertWriteOperation(operation, cloudCursor2, 1);
+ operation.setSuccess();
+ }
+
+ PickerDbFacade.QueryFilterBuilder qfb =
+ new PickerDbFacade.QueryFilterBuilder(/* limit */ 1000);
+ try (Cursor cr = mFacade.queryMediaForUi(qfb.build())) {
+ assertThat(cr.getCount()).isEqualTo(4);
+ }
+
+ qfb.setIsFavorite(true);
+ try (Cursor cr = mFacade.queryMediaForUi(qfb.build())) {
+ assertThat(cr.getCount()).isEqualTo(2);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, CLOUD_ID + 1, DATE_TAKEN_MS);
+
+ cr.moveToNext();
+ assertCloudMediaCursor(cr, LOCAL_ID + 1, DATE_TAKEN_MS);
+ }
+ }
+
+ @Test
+ public void testGetFavoritesAlbumWithoutFilter() 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);
+ Cursor localCursor2 = getMediaCursor(LOCAL_ID + "2", DATE_TAKEN_MS, GENERATION_MODIFIED,
+ /* mediaStoreUri */ null, SIZE_BYTES, IMAGE_MIME_TYPE,
+ STANDARD_MIME_TYPE_EXTENSION, /* isFavorite */ false);
+ Cursor cloudCursor1 = getMediaCursor(CLOUD_ID + "1", DATE_TAKEN_MS, GENERATION_MODIFIED,
+ /* mediaStoreUri */ null, SIZE_BYTES, IMAGE_MIME_TYPE,
+ STANDARD_MIME_TYPE_EXTENSION, /* isFavorite */ true);
+ Cursor cloudCursor2 = getMediaCursor(CLOUD_ID + "2", DATE_TAKEN_MS, GENERATION_MODIFIED,
+ /* mediaStoreUri */ null, SIZE_BYTES, VIDEO_MIME_TYPE,
+ STANDARD_MIME_TYPE_EXTENSION, /* isFavorite */ false);
+
+ try (PickerDbFacade.DbWriteOperation operation =
+ mFacade.beginAddMediaOperation(LOCAL_PROVIDER)) {
+ assertWriteOperation(operation, localCursor1, 1);
+ assertWriteOperation(operation, localCursor2, 1);
+ operation.setSuccess();
+ }
+ try (PickerDbFacade.DbWriteOperation operation =
+ mFacade.beginAddMediaOperation(CLOUD_PROVIDER)) {
+ assertWriteOperation(operation, cloudCursor1, 1);
+ assertWriteOperation(operation, cloudCursor2, 1);
+ operation.setSuccess();
+ }
+
+ PickerDbFacade.QueryFilterBuilder qfb =
+ new PickerDbFacade.QueryFilterBuilder(/* limit */ 1000);
+ try (Cursor cr = mFacade.queryMediaForUi(qfb.build())) {
+ assertThat(cr.getCount()).isEqualTo(4);
+ }
+
+ try (Cursor cr = mFacade.getMergedAlbums(qfb.build())) {
+ assertThat(cr.getCount()).isEqualTo(2);
+ cr.moveToFirst();
+ assertCloudAlbumCursor(cr,
+ ALBUM_ID_FAVORITES,
+ ALBUM_ID_FAVORITES,
+ LOCAL_ID + "1",
+ DATE_TAKEN_MS,
+ /* count */ 2);
+ cr.moveToNext();
+ assertCloudAlbumCursor(cr,
+ ALBUM_ID_VIDEOS,
+ ALBUM_ID_VIDEOS,
+ LOCAL_ID + "1",
+ DATE_TAKEN_MS,
+ /* count */ 2);
+ }
+ }
+
+ @Test
+ public void testGetFavoritesAlbumWithMimeTypeFilter() 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);
+ Cursor localCursor2 = getMediaCursor(LOCAL_ID + "2", DATE_TAKEN_MS, GENERATION_MODIFIED,
+ /* mediaStoreUri */ null, SIZE_BYTES, IMAGE_MIME_TYPE,
+ STANDARD_MIME_TYPE_EXTENSION, /* isFavorite */ false);
+ Cursor cloudCursor1 = getMediaCursor(CLOUD_ID + "1", DATE_TAKEN_MS, GENERATION_MODIFIED,
+ /* mediaStoreUri */ null, SIZE_BYTES, IMAGE_MIME_TYPE,
+ STANDARD_MIME_TYPE_EXTENSION, /* isFavorite */ true);
+ Cursor cloudCursor2 = getMediaCursor(CLOUD_ID + "2", DATE_TAKEN_MS, GENERATION_MODIFIED,
+ /* mediaStoreUri */ null, SIZE_BYTES, VIDEO_MIME_TYPE,
+ STANDARD_MIME_TYPE_EXTENSION, /* isFavorite */ false);
+
+ try (PickerDbFacade.DbWriteOperation operation =
+ mFacade.beginAddMediaOperation(LOCAL_PROVIDER)) {
+ assertWriteOperation(operation, localCursor1, 1);
+ assertWriteOperation(operation, localCursor2, 1);
+ operation.setSuccess();
+ }
+ try (PickerDbFacade.DbWriteOperation operation =
+ mFacade.beginAddMediaOperation(CLOUD_PROVIDER)) {
+ assertWriteOperation(operation, cloudCursor1, 1);
+ assertWriteOperation(operation, cloudCursor2, 1);
+ operation.setSuccess();
+ }
+
+ PickerDbFacade.QueryFilterBuilder qfb =
+ new PickerDbFacade.QueryFilterBuilder(/* limit */ 1000);
+ try (Cursor cr = mFacade.queryMediaForUi(qfb.build())) {
+ assertThat(cr.getCount()).isEqualTo(4);
+ }
+
+ try (Cursor cr = mFacade.getMergedAlbums(qfb.build())) {
+ assertThat(cr.getCount()).isEqualTo(2);
+ cr.moveToFirst();
+ assertCloudAlbumCursor(cr,
+ ALBUM_ID_FAVORITES,
+ ALBUM_ID_FAVORITES,
+ LOCAL_ID + "1",
+ DATE_TAKEN_MS,
+ /* count */ 2);
+ cr.moveToNext();
+ assertCloudAlbumCursor(cr,
+ ALBUM_ID_VIDEOS,
+ ALBUM_ID_VIDEOS,
+ LOCAL_ID + "1",
+ DATE_TAKEN_MS,
+ /* count */ 2);
+ }
+
+ qfb.setMimeType(IMAGE_MIME_TYPE);
+ try (Cursor cr = mFacade.getMergedAlbums(qfb.build())) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertCloudAlbumCursor(cr,
+ ALBUM_ID_FAVORITES,
+ ALBUM_ID_FAVORITES,
+ CLOUD_ID + "1",
+ DATE_TAKEN_MS,
+ /* count */ 1);
+ }
+
+ qfb.setMimeType(VIDEO_MIME_TYPE);
+ try (Cursor cr = mFacade.getMergedAlbums(qfb.build())) {
+ assertThat(cr.getCount()).isEqualTo(2);
+ cr.moveToFirst();
+ assertCloudAlbumCursor(cr,
+ ALBUM_ID_FAVORITES,
+ ALBUM_ID_FAVORITES,
+ LOCAL_ID + "1",
+ DATE_TAKEN_MS,
+ /* count */ 1);
+ cr.moveToNext();
+ assertCloudAlbumCursor(cr,
+ ALBUM_ID_VIDEOS,
+ ALBUM_ID_VIDEOS,
+ LOCAL_ID + "1",
+ DATE_TAKEN_MS,
+ /* count */ 2);
+ }
+
+ qfb.setMimeType("foo");
+ try (Cursor cr = mFacade.getMergedAlbums(qfb.build())) {
+ assertThat(cr.getCount()).isEqualTo(0);
+ }
+ }
+
+ @Test
+ public void testDataColumn() throws Exception {
+ Cursor imageCursor = getMediaCursor(LOCAL_ID, DATE_TAKEN_MS, GENERATION_MODIFIED,
+ /* mediaStoreUri */ null, SIZE_BYTES, IMAGE_MIME_TYPE,
+ STANDARD_MIME_TYPE_EXTENSION, /* isFavorite */ false);
+ Cursor videoCursor = getMediaCursor(LOCAL_ID + 1, DATE_TAKEN_MS, GENERATION_MODIFIED,
+ /* mediaStoreUri */ null, SIZE_BYTES, VIDEO_MIME_TYPE,
+ STANDARD_MIME_TYPE_EXTENSION, /* isFavorite */ false);
+
+ try (PickerDbFacade.DbWriteOperation operation =
+ mFacade.beginAddMediaOperation(LOCAL_PROVIDER)) {
+ assertWriteOperation(operation, imageCursor, 1);
+ assertWriteOperation(operation, videoCursor, 1);
+ operation.setSuccess();
+ }
+
+ try (Cursor cr = queryMediaAll()) {
+ assertThat(cr.getCount()).isEqualTo(2);
+ cr.moveToFirst();
+ assertCloudMediaCursor(cr, LOCAL_ID + 1, VIDEO_MIME_TYPE);
+
+ cr.moveToNext();
+ assertCloudMediaCursor(cr, LOCAL_ID, IMAGE_MIME_TYPE);
+ }
+ }
+
+ @Test
+ public void testAddMediaFailure() throws Exception {
+ try (PickerDbFacade.DbWriteOperation operation =
+ mFacade.beginAddMediaOperation(CLOUD_PROVIDER)) {
+ assertThrows(Exception.class, () -> operation.execute(null /* cursor */));
+ }
+ }
+
+ @Test
+ public void testRemoveMediaFailure() throws Exception {
+ try (PickerDbFacade.DbWriteOperation operation =
+ mFacade.beginRemoveMediaOperation(CLOUD_PROVIDER)) {
+ assertThrows(Exception.class, () -> operation.execute(null /* cursor */));
+ }
+ }
+
+ private Cursor queryMediaAll() {
+ return mFacade.queryMediaForUi(
+ new PickerDbFacade.QueryFilterBuilder(1000).build());
+ }
+
+ private Cursor queryAlbumMedia(String albumId, boolean isLocal) {
+ final String authority = isLocal ? LOCAL_PROVIDER : CLOUD_PROVIDER;
+
+ return mFacade.queryAlbumMediaForUi(
+ new PickerDbFacade.QueryFilterBuilder(1000).setAlbumId(albumId).build(), authority);
+ }
+
+ private void assertAddMediaOperation(String authority, Cursor cursor, int writeCount) {
+ try (PickerDbFacade.DbWriteOperation operation =
+ mFacade.beginAddMediaOperation(authority)) {
+ assertWriteOperation(operation, cursor, writeCount);
+ operation.setSuccess();
+ }
+ }
+
+ private void assertAddAlbumMediaOperation(String authority, Cursor cursor, int writeCount,
+ String albumId) {
+ try (PickerDbFacade.DbWriteOperation operation =
+ mFacade.beginAddAlbumMediaOperation(authority, albumId)) {
+ assertWriteOperation(operation, cursor, writeCount);
+ operation.setSuccess();
+ }
+ }
+
+ private void assertRemoveMediaOperation(String authority, Cursor cursor, int writeCount) {
+ try (PickerDbFacade.DbWriteOperation operation =
+ mFacade.beginRemoveMediaOperation(authority)) {
+ assertWriteOperation(operation, cursor, writeCount);
+ operation.setSuccess();
+ }
+ }
+
+ private void assertResetMediaOperation(String authority, Cursor cursor, int writeCount) {
+ try (PickerDbFacade.DbWriteOperation operation =
+ mFacade.beginResetMediaOperation(authority)) {
+ assertWriteOperation(operation, cursor, writeCount);
+ operation.setSuccess();
+ }
+ }
+
+ private void assertResetAlbumMediaOperation(String authority, int writeCount,
+ String albumId) {
+ try (PickerDbFacade.DbWriteOperation operation =
+ mFacade.beginResetAlbumMediaOperation(authority, albumId)) {
+ assertWriteOperation(operation, null, writeCount);
+ operation.setSuccess();
+ }
+ }
+
+ private static void assertWriteOperation(PickerDbFacade.DbWriteOperation operation,
+ Cursor cursor, int expectedWriteCount) {
+ final int writeCount = operation.execute(cursor);
+ assertThat(writeCount).isEqualTo(expectedWriteCount);
+ }
+
+ // TODO(b/190713331): s/id/CloudMediaProviderContract#MediaColumns#ID/
+ private static Cursor getDeletedMediaCursor(String id) {
+ MatrixCursor c =
+ new MatrixCursor(new String[] {"id"});
+ c.addRow(new String[] {id});
+ return c;
+ }
+
+ private static Cursor getMediaCursor(String id, long dateTakenMs, long generationModified,
+ String mediaStoreUri, long sizeBytes, String mimeType, int standardMimeTypeExtension,
+ boolean isFavorite) {
+ String[] projectionKey = new String[] {
+ MediaColumns.ID,
+ MediaColumns.MEDIA_STORE_URI,
+ MediaColumns.DATE_TAKEN_MILLIS,
+ MediaColumns.SYNC_GENERATION,
+ MediaColumns.SIZE_BYTES,
+ MediaColumns.MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION,
+ MediaColumns.DURATION_MILLIS,
+ MediaColumns.IS_FAVORITE
+ };
+
+ String[] projectionValue = new String[] {
+ id,
+ mediaStoreUri,
+ String.valueOf(dateTakenMs),
+ String.valueOf(generationModified),
+ String.valueOf(sizeBytes),
+ mimeType,
+ String.valueOf(standardMimeTypeExtension),
+ String.valueOf(DURATION_MS),
+ String.valueOf(isFavorite ? 1 : 0)
+ };
+
+ MatrixCursor c = new MatrixCursor(projectionKey);
+ c.addRow(projectionValue);
+ return c;
+ }
+
+ private static Cursor getAlbumMediaCursor(String id, long dateTakenMs, long generationModified,
+ String mediaStoreUri, long sizeBytes, String mimeType, int standardMimeTypeExtension) {
+ String[] projectionKey = new String[] {
+ MediaColumns.ID,
+ MediaColumns.MEDIA_STORE_URI,
+ MediaColumns.DATE_TAKEN_MILLIS,
+ MediaColumns.SYNC_GENERATION,
+ MediaColumns.SIZE_BYTES,
+ MediaColumns.MIME_TYPE,
+ MediaColumns.STANDARD_MIME_TYPE_EXTENSION,
+ MediaColumns.DURATION_MILLIS,
+ };
+
+ String[] projectionValue = new String[] {
+ id,
+ mediaStoreUri,
+ String.valueOf(dateTakenMs),
+ String.valueOf(generationModified),
+ String.valueOf(sizeBytes),
+ mimeType,
+ String.valueOf(standardMimeTypeExtension),
+ String.valueOf(DURATION_MS)
+ };
+
+ MatrixCursor c = new MatrixCursor(projectionKey);
+ c.addRow(projectionValue);
+ return c;
+ }
+
+ private static Cursor getLocalMediaCursor(String localId, long dateTakenMs) {
+ return getMediaCursor(localId, dateTakenMs, GENERATION_MODIFIED, toMediaStoreUri(localId),
+ SIZE_BYTES, VIDEO_MIME_TYPE, STANDARD_MIME_TYPE_EXTENSION,
+ /* isFavorite */ false);
+ }
+
+ private static Cursor getAlbumMediaCursor(String mediaId, long dateTakenMs, boolean isLocal) {
+ return getAlbumMediaCursor(mediaId, dateTakenMs, GENERATION_MODIFIED,
+ isLocal ? toMediaStoreUri(mediaId) : null,
+ SIZE_BYTES, VIDEO_MIME_TYPE, STANDARD_MIME_TYPE_EXTENSION);
+ }
+
+ private static Cursor getCloudMediaCursor(String cloudId, String localId,
+ long dateTakenMs) {
+ return getMediaCursor(cloudId, dateTakenMs, GENERATION_MODIFIED, toMediaStoreUri(localId),
+ SIZE_BYTES, VIDEO_MIME_TYPE, STANDARD_MIME_TYPE_EXTENSION,
+ /* isFavorite */ false);
+ }
+
+ private static String toMediaStoreUri(String localId) {
+ if (localId == null) {
+ return null;
+ }
+ return "content://media/external/file/" + localId;
+ }
+
+ private static String getDisplayName(String mediaId, String mimeType) {
+ final String extension = mimeType.equals(IMAGE_MIME_TYPE)
+ ? PickerDbFacade.IMAGE_FILE_EXTENSION : PickerDbFacade.VIDEO_FILE_EXTENSION;
+ return mediaId + extension;
+ }
+
+ private static String getData(String authority, String displayName) {
+ return "/sdcard/.transforms/synthetic/picker/0/" + authority + "/media/"
+ + displayName;
+ }
+
+ private static void assertCloudAlbumCursor(Cursor cursor, String albumId, String displayName,
+ String mediaCoverId, long dateTakenMs, long mediaCount) {
+ assertThat(cursor.getString(cursor.getColumnIndex(AlbumColumns.ID)))
+ .isEqualTo(albumId);
+ assertThat(cursor.getString(cursor.getColumnIndex(AlbumColumns.DISPLAY_NAME)))
+ .isEqualTo(displayName);
+ assertThat(cursor.getString(cursor.getColumnIndex(AlbumColumns.MEDIA_COVER_ID)))
+ .isEqualTo(mediaCoverId);
+ assertThat(cursor.getLong(cursor.getColumnIndex(AlbumColumns.DATE_TAKEN_MILLIS)))
+ .isEqualTo(dateTakenMs);
+ assertThat(cursor.getLong(cursor.getColumnIndex(AlbumColumns.MEDIA_COUNT)))
+ .isEqualTo(mediaCount);
+ }
+
+ private static void assertCloudMediaCursor(Cursor cursor, String id, String mimeType) {
+ final String displayName = getDisplayName(id, mimeType);
+ final String localData = getData(LOCAL_PROVIDER, displayName);
+ final String cloudData = getData(CLOUD_PROVIDER, displayName);
+
+ assertThat(cursor.getString(cursor.getColumnIndex(MediaColumns.ID)))
+ .isEqualTo(id);
+ assertThat(cursor.getString(cursor.getColumnIndex(MediaColumns.AUTHORITY)))
+ .isEqualTo(id.startsWith(LOCAL_ID) ? LOCAL_PROVIDER : CLOUD_PROVIDER);
+ assertThat(cursor.getString(cursor.getColumnIndex(MediaColumns.DATA)))
+ .isEqualTo(id.startsWith(LOCAL_ID) ? localData : cloudData);
+ }
+
+ private static void assertCloudMediaCursor(Cursor cursor, String id, long dateTakenMs) {
+ assertCloudMediaCursor(cursor, id, VIDEO_MIME_TYPE);
+
+ assertThat(cursor.getString(cursor.getColumnIndex(MediaColumns.MIME_TYPE)))
+ .isEqualTo(VIDEO_MIME_TYPE);
+ assertThat(cursor.getInt(cursor.getColumnIndex(MediaColumns.STANDARD_MIME_TYPE_EXTENSION)))
+ .isEqualTo(STANDARD_MIME_TYPE_EXTENSION);
+ assertThat(cursor.getLong(cursor.getColumnIndex(MediaColumns.DATE_TAKEN_MILLIS)))
+ .isEqualTo(dateTakenMs);
+ assertThat(cursor.getLong(cursor.getColumnIndex(MediaColumns.SYNC_GENERATION)))
+ .isEqualTo(GENERATION_MODIFIED);
+ assertThat(cursor.getLong(cursor.getColumnIndex(MediaColumns.SIZE_BYTES)))
+ .isEqualTo(SIZE_BYTES);
+ assertThat(cursor.getLong(cursor.getColumnIndex(MediaColumns.DURATION_MILLIS)))
+ .isEqualTo(DURATION_MS);
+ }
+
+ private static void assertMediaStoreCursor(Cursor cursor, String id, long dateTakenMs) {
+ final String displayName = getDisplayName(id, VIDEO_MIME_TYPE);
+ final String localData = getData(LOCAL_PROVIDER, displayName);
+ final String cloudData = getData(CLOUD_PROVIDER, displayName);
+
+ assertThat(cursor.getString(cursor.getColumnIndex(PickerMediaColumns.DISPLAY_NAME)))
+ .isEqualTo(displayName);
+ assertThat(cursor.getString(cursor.getColumnIndex(PickerMediaColumns.DATA)))
+ .isEqualTo(id.startsWith(LOCAL_ID) ? localData : cloudData);
+ assertThat(cursor.getString(cursor.getColumnIndex(PickerMediaColumns.MIME_TYPE)))
+ .isEqualTo(VIDEO_MIME_TYPE);
+ assertThat(cursor.getLong(cursor.getColumnIndex(PickerMediaColumns.DATE_TAKEN)))
+ .isEqualTo(dateTakenMs);
+ assertThat(cursor.getLong(cursor.getColumnIndex(PickerMediaColumns.SIZE)))
+ .isEqualTo(SIZE_BYTES);
+ assertThat(cursor.getLong(cursor.getColumnIndex(PickerMediaColumns.DURATION_MILLIS)))
+ .isEqualTo(DURATION_MS);
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/data/PickerResultTest.java b/tests/src/com/android/providers/media/photopicker/data/PickerResultTest.java
new file mode 100644
index 0000000..46fa708
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/data/PickerResultTest.java
@@ -0,0 +1,234 @@
+/*
+ * 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.data;
+
+import static android.provider.MediaStore.Files.FileColumns._SPECIAL_FORMAT_NONE;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ClipData;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Environment;
+import android.provider.MediaStore;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.providers.media.PickerUriResolver;
+import com.android.providers.media.photopicker.PickerSyncController;
+import com.android.providers.media.photopicker.data.model.Item;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class PickerResultTest {
+ private static final String TAG = "PickerResultTest";
+ private static final String IMAGE_FILE_NAME = TAG + "_file_" + "%d" + ".jpg";
+
+ private Context mContext;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ }
+
+ /**
+ * Tests {@link PickerResult#getPickerResponseIntent(boolean, List)} with single item
+ * @throws Exception
+ */
+ @Test
+ public void testGetResultSingle() throws Exception {
+ List<Item> items = null;
+ try {
+ items = createItemSelection(1);
+ final Uri expectedPickerUri = PickerResult.getPickerUri(items.get(0).getContentUri());
+ final Intent intent = PickerResult.getPickerResponseIntent(
+ /* canSelectMultiple */ false, items);
+
+ final Uri result = intent.getData();
+ assertPickerUriFormat(result);
+ assertThat(result).isEqualTo(expectedPickerUri);
+ assertThat(mContext.getContentResolver().getType(result)).isEqualTo("image/jpeg");
+
+ final ClipData clipData = intent.getClipData();
+ assertThat(clipData).isNotNull();
+ final int count = clipData.getItemCount();
+ assertThat(count).isEqualTo(1);
+ assertThat(clipData.getItemAt(0).getUri()).isEqualTo(expectedPickerUri);
+ } finally {
+ deleteFiles(items);
+ }
+ }
+
+ /**
+ * Tests {@link PickerResult#getPickerResponseIntent(boolean, List)} with multiple items
+ * @throws Exception
+ */
+ @Test
+ public void testGetResultMultiple() throws Exception {
+ ArrayList<Item> items = null;
+ try {
+ final int itemCount = 3;
+ items = createItemSelection(itemCount);
+ List<Uri> expectedPickerUris = new ArrayList<>();
+ for (Item item: items) {
+ expectedPickerUris.add(PickerResult.getPickerUri(item.getContentUri()));
+ }
+ final Intent intent = PickerResult.getPickerResponseIntent(/* canSelectMultiple */ true,
+ items);
+
+ final ClipData clipData = intent.getClipData();
+ final int count = clipData.getItemCount();
+ assertThat(count).isEqualTo(itemCount);
+ for (int i = 0; i < count; i++) {
+ Uri uri = clipData.getItemAt(i).getUri();
+ assertPickerUriFormat(uri);
+ assertThat(uri).isEqualTo(expectedPickerUris.get(i));
+ }
+ } finally {
+ deleteFiles(items);
+ }
+ }
+
+ /**
+ * Tests {@link PickerResult#getPickerResponseIntent(boolean, List)} when the user selected
+ * only one item in multi-select mode
+ * @throws Exception
+ */
+ @Test
+ public void testGetResultMultiple_onlyOneItemSelected() throws Exception {
+ ArrayList<Item> items = null;
+ try {
+ final int itemCount = 1;
+ items = createItemSelection(itemCount);
+ final Uri expectedPickerUri = PickerResult.getPickerUri(items.get(0).getContentUri());
+ final Intent intent = PickerResult.getPickerResponseIntent(/* canSelectMultiple */ true,
+ items);
+
+ final ClipData clipData = intent.getClipData();
+ final int count = clipData.getItemCount();
+ assertThat(count).isEqualTo(itemCount);
+ assertPickerUriFormat(clipData.getItemAt(0).getUri());
+ assertThat(clipData.getItemAt(0).getUri()).isEqualTo(expectedPickerUri);
+ } finally {
+ deleteFiles(items);
+ }
+ }
+
+ private void assertPickerUriFormat(Uri uri) {
+ final String pickerUriPrefix = PickerUriResolver.PICKER_URI.toString();
+ assertThat(uri.toString().startsWith(pickerUriPrefix)).isTrue();
+ }
+
+ /**
+ * Returns a PhotoSelection on which the test app does not have access to.
+ */
+ private ArrayList<Item> createItemSelection(int count) throws Exception {
+ ArrayList<Item> selectedItemList = new ArrayList<>();
+
+ for (int i = 0; i < count; i++) {
+ selectedItemList.add(createImageItem());
+ }
+ return selectedItemList;
+ }
+
+ /**
+ * Returns a PhotoSelection item on which the test app does not have access to.
+ */
+ private Item createImageItem() throws Exception {
+ // Create an image and revoke test app's access on it
+ Uri imageUri = assertCreateNewImage();
+ clearMediaOwner(imageUri, mContext.getUserId());
+ // Create with a picker URI with picker db enabled
+ imageUri = PickerUriResolver
+ .getMediaUri(PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY)
+ .buildUpon()
+ .appendPath(String.valueOf(ContentUris.parseId(imageUri)))
+ .build();
+
+ return new Item(imageUri.getLastPathSegment(), "image/jpeg", /* dateTaken */ 0,
+ /* generationModified */ 0, /* duration */ 0, imageUri, _SPECIAL_FORMAT_NONE);
+ }
+
+ private Uri assertCreateNewImage() throws Exception {
+ return assertCreateNewFile(getDownloadsDir(), getImageFileName());
+ }
+
+ private Uri assertCreateNewFile(File dir, String fileName) throws Exception {
+ final File file = new File(dir, fileName);
+ assertThat(file.createNewFile()).isTrue();
+
+ // Write 1 byte because 0byte files are not valid in the picker db
+ try (FileOutputStream fos = new FileOutputStream(file)) {
+ fos.write(1);
+ }
+
+ final Uri uri = MediaStore.scanFile(mContext.getContentResolver(), file);
+ MediaStore.waitForIdle(mContext.getContentResolver());
+ return uri;
+ }
+
+ private String getImageFileName() {
+ // To help avoid flaky tests, give ourselves a unique nonce to be used for
+ // all filesystem paths, so that we don't risk conflicting with previous
+ // test runs.
+ return IMAGE_FILE_NAME + System.nanoTime() + ".jpeg";
+ }
+
+ private File getDownloadsDir() {
+ return new File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_DOWNLOADS);
+ }
+
+ private static void clearMediaOwner(Uri uri, int userId) throws IOException {
+ final String cmd = String.format(
+ "content update --uri %s --user %d --bind owner_package_name:n:",
+ uri, userId);
+ runShellCommand(InstrumentationRegistry.getInstrumentation(), cmd);
+ }
+
+ private void deleteFiles(List<Item> items) {
+ if (items == null) return;
+
+ for (Item item : items) {
+ deleteFile(item);
+ }
+ }
+
+ private void deleteFile(Item item) {
+ if (item == null) return;
+
+ final String cmd = String.format("content delete --uri %s --user %d ",
+ item.getContentUri(), mContext.getUserId());
+ try {
+ runShellCommand(InstrumentationRegistry.getInstrumentation(), cmd);
+ } catch (Exception e) {
+ // Ignore the exception but log it to help debug test failures
+ Log.d(TAG, "Couldn't delete file " + item.getContentUri(), e);
+ }
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/data/SelectionTest.java b/tests/src/com/android/providers/media/photopicker/data/SelectionTest.java
new file mode 100644
index 0000000..ec68646
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/data/SelectionTest.java
@@ -0,0 +1,296 @@
+/*
+ * 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.data;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.when;
+
+import android.app.Application;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.MediaStore;
+import android.text.format.DateUtils;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.providers.media.photopicker.data.model.Item;
+import com.android.providers.media.photopicker.data.model.ItemTest;
+import com.android.providers.media.photopicker.viewmodel.InstantTaskExecutorRule;
+import com.android.providers.media.photopicker.viewmodel.PickerViewModel;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+public class SelectionTest {
+ private Selection mSelection;
+
+ @Rule
+ public InstantTaskExecutorRule instantTaskExecutorRule = new InstantTaskExecutorRule();
+
+ @Mock
+ private Application mApplication;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ final Context context = InstrumentationRegistry.getTargetContext();
+ when(mApplication.getApplicationContext()).thenReturn(context);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mSelection = new PickerViewModel(mApplication).getSelection();
+ });
+ }
+
+ @Test
+ public void testAddSelectedItem() {
+ final String id = "1";
+ final Item item = generateFakeImageItem(id);
+
+ mSelection.addSelectedItem(item);
+
+ final Item selectedItem = mSelection.getSelectedItems().get(0);
+
+ assertThat(selectedItem.getId()).isEqualTo(item.getId());
+ assertThat(selectedItem.getDateTaken()).isEqualTo(item.getDateTaken());
+ assertThat(selectedItem.getMimeType()).isEqualTo(item.getMimeType());
+ assertThat(selectedItem.getDuration()).isEqualTo(item.getDuration());
+ }
+
+ @Test
+ public void testDeleteSelectedItem() {
+ final String id = "1";
+ final Item item = generateFakeImageItem(id);
+
+ assertThat(mSelection.getSelectedItemCount().getValue()).isEqualTo(0);
+
+ mSelection.addSelectedItem(item);
+ assertThat(mSelection.getSelectedItemCount().getValue()).isEqualTo(1);
+
+ mSelection.removeSelectedItem(item);
+ assertThat(mSelection.getSelectedItemCount().getValue()).isEqualTo(0);
+ }
+
+ @Test
+ public void testClearSelectedItem() {
+ final String id = "1";
+ final Item item = generateFakeImageItem(id);
+ final String id2 = "2";
+ final Item item2 = generateFakeImageItem(id2);
+
+ assertThat(mSelection.getSelectedItemCount().getValue()).isEqualTo(0);
+
+ mSelection.addSelectedItem(item);
+ mSelection.addSelectedItem(item2);
+ assertThat(mSelection.getSelectedItemCount().getValue()).isEqualTo(2);
+
+ mSelection.clearSelectedItems();
+ assertThat(mSelection.getSelectedItemCount().getValue()).isEqualTo(0);
+ }
+
+ @Test
+ public void testSetSelectedItem() {
+ final String id1 = "1";
+ final Item item1 = generateFakeImageItem(id1);
+ final String id2 = "2";
+ final Item item2 = generateFakeImageItem(id2);
+ final String id3 = "3";
+ final Item item3 = generateFakeImageItem(id3);
+
+ assertThat(mSelection.getSelectedItemCount().getValue()).isEqualTo(0);
+
+ mSelection.addSelectedItem(item1);
+ mSelection.addSelectedItem(item2);
+ assertThat(mSelection.getSelectedItemCount().getValue()).isEqualTo(2);
+
+ mSelection.setSelectedItem(item3);
+ assertThat(mSelection.getSelectedItemCount().getValue()).isEqualTo(1);
+ assertThat(mSelection.getSelectedItems().get(0).getContentUri())
+ .isEqualTo(item3.getContentUri());
+ }
+
+ @Test
+ public void testGetSelectedItemsForPreview_multiSelect() {
+ final String id1 = "1";
+ final Item item1 = generateFakeImageItem(id1);
+ final String id2 = "2";
+ final Item item2 = generateFakeImageItem(id2);
+ final String id3 = "3";
+ final Item item3 = generateFakeImageItem(id3);
+ final String id4 = "4";
+ final Item item4SameDateTakenAsItem3 = ItemTest.generateItem(id4, "image/jpeg",
+ item3.getDateTaken(), /* generationModified= */ 1L, /* duration */ 1000);
+
+ mSelection.addSelectedItem(item1);
+ mSelection.addSelectedItem(item2);
+ mSelection.addSelectedItem(item3);
+ mSelection.addSelectedItem(item4SameDateTakenAsItem3);
+ assertThat(mSelection.getSelectedItemCount().getValue()).isEqualTo(4);
+
+ List<Item> itemsForPreview = mSelection.getSelectedItemsForPreview();
+ assertThat(itemsForPreview.size()).isEqualTo(0);
+
+ mSelection.prepareSelectedItemsForPreviewAll();
+
+ itemsForPreview = mSelection.getSelectedItemsForPreview();
+ assertThat(itemsForPreview.size()).isEqualTo(4);
+
+ // Verify that the item list is sorted based on dateTaken
+ assertThat(itemsForPreview.get(0).getId()).isEqualTo(id4);
+ assertThat(itemsForPreview.get(1).getId()).isEqualTo(id3);
+ assertThat(itemsForPreview.get(2).getId()).isEqualTo(id2);
+ assertThat(itemsForPreview.get(3).getId()).isEqualTo(id1);
+ }
+
+ @Test
+ public void testGetSelectedItemsForPreview_singleSelect() {
+ final String id1 = "1";
+ final Item item1 = generateFakeImageItem(id1);
+
+ List<Item> itemsForPreview = mSelection.getSelectedItemsForPreview();
+ assertThat(itemsForPreview.size()).isEqualTo(0);
+
+ mSelection.prepareItemForPreviewOnLongPress(item1);
+
+ itemsForPreview = mSelection.getSelectedItemsForPreview();
+ assertThat(itemsForPreview.size()).isEqualTo(1);
+
+ // Verify that the item list has expected element.
+ assertThat(itemsForPreview.get(0).getId()).isEqualTo(id1);
+ }
+
+ @Test
+ public void testParseValuesFromIntent_allowMultipleNotSupported() {
+ final Intent intent = new Intent();
+ intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
+
+ mSelection.parseSelectionValuesFromIntent(intent);
+
+ assertThat(mSelection.canSelectMultiple()).isFalse();
+ }
+
+ @Test
+ public void testParseValuesFromIntent_setDefaultFalseForAllowMultiple() {
+ final Intent intent = new Intent();
+
+ mSelection.parseSelectionValuesFromIntent(intent);
+
+ assertThat(mSelection.canSelectMultiple()).isFalse();
+ }
+
+ @Test
+ public void testParseValuesFromIntent_validMaxSelectionLimit() {
+ final int maxLimit = MediaStore.getPickImagesMaxLimit() - 1;
+ final Intent intent = new Intent();
+ intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, maxLimit);
+
+ mSelection.parseSelectionValuesFromIntent(intent);
+
+ assertThat(mSelection.canSelectMultiple()).isTrue();
+ assertThat(mSelection.getMaxSelectionLimit()).isEqualTo(maxLimit);
+ }
+
+ @Test
+ public void testParseValuesFromIntent_negativeMaxSelectionLimit_throwsException() {
+ final Intent intent = new Intent();
+ intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
+ intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, -1);
+
+ try {
+ mSelection.parseSelectionValuesFromIntent(intent);
+ fail("The maximum selection limit is not allowed to be negative");
+ } catch (IllegalArgumentException expected) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testParseValuesFromIntent_tooLargeMaxSelectionLimit_throwsException() {
+ final Intent intent = new Intent();
+ intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit() + 1);
+
+ try {
+ mSelection.parseSelectionValuesFromIntent(intent);
+ fail("The maximum selection limit should not be greater than "
+ + "MediaStore.getPickImagesMaxLimit()");
+ } catch (IllegalArgumentException expected) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testParseValuesFromIntent_actionGetContent() {
+ final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
+
+ mSelection.parseSelectionValuesFromIntent(intent);
+
+ assertThat(mSelection.canSelectMultiple()).isTrue();
+ assertThat(mSelection.getMaxSelectionLimit())
+ .isEqualTo(MediaStore.getPickImagesMaxLimit());
+ }
+
+ @Test
+ public void testParseValuesFromIntent_actionGetContent_doesNotRespectExtraPickImagesMax() {
+ final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, 5);
+
+ try {
+ mSelection.parseSelectionValuesFromIntent(intent);
+ fail("EXTRA_PICK_IMAGES_MAX is not supported for ACTION_GET_CONTENT");
+ } catch (IllegalArgumentException expected) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testIsSelectionAllowed_exceedsMaxSelectionLimit_selectionNotAllowed() {
+ final int maxLimit = 2;
+ final Intent intent = new Intent();
+ intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
+ intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, maxLimit);
+ mSelection.parseSelectionValuesFromIntent(intent);
+
+ assertThat(mSelection.isSelectionAllowed()).isTrue();
+
+ final String id1 = "1";
+ final Item item1 = generateFakeImageItem(id1);
+ mSelection.addSelectedItem(item1);
+
+ assertThat(mSelection.isSelectionAllowed()).isTrue();
+
+ final String id2 = "2";
+ final Item item2 = generateFakeImageItem(id2);
+ mSelection.addSelectedItem(item2);
+
+ assertThat(mSelection.isSelectionAllowed()).isFalse();
+ }
+
+ private static Item generateFakeImageItem(String id) {
+ final long dateTakenMs = System.currentTimeMillis() + Long.parseLong(id)
+ * DateUtils.DAY_IN_MILLIS;
+
+ return ItemTest.generateItem(id, "image/jpeg", dateTakenMs,
+ /* generationModified= */ 1L, /* duration= */ 1000L);
+ }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/providers/media/photopicker/data/UserIdManagerTest.java b/tests/src/com/android/providers/media/photopicker/data/UserIdManagerTest.java
new file mode 100644
index 0000000..83f8fab
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/data/UserIdManagerTest.java
@@ -0,0 +1,198 @@
+/*
+ * 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.data;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.providers.media.photopicker.data.model.UserId;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class UserIdManagerTest {
+ private final UserHandle personalUser = UserHandle.SYSTEM;
+ private final UserHandle managedUser1 = UserHandle.of(100);
+ // otherUser1 and otherUser2 are users without any work (aka managed) profile.
+ private final UserHandle otherUser1 = UserHandle.of(200);
+ private final UserHandle otherUser2 = UserHandle.of(201);
+
+ private final Context mockContext = mock(Context.class);
+ private final UserManager mockUserManager = mock(UserManager.class);
+
+ private UserIdManager userIdManager;
+
+ @Before
+ public void setUp() throws Exception {
+ when(mockContext.getApplicationContext()).thenReturn(mockContext);
+
+ when(mockUserManager.isManagedProfile(managedUser1.getIdentifier())).thenReturn(true);
+ when(mockUserManager.isManagedProfile(personalUser.getIdentifier())).thenReturn(false);
+ when(mockUserManager.getProfileParent(managedUser1)).thenReturn(personalUser);
+ when(mockUserManager.isManagedProfile(otherUser1.getIdentifier())).thenReturn(false);
+ when(mockUserManager.isManagedProfile(otherUser2.getIdentifier())).thenReturn(false);
+
+ when(mockContext.getSystemServiceName(UserManager.class)).thenReturn("mockUserManager");
+ when(mockContext.getSystemService(UserManager.class)).thenReturn(mockUserManager);
+ }
+
+ @Test
+ public void testUserIdManagerThrowsErrorIfCalledFromNonMainThread() {
+ UserId currentUser = UserId.of(personalUser);
+ initializeUserIdManager(currentUser, Arrays.asList(personalUser));
+
+ assertThrows(IllegalStateException.class, () -> userIdManager.isMultiUserProfiles());
+ assertThrows(IllegalStateException.class, () -> userIdManager.getCurrentUserProfileId());
+ assertThrows(IllegalStateException.class, () -> userIdManager.isPersonalUserId());
+ assertThrows(IllegalStateException.class, () -> userIdManager.isManagedUserId());
+ assertThrows(IllegalStateException.class, () -> userIdManager.getPersonalUserId());
+ assertThrows(IllegalStateException.class, () -> userIdManager.getManagedUserId());
+ }
+
+ // common cases for User Profiles
+ @Test
+ public void testUserIds_personaUser_currentUserIsPersonalUser() {
+ // Returns the current user if there is only 1 user.
+ UserId currentUser = UserId.of(personalUser);
+ initializeUserIdManager(currentUser, Arrays.asList(personalUser));
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ assertThat(userIdManager.isMultiUserProfiles()).isFalse();
+
+ assertThat(userIdManager.isPersonalUserId()).isFalse();
+ assertThat(userIdManager.isManagedUserId()).isFalse();
+
+ assertThat(userIdManager.getPersonalUserId()).isNull();
+ assertThat(userIdManager.getManagedUserId()).isNull();
+ });
+ }
+
+ @Test
+ public void testUserIds_personalUserAndManagedUser_currentUserIsPersonalUser() {
+ // Returns both if there are personal and managed users.
+ UserId currentUser = UserId.of(personalUser);
+ initializeUserIdManager(currentUser, Arrays.asList(personalUser, managedUser1));
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ assertThat(userIdManager.isMultiUserProfiles()).isTrue();
+
+ assertThat(userIdManager.isPersonalUserId()).isTrue();
+ assertThat(userIdManager.isManagedUserId()).isFalse();
+
+ assertThat(userIdManager.getPersonalUserId()).isEqualTo(currentUser);
+ assertThat(userIdManager.getManagedUserId()).isEqualTo(UserId.of(managedUser1));
+ });
+ }
+
+ @Test
+ public void testUserIds_personalUserAndManagedUser_currentUserIsManagedUser() {
+ // Returns both if there are system and managed users.
+ UserId currentUser = UserId.of(managedUser1);
+ initializeUserIdManager(currentUser, Arrays.asList(personalUser, managedUser1));
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ assertThat(userIdManager.isMultiUserProfiles()).isTrue();
+
+ assertThat(userIdManager.isPersonalUserId()).isFalse();
+ assertThat(userIdManager.isManagedUserId()).isTrue();
+
+ assertThat(userIdManager.getPersonalUserId()).isEqualTo(UserId.of(personalUser));
+ assertThat(userIdManager.getManagedUserId()).isEqualTo(currentUser);
+ });
+ }
+
+ // other cases for User Profiles involving different users
+ @Test
+ public void testUserIds_otherUsers_currentUserIsOtherUser2() {
+ // When there is no managed user, returns the current user.
+ UserId currentUser = UserId.of(otherUser2);
+ initializeUserIdManager(currentUser, Arrays.asList(otherUser1, otherUser2));
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ assertThat(userIdManager.isMultiUserProfiles()).isFalse();
+
+ assertThat(userIdManager.isPersonalUserId()).isFalse();
+ assertThat(userIdManager.isManagedUserId()).isFalse();
+
+ assertThat(userIdManager.getPersonalUserId()).isNull();
+ assertThat(userIdManager.getManagedUserId()).isNull();
+ });
+ }
+
+ @Test
+ public void testUserIds_otherUserAndManagedUserAndPersonalUser_currentUserIsOtherUser() {
+ UserId currentUser = UserId.of(otherUser1);
+ initializeUserIdManager(currentUser, Arrays.asList(otherUser1, managedUser1,
+ personalUser));
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ assertThat(userIdManager.isMultiUserProfiles()).isFalse();
+
+ assertThat(userIdManager.isPersonalUserId()).isFalse();
+ assertThat(userIdManager.isManagedUserId()).isFalse();
+
+ assertThat(userIdManager.getPersonalUserId()).isNull();
+ assertThat(userIdManager.getManagedUserId()).isNull();
+ });
+ }
+
+ @Test
+ public void testGetUserIds_otherUserAndManagedUser_currentUserIsManagedUser() {
+ // When there is no system user, returns the current user.
+ // This is a case theoretically can happen but we don't expect. So we return the current
+ // user only.
+ UserId currentUser = UserId.of(managedUser1);
+ initializeUserIdManager(currentUser, Arrays.asList(otherUser1, managedUser1,
+ personalUser));
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ assertThat(userIdManager.isMultiUserProfiles()).isTrue();
+
+ assertThat(userIdManager.isPersonalUserId()).isFalse();
+ assertThat(userIdManager.isManagedUserId()).isTrue();
+
+ assertThat(userIdManager.getPersonalUserId()).isEqualTo(UserId.of(personalUser));
+ assertThat(userIdManager.getManagedUserId()).isEqualTo(currentUser);
+ });
+ }
+
+ @Test
+ public void testUserIds_personalUserAndManagedUser_returnCachedList() {
+ UserId currentUser = UserId.of(personalUser);
+ initializeUserIdManager(currentUser, Arrays.asList(personalUser, managedUser1));
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ assertThat(userIdManager.getPersonalUserId()).isSameInstanceAs(
+ userIdManager.getPersonalUserId());
+ assertThat(userIdManager.getManagedUserId()).isSameInstanceAs(
+ userIdManager.getManagedUserId());
+ });
+ }
+
+ private void initializeUserIdManager(UserId current, List<UserHandle> usersOnDevice) {
+ when(mockUserManager.getUserProfiles()).thenReturn(usersOnDevice);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ userIdManager = new UserIdManager.RuntimeUserIdManager(mockContext, current);
+ });
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/data/model/CategoryTest.java b/tests/src/com/android/providers/media/photopicker/data/model/CategoryTest.java
new file mode 100644
index 0000000..808f70b
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/data/model/CategoryTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.data.model;
+
+import static android.provider.CloudMediaProviderContract.AlbumColumns;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.provider.CloudMediaProviderContract;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.providers.media.photopicker.data.ItemsProvider;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class CategoryTest {
+
+ @Test
+ public void testConstructor() {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final int itemCount = 10;
+ final String categoryName = "Album";
+ final String coverId = "52";
+ final String categoryId = "Album";
+ final boolean categoryIsLocal = true;
+ final Cursor cursor = generateCursorForCategory(categoryId, categoryName,
+ coverId, itemCount, categoryIsLocal);
+ cursor.moveToFirst();
+
+ final Category category = Category.fromCursor(cursor, UserId.CURRENT_USER);
+
+ assertThat(category.getDisplayName(context)).isEqualTo(categoryName);
+ assertThat(category.isLocal()).isEqualTo(categoryIsLocal);
+ assertThat(category.getItemCount()).isEqualTo(itemCount);
+ assertThat(category.getCoverUri()).isEqualTo(ItemsProvider.getItemsUri(coverId,
+ /* authority */ "foo", UserId.CURRENT_USER));
+ assertThat(category.getId()).isEqualTo(categoryId);
+ }
+
+ private static Cursor generateCursorForCategory(String categoryId, String categoryName,
+ String coverId, int itemCount, boolean isLocal) {
+ final MatrixCursor cursor = new MatrixCursor(AlbumColumns.ALL_PROJECTION);
+ cursor.addRow(new Object[] {categoryId, 1, categoryName, coverId, itemCount,
+ "foo"});
+ return cursor;
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/data/model/ItemTest.java b/tests/src/com/android/providers/media/photopicker/data/model/ItemTest.java
new file mode 100644
index 0000000..a78bbef
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/data/model/ItemTest.java
@@ -0,0 +1,373 @@
+/*
+ * 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.data.model;
+
+import static android.provider.CloudMediaProviderContract.MediaColumns;
+import static android.provider.MediaStore.Files.FileColumns._SPECIAL_FORMAT_ANIMATED_WEBP;
+import static android.provider.MediaStore.Files.FileColumns._SPECIAL_FORMAT_GIF;
+import static android.provider.MediaStore.Files.FileColumns._SPECIAL_FORMAT_MOTION_PHOTO;
+import static android.provider.MediaStore.Files.FileColumns._SPECIAL_FORMAT_NONE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.provider.MediaStore;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.providers.media.photopicker.PickerSyncController;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.LocalDate;
+import java.time.ZoneId;
+
+@RunWith(AndroidJUnit4.class)
+public class ItemTest {
+
+ @Test
+ public void testConstructor() {
+ final String id = "1";
+ final long dateTaken = 12345678L;
+ final long generationModified = 1L;
+ final String mimeType = "image/png";
+ final long duration = 1000;
+ final Cursor cursor = generateCursorForItem(id, mimeType, dateTaken, generationModified,
+ duration, _SPECIAL_FORMAT_NONE);
+ cursor.moveToFirst();
+
+ final Item item = new Item(cursor, UserId.CURRENT_USER);
+
+ assertThat(item.getId()).isEqualTo(id);
+ assertThat(item.getDateTaken()).isEqualTo(dateTaken);
+ assertThat(item.getGenerationModified()).isEqualTo(generationModified);
+ assertThat(item.getMimeType()).isEqualTo(mimeType);
+ assertThat(item.getDuration()).isEqualTo(duration);
+ assertThat(item.getContentUri()).isEqualTo(
+ Uri.parse("content://com.android.providers.media.photopicker/media/1"));
+
+ assertThat(item.isDate()).isFalse();
+ assertThat(item.isImage()).isTrue();
+ assertThat(item.isVideo()).isFalse();
+ assertThat(item.isGifOrAnimatedWebp()).isFalse();
+ assertThat(item.isMotionPhoto()).isFalse();
+ }
+
+ @Test
+ public void testConstructor_differentUser() {
+ final String id = "1";
+ final long dateTaken = 12345678L;
+ final long generationModified = 1L;
+ final String mimeType = "image/png";
+ final long duration = 1000;
+ final Cursor cursor = generateCursorForItem(id, mimeType, dateTaken, generationModified,
+ duration, _SPECIAL_FORMAT_NONE);
+ cursor.moveToFirst();
+ final UserId userId = UserId.of(UserHandle.of(10));
+
+ final Item item = new Item(cursor, userId);
+
+ assertThat(item.getId()).isEqualTo(id);
+ assertThat(item.getDateTaken()).isEqualTo(dateTaken);
+ assertThat(item.getGenerationModified()).isEqualTo(generationModified);
+ assertThat(item.getMimeType()).isEqualTo(mimeType);
+ assertThat(item.getDuration()).isEqualTo(duration);
+ assertThat(item.getContentUri()).isEqualTo(
+ Uri.parse("content://10@com.android.providers.media.photopicker/media/1"));
+
+ assertThat(item.isImage()).isTrue();
+
+ assertThat(item.isDate()).isFalse();
+ assertThat(item.isVideo()).isFalse();
+ assertThat(item.isGifOrAnimatedWebp()).isFalse();
+ assertThat(item.isMotionPhoto()).isFalse();
+ }
+
+ @Test
+ public void testIsImage() {
+ final String id = "1";
+ final long dateTaken = 12345678L;
+ final long generationModified = 1L;
+ final String mimeType = "image/png";
+ final long duration = 1000;
+ final Item item = generateItem(id, mimeType, dateTaken, generationModified, duration);
+
+ assertThat(item.isImage()).isTrue();
+
+ assertThat(item.isDate()).isFalse();
+ assertThat(item.isVideo()).isFalse();
+ assertThat(item.isGifOrAnimatedWebp()).isFalse();
+ assertThat(item.isMotionPhoto()).isFalse();
+ }
+
+ @Test
+ public void testIsVideo() {
+ final String id = "1";
+ final long dateTaken = 12345678L;
+ final long generationModified = 1L;
+ final String mimeType = "video/mpeg";
+ final long duration = 1000;
+ final Item item = generateItem(id, mimeType, dateTaken, generationModified, duration);
+
+ assertThat(item.isVideo()).isTrue();
+
+ assertThat(item.isDate()).isFalse();
+ assertThat(item.isImage()).isFalse();
+ assertThat(item.isGifOrAnimatedWebp()).isFalse();
+ assertThat(item.isMotionPhoto()).isFalse();
+ }
+
+ @Test
+ public void testIsMotionPhoto() {
+ final String id = "1";
+ final long dateTaken = 12345678L;
+ final long generationModified = 1L;
+ final String mimeType = "image/jpeg";
+ final long duration = 1000;
+ final Item item = generateSpecialFormatItem(id, mimeType, dateTaken, generationModified,
+ duration, _SPECIAL_FORMAT_MOTION_PHOTO);
+
+ assertThat(item.isMotionPhoto()).isTrue();
+ assertThat(item.isImage()).isTrue();
+
+ assertThat(item.isGifOrAnimatedWebp()).isFalse();
+ assertThat(item.isDate()).isFalse();
+ assertThat(item.isVideo()).isFalse();
+ }
+
+ @Test
+ public void testIsGifOrAnimatedWebp() {
+ final String id = "1";
+ final long dateTaken = 12345678L;
+ final long generationModified = 1L;
+ final String mimeType = "image/jpeg";
+ final long duration = 1000;
+ final Item gifItem = generateSpecialFormatItem(id, mimeType, dateTaken, generationModified,
+ duration, _SPECIAL_FORMAT_GIF);
+
+ assertThat(gifItem.isGifOrAnimatedWebp()).isTrue();
+ assertThat(gifItem.isGif()).isTrue();
+ assertThat(gifItem.isImage()).isTrue();
+
+ assertThat(gifItem.isAnimatedWebp()).isFalse();
+ assertThat(gifItem.isDate()).isFalse();
+ assertThat(gifItem.isVideo()).isFalse();
+
+ final Item animatedWebpItem = generateSpecialFormatItem(id, mimeType, dateTaken,
+ generationModified, duration, _SPECIAL_FORMAT_ANIMATED_WEBP);
+
+ assertThat(animatedWebpItem.isGifOrAnimatedWebp()).isTrue();
+ assertThat(animatedWebpItem.isAnimatedWebp()).isTrue();
+ assertThat(animatedWebpItem.isImage()).isTrue();
+
+ assertThat(animatedWebpItem.isGif()).isFalse();
+ assertThat(animatedWebpItem.isDate()).isFalse();
+ assertThat(animatedWebpItem.isVideo()).isFalse();
+ }
+
+ @Test
+ public void testIsGifDoesNotUseMimeType() {
+ final String id = "1";
+ final long dateTaken = 12345678L;
+ final long generationModified = 1L;
+ final String mimeType = "image/gif";
+ final long duration = 1000;
+ final Item item = generateSpecialFormatItem(id, mimeType, dateTaken, generationModified,
+ duration, _SPECIAL_FORMAT_NONE);
+
+ assertThat(item.isImage()).isTrue();
+
+ assertThat(item.isGifOrAnimatedWebp()).isFalse();
+ assertThat(item.isDate()).isFalse();
+ assertThat(item.isVideo()).isFalse();
+ assertThat(item.isMotionPhoto()).isFalse();
+ }
+
+ @Test
+ public void testCreateDateItem() {
+ final long dateTaken = 12345678L;
+
+ final Item item = Item.createDateItem(dateTaken);
+
+ assertThat(item.getDateTaken()).isEqualTo(dateTaken);
+ assertThat(item.isDate()).isTrue();
+ }
+
+ @Test
+ public void testCompareTo_differentDateTaken() {
+ final String id1 = "1";
+ final long dateTaken1 = 1000000L;
+ final long generationModified1 = 1L;
+ final Item item1 = generateJpegItem(id1, dateTaken1, generationModified1);
+
+ final String id2 = "2";
+ final long dateTaken2 = 20000000L;
+ final long generationModified2 = 2L;
+ final Item item2 = generateJpegItem(id2, dateTaken2, generationModified2);
+
+ assertThat(item1.compareTo(item2)).isEqualTo(-1);
+ assertThat(item2.compareTo(item1)).isEqualTo(1);
+ }
+
+ @Test
+ public void testCompareTo_sameDateTaken() {
+ final long dateTaken = 12345678L;
+ final long generationModified = 1L;
+
+ final String id1 = "1";
+ final Item item1 = generateJpegItem(id1, dateTaken, generationModified);
+
+ final String id2 = "2";
+ final Item item2 = generateJpegItem(id2, dateTaken, generationModified);
+
+ assertThat(item1.compareTo(item2)).isEqualTo(-1);
+ assertThat(item2.compareTo(item1)).isEqualTo(1);
+
+ // Compare the same object
+ assertThat(item2.compareTo(item2)).isEqualTo(0);
+
+ // Compare two items with same dateTaken and same id. This will never happen in real world
+ // use-case because ids are always unique.
+ final Item item2SameValues = generateJpegItem(id2, dateTaken, generationModified);
+ assertThat(item2SameValues.compareTo(item2)).isEqualTo(0);
+ }
+
+ @Test
+ public void testGetContentDescription() {
+ final String id = "1";
+ final long dateTaken = LocalDate.of(2020 /* year */, 7 /* month */, 7 /* dayOfMonth */)
+ .atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
+ final long generationModified = 1L;
+ final long duration = 1000;
+ final Context context = InstrumentationRegistry.getTargetContext();
+
+ Item item = generateItem(id, "image/jpeg", dateTaken, generationModified, duration);
+ assertThat(item.getContentDescription(context))
+ .isEqualTo("Photo taken on Jul 7, 2020, 12:00:00 AM");
+
+ item = generateItem(id, "video/mp4", dateTaken, generationModified, duration);
+ assertThat(item.getContentDescription(context)).isEqualTo(
+ "Video taken on Jul 7, 2020, 12:00:00 AM with duration " + item.getDurationText());
+
+ item = generateSpecialFormatItem(id, "image/gif", dateTaken, generationModified, duration,
+ _SPECIAL_FORMAT_GIF);
+ assertThat(item.getContentDescription(context))
+ .isEqualTo("GIF taken on Jul 7, 2020, 12:00:00 AM");
+
+ item = generateSpecialFormatItem(id, "image/webp", dateTaken, generationModified, duration,
+ _SPECIAL_FORMAT_ANIMATED_WEBP);
+ assertThat(item.getContentDescription(context))
+ .isEqualTo("GIF taken on Jul 7, 2020, 12:00:00 AM");
+
+ item = generateSpecialFormatItem(id, "image/jpeg", dateTaken, generationModified, duration,
+ _SPECIAL_FORMAT_MOTION_PHOTO);
+ assertThat(item.getContentDescription(context))
+ .isEqualTo("Motion Photo taken on Jul 7, 2020, 12:00:00 AM");
+ }
+
+ @Test
+ public void testGetDurationText() {
+ final String id = "1";
+ final long dateTaken = LocalDate.of(2020 /* year */, 7 /* month */, 7 /* dayOfMonth */)
+ .atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
+ final long generationModified = 1L;
+
+ // no duration
+ Item item = generateItem(id, "video", dateTaken, generationModified, -1);
+ assertThat(item.getDurationText()).isEqualTo("");
+
+ // duration = 1000 ms
+ item = generateItem(id, "video", dateTaken, generationModified, 1000);
+ assertThat(item.getDurationText()).isEqualTo("00:01");
+
+ // duration = 10000 ms
+ item = generateItem(id, "video", dateTaken, generationModified, 10000);
+ assertThat(item.getDurationText()).isEqualTo("00:10");
+
+ // duration = 60000 ms
+ item = generateItem(id, "video", dateTaken, generationModified, 60000);
+ assertThat(item.getDurationText()).isEqualTo("01:00");
+
+ // duration = 600000 ms
+ item = generateItem(id, "video", dateTaken, generationModified, 600000);
+ assertThat(item.getDurationText()).isEqualTo("10:00");
+ }
+
+ private static Cursor generateCursorForItem(String id, String mimeType, long dateTaken,
+ long generationModified, long duration, int specialFormat) {
+ final MatrixCursor cursor = new MatrixCursor(MediaColumns.ALL_PROJECTION);
+ cursor.addRow(new Object[] {
+ id,
+ dateTaken,
+ generationModified,
+ mimeType,
+ specialFormat,
+ "1", // size_bytes
+ null, // media_store_uri
+ duration,
+ "0", // is_favorite
+ "/storage/emulated/0/foo", // data
+ PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY});
+ return cursor;
+ }
+
+ private static Item generateJpegItem(String id, long dateTaken, long generationModified) {
+ final String mimeType = "image/jpeg";
+ final long duration = 1000;
+ return generateItem(id, mimeType, dateTaken, generationModified, duration);
+ }
+
+ /**
+ * Generate the {@link Item}
+ * @param id the id
+ * @param mimeType the mime type
+ * @param dateTaken the time of date taken
+ * @param generationModified the generation number associated with the media
+ * @param duration the duration
+ * @return the Item
+ */
+ public static Item generateItem(String id, String mimeType, long dateTaken,
+ long generationModified, long duration) {
+ return new Item(id, mimeType, dateTaken, generationModified, duration,
+ MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL, Long.parseLong(id)),
+ _SPECIAL_FORMAT_NONE);
+ }
+
+ /**
+ * Generate the {@link Item}
+ * @param id the id
+ * @param mimeType the mime type
+ * @param dateTaken the time of date taken
+ * @param generationModified the generation number associated with the media
+ * @param duration the duration
+ * @param specialFormat the special format. See
+ * {@link MediaStore.Files.FileColumns#_SPECIAL_FORMAT}
+ * @return the Item
+ */
+ public static Item generateSpecialFormatItem(String id, String mimeType, long dateTaken,
+ long generationModified, long duration, int specialFormat) {
+ return new Item(id, mimeType, dateTaken, generationModified, duration,
+ MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL, Long.parseLong(id)),
+ specialFormat);
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/ActiveProfileButtonTest.java b/tests/src/com/android/providers/media/photopicker/espresso/ActiveProfileButtonTest.java
new file mode 100644
index 0000000..e96d45c
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/ActiveProfileButtonTest.java
@@ -0,0 +1,152 @@
+/*
+ * 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.espresso;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isSelected;
+import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withParent;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.assertItemNotSelected;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.assertItemSelected;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.clickItem;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.not;
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+
+import com.android.providers.media.R;
+
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4ClassRunner.class)
+public class ActiveProfileButtonTest extends PhotoPickerBaseTest {
+ private static final int PROFILE_BUTTON = R.id.profile_button;
+
+ @BeforeClass
+ public static void setupClass() throws Exception {
+ PhotoPickerBaseTest.setupClass();
+ PhotoPickerBaseTest.setUpActiveProfileButton();
+ }
+
+ @Rule
+ public ActivityScenarioRule<PhotoPickerTestActivity> mRule =
+ new ActivityScenarioRule<>(PhotoPickerBaseTest.getMultiSelectionIntent());
+
+ @Test
+ public void testProfileButton_hideInAlbumPhotos() throws Exception {
+ // Verify profile button is displayed
+ onView(withId(PROFILE_BUTTON)).check(matches(isDisplayed()));
+
+ // Goto Albums page
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .perform(click());
+ // Verify profile button is displayed
+ onView(withId(PROFILE_BUTTON)).check(matches(isDisplayed()));
+
+ // Navigate to photos in Camera album
+ onView(allOf(withText(R.string.picker_category_camera),
+ isDescendantOfA(withId(PICKER_TAB_RECYCLERVIEW_ID)))).perform(click());
+ // Verify profile button is not displayed
+ onView(withId(PROFILE_BUTTON)).check(matches(not(isDisplayed())));
+
+ // Click back button
+ onView(withContentDescription("Navigate up")).perform(click());
+
+ // on clicking back button we are back to Album grid
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .check(matches(isSelected()));
+ // Verify profile button is displayed
+ onView(withId(PROFILE_BUTTON)).check(matches(isDisplayed()));
+
+ // Goto Photos grid
+ onView(allOf(withText(PICKER_PHOTOS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .perform(click());
+ // Verify profile button is displayed
+ onView(withId(PROFILE_BUTTON)).check(matches(isDisplayed()));
+ }
+
+ @Test
+ public void testProfileButton_hideOnItemSelection() throws Exception {
+ // Verify profile button is displayed
+ onView(withId(PROFILE_BUTTON)).check(matches(isDisplayed()));
+
+ // Select 1st item thumbnail and verify profile button is not shown
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
+ assertItemSelected(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_CHECK_ID);
+
+ onView(withId(PROFILE_BUTTON)).check(matches(not(isDisplayed())));
+
+ // Deselect the item to check profile button is shown
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
+ assertItemNotSelected(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_CHECK_ID);
+
+ onView(withId(PROFILE_BUTTON)).check(matches(isDisplayed()));
+
+ // Select 1st item thumbnail and verify profile button is not shown
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
+ assertItemSelected(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_CHECK_ID);
+ onView(withId(PROFILE_BUTTON)).check(matches(not(isDisplayed())));
+
+ // Goto Albums page and verify profile button is not shown
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .perform(click());
+ onView(withId(PROFILE_BUTTON)).check(matches(not(isDisplayed())));
+ }
+
+ @Test
+ public void testProfileButton_doesNotShowErrorDialog() throws Exception {
+ // Verify profile button is displayed
+ onView(withId(PROFILE_BUTTON)).check(matches(isDisplayed()));
+ // Check the text on the button. It should be "Switch to work"
+ onView(withText(R.string.picker_work_profile)).check(matches(isDisplayed()));
+
+ // verify clicking it does not open error dialog
+ onView(withId(PROFILE_BUTTON)).check(matches(isDisplayed())).perform(click());
+ onView(withText(R.string.picker_profile_admin_title)).check(doesNotExist());
+ onView(withText(R.string.picker_profile_work_paused_title)).check(doesNotExist());
+
+ // Clicking the button, it takes a few ms to change the string.
+ // Wait 100ms to be sure.
+ // TODO(b/201982046): Replace with more stable workaround using Espresso idling resources
+ Thread.sleep(100);
+ onView(withText(R.string.picker_personal_profile)).check(matches(isDisplayed()));
+
+ // verify clicking it does not open error dialog
+ onView(withId(PROFILE_BUTTON)).check(matches(isDisplayed())).perform(click());
+ onView(withText(R.string.picker_profile_admin_title)).check(doesNotExist());
+ onView(withText(R.string.picker_profile_work_paused_title)).check(doesNotExist());
+
+ // Clicking the button, it takes a few ms to change the string.
+ // Wait 100ms to be sure.
+ // TODO(b/201982046): Replace with more stable workaround using Espresso idling resources
+ Thread.sleep(100);
+ onView(withText(R.string.picker_work_profile)).check(matches(isDisplayed()));
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/AlbumsTabTest.java b/tests/src/com/android/providers/media/photopicker/espresso/AlbumsTabTest.java
new file mode 100644
index 0000000..0ab97db
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/AlbumsTabTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.espresso;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withParent;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static com.android.providers.media.photopicker.espresso.RecyclerViewMatcher.withRecyclerView;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.assertItemDisplayed;
+
+import static org.hamcrest.Matchers.allOf;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+
+import com.android.providers.media.R;
+
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4ClassRunner.class)
+public class AlbumsTabTest extends PhotoPickerBaseTest {
+
+ // TODO(b/192304192): We need to use multi selection mode to go into full screen to check all
+ // the categories. Remove this when we can change BottomSheet behavior from test.
+ @Rule
+ public ActivityScenarioRule<PhotoPickerTestActivity> mRule =
+ new ActivityScenarioRule<>(PhotoPickerBaseTest.getMultiSelectionIntent());
+
+ @Ignore("b/227478958 Odd failure to verify Downloads album")
+ @Test
+ public void testAlbumGrid() {
+ // Goto Albums page
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .perform(click());
+
+ // Verify that toolbar has correct components
+ onView(withId(TAB_LAYOUT_ID)).check(matches((isDisplayed())));
+ // Photos tab
+ onView(allOf(withText(PICKER_PHOTOS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .check(matches((isDisplayed())));
+ // Albums tab
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .check(matches((isDisplayed())));
+ // Cancel button
+ final String cancelString =
+ InstrumentationRegistry.getTargetContext().getResources().getString(
+ android.R.string.cancel);
+ onView(withContentDescription(cancelString)).check(matches((isDisplayed())));
+
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ final int expectedAlbumCount = 3;
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID))
+ .check(new RecyclerViewItemCountAssertion(expectedAlbumCount));
+
+ // First album is Camera
+ assertItemContentInAlbumList(/* position */ 0, R.string.picker_category_videos);
+ // Second album is Videos
+ assertItemContentInAlbumList(/* position */ 1, R.string.picker_category_camera);
+ // Third album is Downloads
+ assertItemContentInAlbumList(/* position */ 2, R.string.picker_category_downloads);
+
+ // TODO(b/200513628): Check the bitmap of the album covers
+ }
+
+ private void assertItemContentInAlbumList(int position, int albumNameResId) {
+ // Verify the components are shown on the album item
+ assertItemDisplayed(PICKER_TAB_RECYCLERVIEW_ID, position, R.id.album_name);
+ assertItemDisplayed(PICKER_TAB_RECYCLERVIEW_ID, position, R.id.item_count);
+ assertItemDisplayed(PICKER_TAB_RECYCLERVIEW_ID, position, R.id.icon_thumbnail);
+
+ // Verify we have the album in the list
+ onView(allOf(withText(albumNameResId), isDescendantOfA(withId(PICKER_TAB_RECYCLERVIEW_ID))))
+ .check(matches(isDisplayed()));
+
+ // Verify the position of the album name matches the correct order
+ onView(withRecyclerView(PICKER_TAB_RECYCLERVIEW_ID)
+ .atPositionOnView(position, R.id.album_name))
+ .check(matches(withText(albumNameResId)));
+
+ // Verify the item count is correct
+ onView(withRecyclerView(PICKER_TAB_RECYCLERVIEW_ID)
+ .atPositionOnView(position, R.id.item_count))
+ .check(matches(withText("1 item")));
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/BlockedByAdminProfileButtonTest.java b/tests/src/com/android/providers/media/photopicker/espresso/BlockedByAdminProfileButtonTest.java
new file mode 100644
index 0000000..5ad647b
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/BlockedByAdminProfileButtonTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.espresso;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+
+import com.android.providers.media.R;
+
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4ClassRunner.class)
+public class BlockedByAdminProfileButtonTest extends PhotoPickerBaseTest {
+ @BeforeClass
+ public static void setupClass() throws Exception {
+ PhotoPickerBaseTest.setupClass();
+ PhotoPickerBaseTest.setUpBlockedByAdminProfileButton();
+ }
+
+ @Rule
+ public ActivityScenarioRule<PhotoPickerTestActivity> mRule =
+ new ActivityScenarioRule<>(PhotoPickerBaseTest.getMultiSelectionIntent());
+
+ @Test
+ public void testProfileButton_dialog() throws Exception {
+ final int profileButtonId = R.id.profile_button;
+ // Verify profile button is displayed
+ onView(withId(profileButtonId)).check(matches(isDisplayed()));
+ // Check the text on the button. It should be "Switch to personal"
+ onView(withText(R.string.picker_personal_profile)).check(matches(isDisplayed()));
+
+ // Verify onClick shows a dialog
+ onView(withId(profileButtonId)).check(matches(isDisplayed())).perform(click());
+ onView(withText(R.string.picker_profile_admin_title)).check(matches(isDisplayed()));
+ onView(withText(R.string.picker_profile_admin_msg_from_work)).check(matches(isDisplayed()));
+
+ onView(withText(android.R.string.ok)).check(matches(isDisplayed())).perform(click());
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/BottomSheetIdlingResource.java b/tests/src/com/android/providers/media/photopicker/espresso/BottomSheetIdlingResource.java
new file mode 100644
index 0000000..93322aa
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/BottomSheetIdlingResource.java
@@ -0,0 +1,109 @@
+/*
+ * 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.espresso;
+
+import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_DRAGGING;
+import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_SETTLING;
+
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.test.espresso.IdlingRegistry;
+import androidx.test.espresso.IdlingResource;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+
+import com.android.providers.media.R;
+
+import com.google.android.material.bottomsheet.BottomSheetBehavior;
+
+public class BottomSheetIdlingResource implements IdlingResource {
+ private static final int NO_EXPECTED_STATE = -1;
+ private final BottomSheetBehavior<View> mBottomSheetBehavior;
+ private ResourceCallback mResourceCallback;
+ private int mExpectedState = NO_EXPECTED_STATE;
+
+ public BottomSheetIdlingResource(View bottomSheetView) {
+ mBottomSheetBehavior = BottomSheetBehavior.from(bottomSheetView);
+ mBottomSheetBehavior.addBottomSheetCallback(new IdleListener());
+ }
+
+ @Override
+ public String getName() {
+ return BottomSheetIdlingResource.class.getName();
+ }
+
+ @Override
+ public boolean isIdleNow() {
+ int state = mBottomSheetBehavior.getState();
+
+ if (isIdleState(state)) {
+ if (mResourceCallback != null) {
+ mResourceCallback.onTransitionToIdle();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
+ mResourceCallback = resourceCallback;
+ }
+
+ private boolean isExpectedState(int state) {
+ // Checks if expected state is not set or if the current state is as expected.
+ return mExpectedState == NO_EXPECTED_STATE || state == mExpectedState;
+ }
+
+ private boolean isIdleState(int state) {
+ return state != STATE_DRAGGING && state != STATE_SETTLING;
+ }
+
+ /**
+ * Set expected state for BottomSheet to stabilise BottomSheet tests. This waits for
+ * BottomSheet to come to the expected state.
+ */
+ public void setExpectedState(int state) {
+ mExpectedState = state;
+ }
+
+ private final class IdleListener extends BottomSheetBehavior.BottomSheetCallback {
+ @Override
+ public void onStateChanged(@NonNull View bottomSheet, int newState) {
+ if (mResourceCallback != null && isIdleNow() && isExpectedState(newState)) {
+ mResourceCallback.onTransitionToIdle();
+ }
+ }
+
+ @Override
+ public void onSlide(@NonNull View bottomSheet, float slideOffset) {}
+ }
+
+ /**
+ * @return {@link BottomSheetIdlingResource} that is registered to the activity
+ * related to the given {@link ActivityScenarioRule}.
+ */
+ public static BottomSheetIdlingResource register(ActivityScenarioRule rule) {
+ final BottomSheetIdlingResource[] idlingResources = new BottomSheetIdlingResource[1];
+ rule.getScenario().onActivity((activity -> {
+ idlingResources[0] = new BottomSheetIdlingResource(
+ activity.findViewById(R.id.bottom_sheet));
+ }));
+ IdlingRegistry.getInstance().register(idlingResources[0]);
+ return idlingResources[0];
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/BottomSheetTestUtils.java b/tests/src/com/android/providers/media/photopicker/espresso/BottomSheetTestUtils.java
new file mode 100644
index 0000000..7b23d1b
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/BottomSheetTestUtils.java
@@ -0,0 +1,75 @@
+/*
+ * 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.espresso;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+
+import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED;
+import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED;
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Rect;
+import android.view.View;
+import android.view.WindowManager;
+
+import androidx.test.espresso.IdlingRegistry;
+import androidx.test.espresso.action.ViewActions;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+
+import com.android.providers.media.R;
+
+import com.google.android.material.bottomsheet.BottomSheetBehavior;
+
+class BottomSheetTestUtils {
+ public static void assertBottomSheetState(Activity activity, int state) {
+ final BottomSheetBehavior<View> bottomSheetBehavior =
+ BottomSheetBehavior.from(activity.findViewById(R.id.bottom_sheet));
+ assertThat(bottomSheetBehavior.getState()).isEqualTo(state);
+ if (state == STATE_COLLAPSED) {
+ final int peekHeight =
+ getBottomSheetPeekHeight(PhotoPickerBaseTest.getIsolatedContext());
+ assertThat(bottomSheetBehavior.getPeekHeight()).isEqualTo(peekHeight);
+ }
+ }
+
+ public static void swipeUp(ActivityScenarioRule<PhotoPickerTestActivity> rule) {
+ // Register bottom sheet idling resource so that we don't read bottom sheet state when
+ // in between changing states
+ final BottomSheetIdlingResource bottomSheetIdlingResource =
+ BottomSheetIdlingResource.register(rule);
+
+ try {
+ // Swipe up and check that the PhotoPicker is in full screen mode
+ bottomSheetIdlingResource.setExpectedState(STATE_EXPANDED);
+ onView(withId(R.id.privacy_text)).perform(ViewActions.swipeUp());
+ rule.getScenario().onActivity(activity -> {
+ assertBottomSheetState(activity, STATE_EXPANDED);
+ });
+ } finally {
+ IdlingRegistry.getInstance().unregister(bottomSheetIdlingResource);
+ }
+ }
+
+ private static int getBottomSheetPeekHeight(Context context) {
+ final WindowManager windowManager = context.getSystemService(WindowManager.class);
+ final Rect displayBounds = windowManager.getCurrentWindowMetrics().getBounds();
+ return (int) (displayBounds.height() * 0.60);
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/CustomSwipeAction.java b/tests/src/com/android/providers/media/photopicker/espresso/CustomSwipeAction.java
new file mode 100644
index 0000000..120d796
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/CustomSwipeAction.java
@@ -0,0 +1,93 @@
+/*
+ * 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.espresso;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+
+import android.view.View;
+
+import androidx.test.espresso.Espresso;
+import androidx.test.espresso.UiController;
+import androidx.test.espresso.ViewAction;
+import androidx.test.espresso.action.GeneralLocation;
+import androidx.test.espresso.action.GeneralSwipeAction;
+import androidx.test.espresso.action.Press;
+import androidx.test.espresso.action.Swipe;
+import androidx.test.espresso.action.ViewActions;
+import androidx.test.espresso.matcher.ViewMatchers;
+
+import com.android.providers.media.R;
+
+import org.hamcrest.Matcher;
+
+public class CustomSwipeAction {
+
+ /**
+ * A custom swipeLeft method to avoid system gestures taking over ViewActions#swipeLeft
+ */
+ private static ViewAction customSwipeLeft() {
+ return new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER,
+ GeneralLocation.CENTER_LEFT, Press.FINGER);
+ }
+
+ public static void swipeLeftAndWait(int viewId) {
+ onView(withId(viewId)).perform(customSwipeLeft());
+ Espresso.onIdle();
+ }
+
+ /**
+ * A custom swipeRight method to avoid system gestures taking over ViewActions#swipeRight
+ */
+ private static ViewAction customSwipeRight() {
+ return new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER,
+ GeneralLocation.CENTER_RIGHT, Press.FINGER);
+ }
+
+ public static void swipeRightAndWait(int viewId) {
+ // Use customSwipeRight to avoid system gestures taking over ViewActions#swipeRight
+ onView(withId(viewId)).perform(customSwipeRight());
+ Espresso.onIdle();
+ }
+
+ /**
+ * A custom swipeDown method to avoid 90% visibility criteria on a view
+ */
+ public static ViewAction customSwipeDownPartialScreen() {
+ return withCustomConstraints(ViewActions.swipeDown(),
+ ViewMatchers.isDisplayingAtLeast(/* areaPercentage */ 60));
+ }
+
+ private static ViewAction withCustomConstraints(ViewAction action, Matcher<View> constraints) {
+ return new ViewAction() {
+ @Override
+ public Matcher<View> getConstraints() {
+ return constraints;
+ }
+
+ @Override
+ public String getDescription() {
+ return action.getDescription();
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ action.perform(uiController, view);
+ }
+ };
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/MaxSelectionTest.java b/tests/src/com/android/providers/media/photopicker/espresso/MaxSelectionTest.java
new file mode 100644
index 0000000..c025cc0
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/MaxSelectionTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.espresso;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.assertItemNotSelected;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.assertItemSelected;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.clickItem;
+
+import android.view.View;
+
+import androidx.test.espresso.Espresso;
+import androidx.test.espresso.IdlingRegistry;
+import androidx.test.espresso.IdlingResource;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4ClassRunner.class)
+public class MaxSelectionTest extends PhotoPickerBaseTest {
+ private static final int MAX_SELECTION_COUNT = 2;
+
+ @Rule
+ public ActivityScenarioRule<PhotoPickerTestActivity> mRule = new ActivityScenarioRule<>(
+ PhotoPickerBaseTest.getMultiSelectionIntent(MAX_SELECTION_COUNT));
+
+ @Test
+ public void testMaxSelection() {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Select first image item thumbnail and verify select icon is selected
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
+ assertItemSelected(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_CHECK_ID);
+
+ // Select second image item thumbnail and verify select icon is selected
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_2_POSITION, ICON_THUMBNAIL_ID);
+ assertItemSelected(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_2_POSITION, ICON_CHECK_ID);
+
+ // Click Video item thumbnail and verify select icon is not selected. Because we set the
+ // max selection is 2.
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, VIDEO_POSITION, ICON_THUMBNAIL_ID);
+ assertItemNotSelected(PICKER_TAB_RECYCLERVIEW_ID, VIDEO_POSITION, ICON_CHECK_ID);
+ // Show pop up message
+ onView(withText("Select up to 2 items")).check(matches(isDisplayed()));
+
+ // Wait for the snackbar is dismissed
+ registerSnackBarDetachedAndWaitForIdle();
+
+ // Click View selected button
+ onView(withId(VIEW_SELECTED_BUTTON_ID)).perform(click());
+ onView(withId(PREVIEW_VIEW_PAGER_ID)).check(matches(isDisplayed()));
+
+ // Click back button and verify we are back to photos tab
+ onView(withContentDescription("Navigate up")).perform(click());
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // DeSelect second image item and verify select icon is not selected
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_2_POSITION, ICON_THUMBNAIL_ID);
+ assertItemNotSelected(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_2_POSITION, ICON_CHECK_ID);
+
+ // Click Video item thumbnail and verify select icon is selected.
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, VIDEO_POSITION, ICON_THUMBNAIL_ID);
+ assertItemSelected(PICKER_TAB_RECYCLERVIEW_ID, VIDEO_POSITION, ICON_CHECK_ID);
+ // The pop up message is not shown
+ onView(withText("Select up to 2 items")).check(doesNotExist());
+ }
+
+ private void registerSnackBarDetachedAndWaitForIdle() {
+ registerViewDetachedIdlingResourceAndWaitForIdle(
+ com.google.android.material.R.id.snackbar_text);
+ }
+
+ private void registerViewDetachedIdlingResourceAndWaitForIdle(int viewResId) {
+ mRule.getScenario().onActivity(activity -> {
+ final View view = activity.findViewById(viewResId);
+ IdlingRegistry.getInstance().register(new ViewDetachedIdlingResource(view));
+ });
+ Espresso.onIdle();
+ }
+
+ private static class ViewDetachedIdlingResource implements IdlingResource {
+
+ private boolean mIsDetached = false;
+ private IdlingResource.ResourceCallback mCallback;
+
+ ViewDetachedIdlingResource(View view) {
+ view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ mIsDetached = false;
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ view.removeOnAttachStateChangeListener(this);
+ mIsDetached = true;
+ mCallback.onTransitionToIdle();
+ }
+ });
+ }
+
+ @Override
+ public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
+ mCallback = resourceCallback;
+ }
+
+ @Override
+ public String getName() {
+ return "ViewDetachedIdlingResource";
+ }
+
+ @Override
+ public boolean isIdleNow() {
+ return mIsDetached;
+ }
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/MimeTypeFilterTest.java b/tests/src/com/android/providers/media/photopicker/espresso/MimeTypeFilterTest.java
new file mode 100644
index 0000000..ce0c612
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/MimeTypeFilterTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.espresso;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withParent;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.hamcrest.Matchers.allOf;
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+
+import com.android.providers.media.R;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4ClassRunner.class)
+public class MimeTypeFilterTest extends PhotoPickerBaseTest {
+
+ private static final String IMAGE_MIME_TYPE = "image/*";
+
+ @Rule
+ public ActivityScenarioRule<PhotoPickerTestActivity> mRule = new ActivityScenarioRule<>(
+ PhotoPickerBaseTest.getSingleSelectMimeTypeFilterIntent(IMAGE_MIME_TYPE));
+
+ @Test
+ public void testPhotosTabOnlyImageItems() {
+
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Two image items and one recent date header
+ final int expectedItemCount = 3;
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(
+ new RecyclerViewItemCountAssertion(expectedItemCount));
+
+ final int videoContainerId = R.id.video_container;
+ // No Video item
+ onView(allOf(withId(videoContainerId),
+ withParent(withId(PICKER_TAB_RECYCLERVIEW_ID)))).check(doesNotExist());
+ }
+
+ @Test
+ public void testAlbumsTabNoVideosAlbum() {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Go to Albums tab
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .perform(click());
+
+ // Only two albums, Camera and Downloads
+ final int expectedItemCount = 2;
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(
+ new RecyclerViewItemCountAssertion(expectedItemCount));
+
+ final int cameraStringId = R.string.picker_category_camera;
+ // Camera album exists
+ onView(allOf(withText(cameraStringId),
+ isDescendantOfA(withId(PICKER_TAB_RECYCLERVIEW_ID)))).check(matches(isDisplayed()));
+
+ final int downloadsStringId = R.string.picker_category_downloads;
+ // Downloads album exists
+ onView(allOf(withText(downloadsStringId),
+ isDescendantOfA(withId(PICKER_TAB_RECYCLERVIEW_ID)))).check(matches(isDisplayed()));
+
+ final int itemCountId = R.id.item_count;
+ // No item count on album items if there is mime type filter
+ onView(allOf(withId(itemCountId),
+ withParent(withId(PICKER_TAB_RECYCLERVIEW_ID)))).check(doesNotExist());
+ }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/MultiSelectTest.java b/tests/src/com/android/providers/media/photopicker/espresso/MultiSelectTest.java
new file mode 100644
index 0000000..1463538
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/MultiSelectTest.java
@@ -0,0 +1,334 @@
+/*
+ * 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.espresso;
+
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isNotSelected;
+import static androidx.test.espresso.matcher.ViewMatchers.isSelected;
+import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static com.android.providers.media.photopicker.espresso.BottomSheetTestUtils.assertBottomSheetState;
+import static com.android.providers.media.photopicker.espresso.CustomSwipeAction.swipeLeftAndWait;
+import static com.android.providers.media.photopicker.espresso.CustomSwipeAction.swipeRightAndWait;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewMatcher.withRecyclerView;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.assertItemDisplayed;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.assertItemNotSelected;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.assertItemSelected;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.clickItem;
+
+import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.not;
+
+import android.app.Activity;
+
+import androidx.test.espresso.IdlingRegistry;
+import androidx.test.espresso.action.ViewActions;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+
+import com.android.providers.media.R;
+
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4ClassRunner.class)
+public class MultiSelectTest extends PhotoPickerBaseTest {
+
+ private static final int TAB_VIEW_PAGER_ID = R.id.picker_tab_viewpager;
+
+ @Rule
+ public ActivityScenarioRule<PhotoPickerTestActivity> mRule
+ = new ActivityScenarioRule<>(PhotoPickerBaseTest.getMultiSelectionIntent());
+
+ @Test
+ public void testMultiSelectDoesNotShowProfileButton() {
+ assertProfileButtonNotShown();
+ }
+
+ @Test
+ public void testMultiselect_showDragBar() {
+ onView(withId(DRAG_BAR_ID)).check(matches(isDisplayed()));
+ }
+
+ @Test
+ public void testMultiselect_showPrivacyText() {
+ onView(withId(PRIVACY_TEXT_ID)).check(matches(isDisplayed()));
+ }
+
+ @Test
+ public void testMultiselect_selectIcon() {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Check select icon is visible
+ assertItemDisplayed(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, R.id.overlay_gradient);
+ assertItemDisplayed(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_CHECK_ID);
+
+ // Verify that select icon is not selected yet
+ assertItemNotSelected(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_CHECK_ID);
+
+ // Select image item thumbnail and verify select icon is selected
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
+ assertItemSelected(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_CHECK_ID);
+
+ // Deselect the item to check item is marked as not selected.
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
+ assertItemNotSelected(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_CHECK_ID);
+
+ // Now, click on the select/check icon, verify we can also click on check icon to select or
+ // deselect an item.
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_CHECK_ID);
+ assertItemSelected(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_CHECK_ID);
+
+ // Click on recyclerView item, this deselects the item. Verify that we can click on any
+ // region on the recyclerView item to select/deselect the item.
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, /* targetViewId */ -1);
+ assertItemNotSelected(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_CHECK_ID);
+ }
+
+ @Test
+ public void testMultiSelect_bottomBar() {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ final int bottomBarId = R.id.picker_bottom_bar;
+ final int viewSelectedId = R.id.button_view_selected;
+ final int addButtonId = R.id.button_add;
+
+ // Initially, buttons should be hidden
+ onView(withId(bottomBarId)).check(matches(not(isDisplayed())));
+
+ // Selecting one item shows view selected and add button
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
+
+ onView(withId(bottomBarId)).check(matches(isDisplayed()));
+ onView(withId(viewSelectedId)).check(matches(isDisplayed()));
+ onView(withId(viewSelectedId)).check(matches(withText(R.string.picker_view_selected)));
+ onView(withId(addButtonId)).check(matches(isDisplayed()));
+
+ // When the selected item count is 0, ViewSelected and add button should hide
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
+ onView(withId(bottomBarId)).check(matches(not(isDisplayed())));
+ onView(withId(viewSelectedId)).check(matches(not(isDisplayed())));
+ onView(withId(addButtonId)).check(matches(not(isDisplayed())));
+ }
+
+ @Test
+ public void testMultiSelect_addButtonText() {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ final int addButtonId = R.id.button_add;
+ final String addButtonString =
+ getTargetContext().getResources().getString(R.string.add);
+
+ // Selecting one item will enable add button and show "Add (1)" as button text
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
+
+ onView(withId(addButtonId)).check(matches(isDisplayed()));
+ onView(withId(addButtonId)).check(matches(withText(addButtonString + " (1)")));
+
+ // When the selected item count is 2, "Add (2)" should be displayed
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_2_POSITION, ICON_THUMBNAIL_ID);
+ onView(withId(addButtonId)).check(matches(isDisplayed()));
+ onView(withId(addButtonId)).check(matches(withText(addButtonString + " (2)")));
+
+ // When the item is deselected add button resets to selected count
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_2_POSITION, ICON_THUMBNAIL_ID);
+ onView(withId(addButtonId)).check(matches(isDisplayed()));
+ onView(withId(addButtonId)).check(matches(withText(addButtonString + " (1)")));
+ }
+
+ @Test
+ public void testMultiSelectAcrossCategories() {
+ // Navigate to Albums tab
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .perform(click());
+
+ final int cameraStringId = R.string.picker_category_camera;
+ // Navigate to photos in Camera album
+ onView(allOf(withText(cameraStringId),
+ isDescendantOfA(withId(PICKER_TAB_RECYCLERVIEW_ID)))).perform(click());
+
+ // Selecting one item will enable add button and show "Add (1)" as button text
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, /* position */ 1, ICON_THUMBNAIL_ID);
+
+ final int addButtonId = R.id.button_add;
+ final String addButtonString =
+ getTargetContext().getResources().getString(R.string.add);
+ onView(withId(addButtonId)).check(matches(isDisplayed()));
+ onView(withId(addButtonId)).check(matches(withText(addButtonString + " (1)")));
+
+ // Click back button
+ onView(withContentDescription("Navigate up")).perform(click());
+
+ // On clicking back button we are back to Albums tab
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .check(matches(isSelected()));
+
+ // Navigate to photos in Video album
+ final int videoStringId = R.string.picker_category_videos;
+ onView(allOf(withText(videoStringId),
+ isDescendantOfA(withId(PICKER_TAB_RECYCLERVIEW_ID)))).perform(click());
+
+ // When the selected item count is 2, "Add (2)" should be displayed
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, /* position */ 1, ICON_THUMBNAIL_ID);
+ onView(withId(addButtonId)).check(matches(isDisplayed()));
+ onView(withId(addButtonId)).check(matches(withText(addButtonString + " (2)")));
+ }
+
+ @Test
+ public void testMultiSelectAcrossDifferentTabs() {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Select image item thumbnail and verify select icon is selected
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
+ assertItemSelected(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_CHECK_ID);
+
+ // Navigate to Albums tab
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .perform(click());
+
+ final int cameraStringId = R.string.picker_category_camera;
+ // Navigate to photos in Camera album
+ onView(allOf(withText(cameraStringId),
+ isDescendantOfA(withId(PICKER_TAB_RECYCLERVIEW_ID)))).perform(click());
+
+ final int position = 1;
+ // The image item in Camera album is selected
+ assertItemSelected(PICKER_TAB_RECYCLERVIEW_ID, position, ICON_CHECK_ID);
+
+ // Deselect the item
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, position, ICON_THUMBNAIL_ID);
+ assertItemNotSelected(PICKER_TAB_RECYCLERVIEW_ID, position, ICON_CHECK_ID);
+
+ // Click back button
+ onView(withContentDescription("Navigate up")).perform(click());
+
+ // On clicking back button we are back to Albums tab
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .check(matches(isSelected()));
+ onView(allOf(withText(cameraStringId),
+ isDescendantOfA(withId(PICKER_TAB_RECYCLERVIEW_ID)))).check(matches(isDisplayed()));
+
+ // Navigate to Photos tab
+ onView(allOf(withText(PICKER_PHOTOS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .perform(click());
+
+ // The image item is not selected
+ assertItemNotSelected(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_CHECK_ID);
+ }
+
+ @Test
+ @Ignore("Enable after b/228574741 is fixed")
+ public void testMultiSelectTabSwiping() throws Exception {
+ onView(withId(TAB_LAYOUT_ID)).check(matches(isDisplayed()));
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, TAB_VIEW_PAGER_ID)) {
+ // Swipe left, we should see albums tab
+ swipeLeftAndWait(TAB_VIEW_PAGER_ID);
+
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .check(matches(isSelected()));
+ onView(allOf(withText(PICKER_PHOTOS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .check(matches(isNotSelected()));
+ // Verify Camera album is shown, we are in albums tab
+ onView(allOf(withText(R.string.picker_category_camera),
+ isDescendantOfA(withId(PICKER_TAB_RECYCLERVIEW_ID)))).check(
+ matches(isDisplayed()));
+
+ // Swipe right, we should see photos tab
+ swipeRightAndWait(TAB_VIEW_PAGER_ID);
+
+ onView(allOf(withText(PICKER_PHOTOS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .check(matches(isSelected()));
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .check(matches(isNotSelected()));
+ // Verify first item is recent header, we are in photos tab
+ onView(withRecyclerView(PICKER_TAB_RECYCLERVIEW_ID)
+ .atPositionOnView(0, R.id.date_header_title))
+ .check(matches(withText(R.string.recent)));
+ }
+ }
+
+ @Test
+ @Ignore("Enable after b/222013536 is fixed")
+ public void testMultiSelectScrollDownToClose() {
+ final BottomSheetIdlingResource bottomSheetIdlingResource =
+ BottomSheetIdlingResource.register(mRule);
+
+ try {
+ bottomSheetIdlingResource.setExpectedState(STATE_EXPANDED);
+ onView(withId(DRAG_BAR_ID)).check(matches(isDisplayed()));
+ onView(withId(PRIVACY_TEXT_ID)).check(matches(isDisplayed()));
+ mRule.getScenario().onActivity(activity -> {
+ assertBottomSheetState(activity, STATE_EXPANDED);
+ });
+
+ // Shows dragBar and privacy text after we are back to Photos tab
+ onView(withId(DRAG_BAR_ID)).check(matches(isDisplayed()));
+ onView(withId(PRIVACY_TEXT_ID)).check(matches(isDisplayed()));
+ mRule.getScenario().onActivity(activity -> {
+ assertBottomSheetState(activity, STATE_EXPANDED);
+ });
+
+ // Swiping down on drag bar or toolbar is not closing the bottom sheet as closing the
+ // bottomsheet requires a stronger downward swipe.
+ onView(withId(R.id.bottom_sheet)).perform(ViewActions.swipeDown());
+ } finally {
+ IdlingRegistry.getInstance().unregister(bottomSheetIdlingResource);
+ }
+
+ assertThat(mRule.getScenario().getResult().getResultCode()).isEqualTo(
+ Activity.RESULT_CANCELED);
+ }
+
+
+ private void assertProfileButtonNotShown() {
+ // Partial screen does not show profile button
+ onView(withId(R.id.profile_button)).check(matches(not(isDisplayed())));
+
+ // Navigate to Albums tab
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .perform(click());
+ onView(withId(R.id.profile_button)).check(matches(not(isDisplayed())));
+
+ final int cameraStringId = R.string.picker_category_camera;
+ // Navigate to photos in Camera album
+ onView(allOf(withText(cameraStringId),
+ isDescendantOfA(withId(PICKER_TAB_RECYCLERVIEW_ID)))).perform(click());
+ onView(withId(R.id.profile_button)).check(matches(not(isDisplayed())));
+
+ // Click back button
+ onView(withContentDescription("Navigate up")).perform(click());
+
+ // on clicking back button we are back to Album grid
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .check(matches(isSelected()));
+ onView(withId(R.id.profile_button)).check(matches(not(isDisplayed())));
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/NoItemsTest.java b/tests/src/com/android/providers/media/photopicker/espresso/NoItemsTest.java
new file mode 100644
index 0000000..8b55975
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/NoItemsTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.espresso;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withParent;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.not;
+
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+
+import android.provider.MediaStore;
+
+import com.android.providers.media.R;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4ClassRunner.class)
+public class NoItemsTest extends PhotoPickerBaseTest {
+
+ @BeforeClass
+ public static void setupClass() throws Exception {
+ PhotoPickerBaseTest.setupClass();
+ deleteFiles(/* invalidateMediaStore */ true);
+ MediaStore.waitForIdle(getIsolatedContext().getContentResolver());
+ }
+
+ /**
+ * Simple test to check we are able to launch PhotoPickerActivity with no items
+ */
+ @Test
+ public void testNoItems_Simple() {
+ try (ActivityScenario<PhotoPickerTestActivity> scenario = ActivityScenario.launch(
+ PhotoPickerBaseTest.getSingleSelectionIntent())) {
+ final int pickerTabRecyclerViewId = R.id.picker_tab_recyclerview;
+
+ onView(withId(pickerTabRecyclerViewId)).check(matches(not(isDisplayed())));
+ onView(withId(android.R.id.empty)).check(matches(isDisplayed()));
+ onView(withText(R.string.picker_photos_empty_message)).check(matches(isDisplayed()));
+
+ // Goto Albums page
+ onView(allOf(withText(R.string.picker_albums),
+ isDescendantOfA(withId(R.id.tab_layout)))).perform(click());
+
+ onView(withId(pickerTabRecyclerViewId)).check(matches(not(isDisplayed())));
+ onView(withId(android.R.id.empty)).check(matches(isDisplayed()));
+ onView(withText(R.string.picker_albums_empty_message)).check(matches(isDisplayed()));
+ }
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/OrientationUtils.java b/tests/src/com/android/providers/media/photopicker/espresso/OrientationUtils.java
new file mode 100644
index 0000000..3ca9163
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/OrientationUtils.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+package com.android.providers.media.photopicker.espresso;
+
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
+import static androidx.test.espresso.Espresso.onIdle;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+
+class OrientationUtils {
+ public static void setLandscapeOrientation(ActivityScenarioRule<PhotoPickerTestActivity> rule) {
+ changeOrientation(rule, SCREEN_ORIENTATION_LANDSCAPE, ORIENTATION_LANDSCAPE);
+ }
+
+ public static void setPortraitOrientation(ActivityScenarioRule<PhotoPickerTestActivity> rule) {
+ changeOrientation(rule, SCREEN_ORIENTATION_PORTRAIT, ORIENTATION_PORTRAIT);
+ }
+
+ private static void changeOrientation(ActivityScenarioRule<PhotoPickerTestActivity> rule,
+ int screenOrientation, int configOrientation) {
+ rule.getScenario().onActivity(activity -> {
+ activity.setRequestedOrientation(screenOrientation);
+ });
+
+ onIdle();
+
+ rule.getScenario().onActivity(activity -> {
+ assertThat(activity.getResources().getConfiguration().orientation)
+ .isEqualTo(configOrientation);
+ });
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerActivityTest.java b/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerActivityTest.java
new file mode 100644
index 0000000..8919187
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerActivityTest.java
@@ -0,0 +1,317 @@
+/*
+ * 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.espresso;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isNotSelected;
+import static androidx.test.espresso.matcher.ViewMatchers.isSelected;
+import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static com.android.providers.media.photopicker.espresso.BottomSheetTestUtils.assertBottomSheetState;
+import static com.android.providers.media.photopicker.espresso.CustomSwipeAction.customSwipeDownPartialScreen;
+import static com.android.providers.media.photopicker.espresso.CustomSwipeAction.swipeLeftAndWait;
+import static com.android.providers.media.photopicker.espresso.CustomSwipeAction.swipeRightAndWait;
+import static com.android.providers.media.photopicker.espresso.OrientationUtils.setLandscapeOrientation;
+import static com.android.providers.media.photopicker.espresso.OrientationUtils.setPortraitOrientation;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewMatcher.withRecyclerView;
+
+import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED;
+import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.not;
+
+import android.app.Activity;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.espresso.IdlingRegistry;
+import androidx.test.espresso.action.ViewActions;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+import androidx.viewpager2.widget.ViewPager2;
+
+import com.android.providers.media.R;
+
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4ClassRunner.class)
+public class PhotoPickerActivityTest extends PhotoPickerBaseTest {
+
+ private static final int TAB_VIEW_PAGER_ID = R.id.picker_tab_viewpager;
+
+ @Rule
+ public ActivityScenarioRule<PhotoPickerTestActivity> mRule
+ = new ActivityScenarioRule<>(PhotoPickerBaseTest.getSingleSelectionIntent());
+
+ /**
+ * Simple test to check we are able to launch PhotoPickerActivity
+ */
+ @Test
+ public void testActivityLayout_Simple() {
+ onView(withId(R.id.toolbar)).check(matches(isDisplayed()));
+ onView(withId(R.id.fragment_container)).check(matches(isDisplayed()));
+ onView(withId(DRAG_BAR_ID)).check(matches(isDisplayed()));
+ onView(withId(PRIVACY_TEXT_ID)).check(matches(isDisplayed()));
+ // Partial screen does not show profile button
+ onView(withId(R.id.profile_button)).check(matches(not(isDisplayed())));
+ onView(withId(android.R.id.empty)).check(matches(not(isDisplayed())));
+
+ final String cancelString =
+ InstrumentationRegistry.getTargetContext().getResources().getString(
+ android.R.string.cancel);
+ onView(withContentDescription(cancelString)).perform(click());
+ assertThat(mRule.getScenario().getResult().getResultCode()).isEqualTo(
+ Activity.RESULT_CANCELED);
+ }
+
+ @Test
+ public void testDoesNotShowProfileButton_partialScreen() {
+ assertProfileButtonNotShown();
+ }
+
+ @Test
+ @Ignore("Enable after b/222013536 is fixed")
+ public void testDoesNotShowProfileButton_fullScreen() {
+ // Bottomsheet assertions are different for landscape mode
+ setPortraitOrientation(mRule);
+
+ // Partial screen does not show profile button
+ onView(withId(R.id.profile_button)).check(matches(not(isDisplayed())));
+
+ BottomSheetTestUtils.swipeUp(mRule);
+
+ assertProfileButtonNotShown();
+ }
+
+ @Test
+ @Ignore("Enable after b/222013536 is fixed")
+ public void testBottomSheetState() {
+ // Bottom sheet assertions are different for landscape mode
+ setPortraitOrientation(mRule);
+
+ // Register bottom sheet idling resource so that we don't read bottom sheet state when
+ // in between changing states
+ final BottomSheetIdlingResource bottomSheetIdlingResource =
+ BottomSheetIdlingResource.register(mRule);
+
+ try {
+ // Single select PhotoPicker is launched in partial screen mode
+ bottomSheetIdlingResource.setExpectedState(STATE_COLLAPSED);
+ onView(withId(DRAG_BAR_ID)).check(matches(isDisplayed()));
+ onView(withId(PRIVACY_TEXT_ID)).check(matches(isDisplayed()));
+ mRule.getScenario().onActivity(activity -> {
+ assertBottomSheetState(activity, STATE_COLLAPSED);
+ });
+
+ // Swipe up and check that the PhotoPicker is in full screen mode
+ bottomSheetIdlingResource.setExpectedState(STATE_EXPANDED);
+ onView(withId(PRIVACY_TEXT_ID)).perform(ViewActions.swipeUp());
+ mRule.getScenario().onActivity(activity -> {
+ assertBottomSheetState(activity, STATE_EXPANDED);
+ });
+
+ // Swipe down and check that the PhotoPicker is in partial screen mode
+ bottomSheetIdlingResource.setExpectedState(STATE_COLLAPSED);
+ onView(withId(PRIVACY_TEXT_ID)).perform(ViewActions.swipeDown());
+ mRule.getScenario().onActivity(activity -> {
+ assertBottomSheetState(activity, STATE_COLLAPSED);
+ });
+
+ // Swiping down on drag bar is not strong enough as closing the bottomsheet requires a
+ // stronger downward swipe using espresso.
+ // Simply swiping down on R.id.bottom_sheet throws an error from espresso, as the view
+ // is only 60% visible, but downward swipe is only successful on an element which is 90%
+ // visible.
+ onView(withId(R.id.bottom_sheet)).perform(customSwipeDownPartialScreen());
+ } finally {
+ IdlingRegistry.getInstance().unregister(bottomSheetIdlingResource);
+ }
+ assertThat(mRule.getScenario().getResult().getResultCode()).isEqualTo(
+ Activity.RESULT_CANCELED);
+ }
+
+ @Test
+ @Ignore("Enable after b/222013536 is fixed")
+ public void testBottomSheetStateInLandscapeMode() {
+ // Bottom sheet assertions are different for landscape mode
+ setLandscapeOrientation(mRule);
+
+ // Register bottom sheet idling resource so that we don't read bottom sheet state when
+ // in between changing states
+ final BottomSheetIdlingResource bottomSheetIdlingResource =
+ BottomSheetIdlingResource.register(mRule);
+
+ try {
+ // Single select PhotoPicker is launched in full screen mode in Landscape orientation
+ mRule.getScenario().onActivity(activity -> {
+ assertBottomSheetState(activity, STATE_EXPANDED);
+ });
+
+ // Swiping down on drag bar / privacy text is not strong enough as closing the
+ // bottomsheet requires a stronger downward swipe using espresso.
+ onView(withId(R.id.bottom_sheet)).perform(ViewActions.swipeDown());
+ } finally {
+ IdlingRegistry.getInstance().unregister(bottomSheetIdlingResource);
+ }
+ assertThat(mRule.getScenario().getResult().getResultCode()).isEqualTo(
+ Activity.RESULT_CANCELED);
+ }
+
+ @Test
+ public void testToolbarLayout() {
+ onView(withId(R.id.toolbar)).check(matches(isDisplayed()));
+
+ onView(withId(TAB_LAYOUT_ID)).check(matches(isDisplayed()));
+
+ mRule.getScenario().onActivity(activity -> {
+ final ViewPager2 viewPager2 = activity.findViewById(TAB_VIEW_PAGER_ID);
+ assertThat(viewPager2.getAdapter().getItemCount()).isEqualTo(2);
+ });
+
+ onView(allOf(withText(PICKER_PHOTOS_STRING_ID),
+ isDescendantOfA(withId(TAB_LAYOUT_ID)))).check(matches(isDisplayed()));
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID),
+ isDescendantOfA(withId(TAB_LAYOUT_ID)))).check(matches(isDisplayed()));
+
+ // TODO(b/200513333): Check close icon
+ }
+
+ @Test
+ public void testTabNavigation() {
+ onView(withId(TAB_LAYOUT_ID)).check(matches(isDisplayed()));
+
+ // On clicking albums tab item, we should see albums tab
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .perform(click());
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .check(matches(isSelected()));
+ onView(allOf(withText(PICKER_PHOTOS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .check(matches(isNotSelected()));
+ // Verify Camera album is shown, we are in albums tab
+ onView(allOf(withText(R.string.picker_category_camera),
+ isDescendantOfA(withId(PICKER_TAB_RECYCLERVIEW_ID)))).check(matches(isDisplayed()));
+
+
+ // On clicking photos tab item, we should see photos tab
+ onView(allOf(withText(PICKER_PHOTOS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .perform(click());
+ onView(allOf(withText(PICKER_PHOTOS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .check(matches(isSelected()));
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .check(matches(isNotSelected()));
+ // Verify first item is recent header, we are in photos tab
+ onView(withRecyclerView(PICKER_TAB_RECYCLERVIEW_ID)
+ .atPositionOnView(0, R.id.date_header_title))
+ .check(matches(withText(R.string.recent)));
+ }
+
+ @Test
+ @Ignore("Enable after b/222013536 is fixed")
+ public void testTabSwiping() throws Exception {
+ onView(withId(TAB_LAYOUT_ID)).check(matches(isDisplayed()));
+
+ // If we want to swipe the viewPager2 of tabContainerFragment in Espresso tests, at least 90
+ // percent of the view's area is displayed to the user. Swipe up the bottom Sheet to make
+ // sure it is in full Screen mode.
+ // Register bottom sheet idling resource so that we don't read bottom sheet state when
+ // in between changing states
+ final BottomSheetIdlingResource bottomSheetIdlingResource =
+ BottomSheetIdlingResource.register(mRule);
+
+ try {
+
+ // When accessibility is enabled, we always launch the photo picker in full screen mode.
+ // Accessibility is enabled in Espresso test, so we can't check the COLLAPSED state.
+// // Single select PhotoPicker is launched in partial screen mode
+// bottomSheetIdlingResource.setExpectedState(STATE_COLLAPSED);
+// mRule.getScenario().onActivity(activity -> {
+// assertBottomSheetState(activity, STATE_COLLAPSED);
+// });
+
+ // Swipe up and check that the PhotoPicker is in full screen mode.
+// onView(withId(PRIVACY_TEXT_ID)).check(matches(isDisplayed()));
+// onView(withId(PRIVACY_TEXT_ID)).perform(ViewActions.swipeUp());
+ bottomSheetIdlingResource.setExpectedState(STATE_EXPANDED);
+ mRule.getScenario().onActivity(activity -> {
+ assertBottomSheetState(activity, STATE_EXPANDED);
+ });
+ } finally {
+ IdlingRegistry.getInstance().unregister(bottomSheetIdlingResource);
+ }
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, TAB_VIEW_PAGER_ID)) {
+ // Swipe left, we should see albums tab
+ swipeLeftAndWait(TAB_VIEW_PAGER_ID);
+
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .check(matches(isSelected()));
+ onView(allOf(withText(PICKER_PHOTOS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .check(matches(isNotSelected()));
+ // Verify Camera album is shown, we are in albums tab
+ onView(allOf(withText(R.string.picker_category_camera),
+ isDescendantOfA(withId(PICKER_TAB_RECYCLERVIEW_ID)))).check(
+ matches(isDisplayed()));
+
+ // Swipe right, we should see photos tab
+ swipeRightAndWait(TAB_VIEW_PAGER_ID);
+
+ onView(allOf(withText(PICKER_PHOTOS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .check(matches(isSelected()));
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .check(matches(isNotSelected()));
+ // Verify first item is recent header, we are in photos tab
+ onView(withRecyclerView(PICKER_TAB_RECYCLERVIEW_ID)
+ .atPositionOnView(0, R.id.date_header_title))
+ .check(matches(withText(R.string.recent)));
+ }
+ }
+
+ private void assertProfileButtonNotShown() {
+ // Partial screen does not show profile button
+ onView(withId(R.id.profile_button)).check(matches(not(isDisplayed())));
+
+ // Navigate to Albums tab
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .perform(click());
+ onView(withId(R.id.profile_button)).check(matches(not(isDisplayed())));
+
+ final int cameraStringId = R.string.picker_category_camera;
+ // Navigate to photos in Camera album
+ onView(allOf(withText(cameraStringId),
+ isDescendantOfA(withId(PICKER_TAB_RECYCLERVIEW_ID)))).perform(click());
+ onView(withId(R.id.profile_button)).check(matches(not(isDisplayed())));
+
+ // Click back button
+ onView(withContentDescription("Navigate up")).perform(click());
+
+ // on clicking back button we are back to Album grid
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .check(matches(isSelected()));
+ onView(withId(R.id.profile_button)).check(matches(not(isDisplayed())));
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerBaseTest.java b/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerBaseTest.java
new file mode 100644
index 0000000..f2c80e7
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerBaseTest.java
@@ -0,0 +1,296 @@
+/*
+ * 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.espresso;
+
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.provider.MediaStore;
+import android.system.ErrnoException;
+import android.system.Os;
+
+import androidx.core.util.Supplier;
+import androidx.test.InstrumentationRegistry;
+
+import com.android.providers.media.R;
+import com.android.providers.media.photopicker.data.UserIdManager;
+import com.android.providers.media.photopicker.data.model.UserId;
+import com.android.providers.media.scan.MediaScannerTest.IsolatedContext;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.attribute.FileTime;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+public class PhotoPickerBaseTest {
+ protected static final int PICKER_TAB_RECYCLERVIEW_ID = R.id.picker_tab_recyclerview;
+ protected static final int TAB_LAYOUT_ID = R.id.tab_layout;
+ protected static final int PICKER_PHOTOS_STRING_ID = R.string.picker_photos;
+ protected static final int PICKER_ALBUMS_STRING_ID = R.string.picker_albums;
+ protected static final int PREVIEW_VIEW_PAGER_ID = R.id.preview_viewPager;
+ protected static final int ICON_CHECK_ID = R.id.icon_check;
+ protected static final int ICON_THUMBNAIL_ID = R.id.icon_thumbnail;
+ protected static final int VIEW_SELECTED_BUTTON_ID = R.id.button_view_selected;
+ protected static final int PREVIEW_IMAGE_VIEW_ID = R.id.preview_imageView;
+ protected static final int DRAG_BAR_ID = R.id.drag_bar;
+ protected static final int PREVIEW_GIF_ID = R.id.preview_gif;
+ protected static final int PREVIEW_MOTION_PHOTO_ID = R.id.preview_motion_photo;
+ protected static final int PREVIEW_ADD_OR_SELECT_BUTTON_ID = R.id.preview_add_or_select_button;
+ protected static final int PRIVACY_TEXT_ID = R.id.privacy_text;
+
+ protected static final int DIMEN_PREVIEW_ADD_OR_SELECT_WIDTH
+ = R.dimen.preview_add_or_select_width;
+
+ /**
+ * The position of the first image item in the grid on the Photos tab
+ */
+ protected static final int IMAGE_1_POSITION = 1;
+
+ /**
+ * The position of the second item in the grid on the Photos tab
+ */
+ protected static final int IMAGE_2_POSITION = 2;
+
+ /**
+ * The position of the video item in the grid on the Photos tab
+ */
+ protected static final int VIDEO_POSITION = 3;
+
+
+ private static final Intent sSingleSelectIntent;
+ static {
+ sSingleSelectIntent = new Intent(MediaStore.ACTION_PICK_IMAGES);
+ sSingleSelectIntent.addCategory(Intent.CATEGORY_FRAMEWORK_INSTRUMENTATION_TEST);
+ }
+
+ private static final Intent sMultiSelectionIntent;
+ static {
+ sMultiSelectionIntent = new Intent(MediaStore.ACTION_PICK_IMAGES);
+ Bundle extras = new Bundle();
+ extras.putInt(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit());
+ sMultiSelectionIntent.addCategory(Intent.CATEGORY_FRAMEWORK_INSTRUMENTATION_TEST);
+ sMultiSelectionIntent.putExtras(extras);
+ }
+
+ private static final File IMAGE_1_FILE = new File(Environment.getExternalStorageDirectory(),
+ Environment.DIRECTORY_DCIM + "/Camera"
+ + "/image_" + System.currentTimeMillis() + ".jpeg");
+ private static final File IMAGE_2_FILE = new File(Environment.getExternalStorageDirectory(),
+ Environment.DIRECTORY_DOWNLOADS + "/image_" + System.currentTimeMillis() + ".jpeg");
+ private static final File VIDEO_FILE = new File(Environment.getExternalStorageDirectory(),
+ Environment.DIRECTORY_MOVIES + "/video_" + System.currentTimeMillis() + ".mp4");
+
+ private static final long POLLING_TIMEOUT_MILLIS_LONG = TimeUnit.SECONDS.toMillis(2);
+ private static final long POLLING_SLEEP_MILLIS = 200;
+
+ private static IsolatedContext sIsolatedContext;
+ private static UserIdManager sUserIdManager;
+
+ public static Intent getSingleSelectMimeTypeFilterIntent(String mimeTypeFilter) {
+ final Intent intent = new Intent(sSingleSelectIntent);
+ intent.setType(mimeTypeFilter);
+ return intent;
+ }
+
+ public static Intent getSingleSelectionIntent() {
+ return sSingleSelectIntent;
+ }
+
+ public static Intent getMultiSelectionIntent() {
+ return sMultiSelectionIntent;
+ }
+
+ public static Intent getMultiSelectionIntent(int max) {
+ final Intent intent = new Intent(sMultiSelectionIntent);
+ Bundle extras = new Bundle();
+ extras.putInt(MediaStore.EXTRA_PICK_IMAGES_MAX, max);
+ intent.putExtras(extras);
+ return intent;
+ }
+
+ public static IsolatedContext getIsolatedContext() {
+ return sIsolatedContext;
+ }
+
+ public static UserIdManager getMockUserIdManager() {
+ return sUserIdManager;
+ }
+
+ @BeforeClass
+ public static void setupClass() throws Exception {
+ MediaStore.waitForIdle(getTargetContext().getContentResolver());
+ pollForCondition(() -> isExternalStorageStateMounted(), "Timed out while"
+ + " waiting for ExternalStorageState to be MEDIA_MOUNTED");
+
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .adoptShellPermissionIdentity(Manifest.permission.LOG_COMPAT_CHANGE,
+ Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
+ Manifest.permission.INTERACT_ACROSS_USERS);
+
+ sIsolatedContext = new IsolatedContext(getTargetContext(), "modern",
+ /* asFuseThread */ false);
+
+ sUserIdManager = mock(UserIdManager.class);
+ when(sUserIdManager.getCurrentUserProfileId()).thenReturn(UserId.CURRENT_USER);
+
+ createFiles();
+ }
+
+ @AfterClass
+ public static void destroyClass() {
+ deleteFiles(/* invalidateMediaStore */ false);
+
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().dropShellPermissionIdentity();
+ }
+
+ protected static void deleteFiles(boolean invalidateMediaStore) {
+ deleteFile(IMAGE_1_FILE, invalidateMediaStore);
+ deleteFile(IMAGE_2_FILE, invalidateMediaStore);
+ deleteFile(VIDEO_FILE, invalidateMediaStore);
+ }
+
+ private static void deleteFile(File file, boolean invalidateMediaStore) {
+ file.delete();
+ if (invalidateMediaStore) {
+ final Uri uri = MediaStore.scanFile(getIsolatedContext().getContentResolver(), file);
+ assertThat(uri).isNull();
+ // Force picker db sync for that db operation
+ MediaStore.waitForIdle(getIsolatedContext().getContentResolver());
+ }
+ }
+
+ private static void createFiles() throws Exception {
+ long timeNow = System.currentTimeMillis();
+ // Create files and change dateModified so that we can predict the recyclerView item
+ // position. Set modified date ahead of time, so that even if other files are created,
+ // the below files always have positions 1, 2 and 3.
+ createFile(IMAGE_1_FILE, timeNow + 30000);
+ createFile(IMAGE_2_FILE, timeNow + 20000);
+ createFile(VIDEO_FILE, timeNow + 10000);
+ }
+
+ private static void pollForCondition(Supplier<Boolean> condition, String errorMessage)
+ throws Exception {
+ for (int i = 0; i < POLLING_TIMEOUT_MILLIS_LONG / POLLING_SLEEP_MILLIS; i++) {
+ if (condition.get()) {
+ return;
+ }
+ Thread.sleep(POLLING_SLEEP_MILLIS);
+ }
+ throw new TimeoutException(errorMessage);
+ }
+
+ private static boolean isExternalStorageStateMounted() {
+ final File target = Environment.getExternalStorageDirectory();
+ try {
+ return (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState(target))
+ && Os.statvfs(target.getAbsolutePath()).f_blocks > 0);
+ } catch (ErrnoException ignored) {
+ }
+ return false;
+ }
+
+ private static void createFile(File file, long dateModified) throws IOException {
+ File parentFile = file.getParentFile();
+ parentFile.mkdirs();
+
+ assertThat(parentFile.exists()).isTrue();
+ assertThat(file.createNewFile()).isTrue();
+ // Write 1 byte because 0byte files are not valid in the picker db
+ try (FileOutputStream fos = new FileOutputStream(file)) {
+ fos.write(1);
+ }
+
+ // Change dateModified so that we can predict the recyclerView item position
+ Files.setLastModifiedTime(file.toPath(), FileTime.fromMillis(dateModified));
+
+ final Uri uri = MediaStore.scanFile(getIsolatedContext().getContentResolver(), file);
+ MediaStore.waitForIdle(getIsolatedContext().getContentResolver());
+ assertThat(uri).isNotNull();
+ }
+
+ /**
+ * Mock UserIdManager class such that the profile button is active and the user is in personal
+ * profile.
+ */
+ static void setUpActiveProfileButton() {
+ when(sUserIdManager.isMultiUserProfiles()).thenReturn(true);
+ when(sUserIdManager.isBlockedByAdmin()).thenReturn(false);
+ when(sUserIdManager.isWorkProfileOff()).thenReturn(false);
+ when(sUserIdManager.isCrossProfileAllowed()).thenReturn(true);
+ when(sUserIdManager.isManagedUserSelected()).thenReturn(false);
+
+ // setPersonalAsCurrentUserProfile() is called onClick of Active Profile Button to change
+ // profiles
+ doAnswer(invocation -> {
+ updateIsManagedUserSelected(/* isManagedUserSelected */ false);
+ return null;
+ }).when(sUserIdManager).setPersonalAsCurrentUserProfile();
+
+ // setManagedAsCurrentUserProfile() is called onClick of Active Profile Button to change
+ // profiles
+ doAnswer(invocation -> {
+ updateIsManagedUserSelected(/* isManagedUserSelected */ true);
+ return null;
+ }).when(sUserIdManager).setManagedAsCurrentUserProfile();
+ }
+
+ /**
+ * Mock UserIdManager class such that the user is in personal profile and work apps are
+ * turned off
+ */
+ static void setUpWorkAppsOffProfileButton() {
+ when(sUserIdManager.isMultiUserProfiles()).thenReturn(true);
+ when(sUserIdManager.isBlockedByAdmin()).thenReturn(false);
+ when(sUserIdManager.isWorkProfileOff()).thenReturn(true);
+ when(sUserIdManager.isCrossProfileAllowed()).thenReturn(false);
+ when(sUserIdManager.isManagedUserSelected()).thenReturn(false);
+ }
+
+ /**
+ * Mock UserIdManager class such that the user is in work profile and accessing personal
+ * profile content is blocked by admin
+ */
+ static void setUpBlockedByAdminProfileButton() {
+ when(sUserIdManager.isMultiUserProfiles()).thenReturn(true);
+ when(sUserIdManager.isBlockedByAdmin()).thenReturn(true);
+ when(sUserIdManager.isWorkProfileOff()).thenReturn(false);
+ when(sUserIdManager.isCrossProfileAllowed()).thenReturn(false);
+ when(sUserIdManager.isManagedUserSelected()).thenReturn(true);
+ }
+
+ private static void updateIsManagedUserSelected(boolean isManagedUserSelected) {
+ when(sUserIdManager.isManagedUserSelected()).thenReturn(isManagedUserSelected);
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerTestActivity.java b/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerTestActivity.java
new file mode 100644
index 0000000..5b5a356
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerTestActivity.java
@@ -0,0 +1,32 @@
+/*
+ * 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.espresso;
+
+import com.android.providers.media.photopicker.PhotoPickerActivity;
+import com.android.providers.media.photopicker.data.ItemsProvider;
+import com.android.providers.media.photopicker.viewmodel.PickerViewModel;
+
+public class PhotoPickerTestActivity extends PhotoPickerActivity {
+ @Override
+ protected PickerViewModel createViewModel() {
+ PickerViewModel pickerViewModel = super.createViewModel();
+ pickerViewModel.setItemsProvider(new ItemsProvider(
+ PhotoPickerBaseTest.getIsolatedContext()));
+ pickerViewModel.setUserIdManager(PhotoPickerBaseTest.getMockUserIdManager());
+ return pickerViewModel;
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/PhotosTabTest.java b/tests/src/com/android/providers/media/photopicker/espresso/PhotosTabTest.java
new file mode 100644
index 0000000..dd901a2
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/PhotosTabTest.java
@@ -0,0 +1,171 @@
+/*
+ * 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.espresso;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isSelected;
+import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withParent;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static com.android.providers.media.photopicker.espresso.RecyclerViewMatcher.withRecyclerView;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.assertItemDisplayed;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.assertItemNotDisplayed;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.not;
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+
+import com.android.providers.media.R;
+import com.android.providers.media.photopicker.util.DateTimeUtils;
+
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4ClassRunner.class)
+public class PhotosTabTest extends PhotoPickerBaseTest {
+ private static final int ICON_GIF_ID = R.id.icon_gif;
+ private static final int ICON_MOTION_PHOTO_ID = R.id.icon_motion_photo;
+ private static final int VIDEO_CONTAINER_ID = R.id.video_container;
+ private static final int OVERLAY_GRADIENT_ID = R.id.overlay_gradient;
+
+ @Rule
+ public final ActivityScenarioRule<PhotoPickerTestActivity> mRule
+ = new ActivityScenarioRule<>(PhotoPickerBaseTest.getSingleSelectionIntent());
+
+ @Test
+ public void testPhotoGridLayout_photoGrid() {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // check the count of items
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(new RecyclerViewItemCountAssertion(4));
+
+ // Verify first item is recent header
+ onView(withRecyclerView(PICKER_TAB_RECYCLERVIEW_ID)
+ .atPositionOnView(0, R.id.date_header_title))
+ .check(matches(withText(R.string.recent)));
+
+ // Verify bottom bar is not displayed
+ onView(withId(R.id.picker_bottom_bar)).check(matches(not(isDisplayed())));
+ }
+
+ @Test
+ public void testPhotoGridLayout_image() {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Verify we have the thumbnail
+ assertItemDisplayed(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
+
+ // Verify check icon, gif icon, motion photo icon and video icon are not displayed
+ assertItemNotDisplayed(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, OVERLAY_GRADIENT_ID);
+ assertItemNotDisplayed(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_CHECK_ID);
+ assertItemNotDisplayed(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_GIF_ID);
+ assertItemNotDisplayed(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_MOTION_PHOTO_ID);
+ assertItemNotDisplayed(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, VIDEO_CONTAINER_ID);
+ }
+
+ @Test
+ public void testPhotoGridLayout_video() {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Verify we have the thumbnail
+ assertItemDisplayed(PICKER_TAB_RECYCLERVIEW_ID, VIDEO_POSITION, ICON_THUMBNAIL_ID);
+
+ // Verify video icon and duration are displayed
+ assertItemDisplayed(PICKER_TAB_RECYCLERVIEW_ID, VIDEO_POSITION, OVERLAY_GRADIENT_ID);
+ assertItemDisplayed(PICKER_TAB_RECYCLERVIEW_ID, VIDEO_POSITION, VIDEO_CONTAINER_ID);
+ assertItemDisplayed(PICKER_TAB_RECYCLERVIEW_ID, VIDEO_POSITION, R.id.video_duration);
+ assertItemDisplayed(PICKER_TAB_RECYCLERVIEW_ID, VIDEO_POSITION, R.id.icon_video);
+ onView(withRecyclerView(PICKER_TAB_RECYCLERVIEW_ID)
+ .atPositionOnView(VIDEO_POSITION, R.id.video_duration))
+ .check(matches(withText(containsString("0"))));
+
+ // Verify check icon and gif icon and motion photo icon are not displayed
+ assertItemNotDisplayed(PICKER_TAB_RECYCLERVIEW_ID, VIDEO_POSITION, ICON_CHECK_ID);
+ assertItemNotDisplayed(PICKER_TAB_RECYCLERVIEW_ID, VIDEO_POSITION, ICON_GIF_ID);
+ assertItemNotDisplayed(PICKER_TAB_RECYCLERVIEW_ID, VIDEO_POSITION, ICON_MOTION_PHOTO_ID);
+ }
+
+ @Test
+ public void testPhotoGrid_albumPhotos() {
+ // Navigate to Albums tab
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .perform(click());
+
+ final int cameraStringId = R.string.picker_category_camera;
+ // Navigate to photos in Camera album
+ onView(allOf(withText(cameraStringId),
+ isDescendantOfA(withId(PICKER_TAB_RECYCLERVIEW_ID)))).perform(click());
+
+ // Verify that toolbar has the title as category name
+ onView(allOf(withText(cameraStringId), withParent(withId(R.id.toolbar))))
+ .check(matches(isDisplayed()));
+
+ // Verify that tab tabs are not shown on the toolbar
+ onView(withId(TAB_LAYOUT_ID)).check(matches(not(isDisplayed())));
+
+ // Verify that privacy text is not shown
+ onView(withId(PRIVACY_TEXT_ID)).check(matches(not(isDisplayed())));
+
+ // Verify that drag bar is shown
+ onView(withId(DRAG_BAR_ID)).check(matches(isDisplayed()));
+
+ final int dateHeaderTitleId = R.id.date_header_title;
+ final int recentHeaderPosition = 0;
+ // Verify that first item is not a recent header
+ assertItemDisplayed(PICKER_TAB_RECYCLERVIEW_ID, recentHeaderPosition, dateHeaderTitleId);
+ onView(withRecyclerView(PICKER_TAB_RECYCLERVIEW_ID)
+ .atPositionOnView(recentHeaderPosition, dateHeaderTitleId))
+ .check(matches(not(withText(R.string.recent))));
+
+ // Verify that first item is TODAY
+ onView(withRecyclerView(PICKER_TAB_RECYCLERVIEW_ID)
+ .atPositionOnView(0, dateHeaderTitleId))
+ .check(matches(withText(
+ DateTimeUtils.getDateHeaderString(System.currentTimeMillis()))));
+
+ final int photoItemPosition = 1;
+ // Verify first item is image and has no other icons other than thumbnail
+ assertItemDisplayed(PICKER_TAB_RECYCLERVIEW_ID, photoItemPosition, ICON_THUMBNAIL_ID);
+
+ // Verify check icon, gif icon, motion photo icon and video icon are not displayed
+ assertItemNotDisplayed(PICKER_TAB_RECYCLERVIEW_ID, photoItemPosition, OVERLAY_GRADIENT_ID);
+ assertItemNotDisplayed(PICKER_TAB_RECYCLERVIEW_ID, photoItemPosition, ICON_CHECK_ID);
+ assertItemNotDisplayed(PICKER_TAB_RECYCLERVIEW_ID, photoItemPosition, ICON_GIF_ID);
+ assertItemNotDisplayed(PICKER_TAB_RECYCLERVIEW_ID, photoItemPosition, ICON_MOTION_PHOTO_ID);
+ assertItemNotDisplayed(PICKER_TAB_RECYCLERVIEW_ID, photoItemPosition, VIDEO_CONTAINER_ID);
+
+ // Click back button
+ onView(withContentDescription("Navigate up")).perform(click());
+
+ // on clicking back button we are back to Album grid
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .check(matches(isSelected()));
+ onView(allOf(withText(cameraStringId),
+ isDescendantOfA(withId(PICKER_TAB_RECYCLERVIEW_ID)))).check(matches(isDisplayed()));
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/PreviewMultiSelectLongPressTest.java b/tests/src/com/android/providers/media/photopicker/espresso/PreviewMultiSelectLongPressTest.java
new file mode 100644
index 0000000..de6ad97
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/PreviewMultiSelectLongPressTest.java
@@ -0,0 +1,255 @@
+/*
+ * 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.espresso;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static com.android.providers.media.photopicker.espresso.CustomSwipeAction.swipeLeftAndWait;
+import static com.android.providers.media.photopicker.espresso.CustomSwipeAction.swipeRightAndWait;
+import static com.android.providers.media.photopicker.espresso.OrientationUtils.setLandscapeOrientation;
+import static com.android.providers.media.photopicker.espresso.OrientationUtils.setPortraitOrientation;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.*;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.assertItemNotSelected;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.assertItemSelected;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.longClickItem;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.hamcrest.Matchers.not;
+
+import android.widget.Button;
+
+import androidx.lifecycle.ViewModelProvider;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+
+import com.android.providers.media.R;
+import com.android.providers.media.photopicker.data.Selection;
+import com.android.providers.media.photopicker.viewmodel.PickerViewModel;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4ClassRunner.class)
+public class PreviewMultiSelectLongPressTest extends PhotoPickerBaseTest {
+ private static final int ICON_THUMBNAIL_ID = R.id.icon_thumbnail;
+
+ @Rule
+ public ActivityScenarioRule<PhotoPickerTestActivity> mRule
+ = new ActivityScenarioRule<>(PhotoPickerBaseTest.getMultiSelectionIntent());
+
+ @Test
+ public void testPreview_multiSelect_longPress_image() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Navigate to preview
+ longClickItem(PICKER_TAB_RECYCLERVIEW_ID, /* position */ 1, ICON_THUMBNAIL_ID);
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ // No dragBar in preview
+ onView(withId(DRAG_BAR_ID)).check(matches(not(isDisplayed())));
+
+ // No privacy text in preview
+ onView(withId(PRIVACY_TEXT_ID)).check(matches(not(isDisplayed())));
+
+ // Verify image is previewed
+ assertMultiSelectLongPressCommonLayoutMatches();
+ onView(withId(R.id.preview_imageView)).check(matches(isDisplayed()));
+ // Verify no special format icon is previewed
+ onView(withId(PREVIEW_MOTION_PHOTO_ID)).check(doesNotExist());
+ onView(withId(PREVIEW_GIF_ID)).check(doesNotExist());
+ }
+
+ // Navigate back to Photo grid
+ onView(withContentDescription("Navigate up")).perform(click());
+
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+ // Shows dragBar and privacy text after we are back to Photos tab
+ onView(withId(DRAG_BAR_ID)).check(matches(isDisplayed()));
+ onView(withId(PRIVACY_TEXT_ID)).check(matches(isDisplayed()));
+ }
+
+ @Test
+ public void testPreview_multiSelect_longPress_video() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Navigate to preview
+ longClickItem(PICKER_TAB_RECYCLERVIEW_ID, /* position */ 3, ICON_THUMBNAIL_ID);
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ assertMultiSelectLongPressCommonLayoutMatches();
+ // Verify thumbnail view is displayed
+ onView(withId(R.id.preview_video_image)).check(matches(isDisplayed()));
+ // TODO (b/232792753): Assert video player visibility using custom IdlingResource
+
+ // Verify no special format icon is previewed
+ onView(withId(PREVIEW_MOTION_PHOTO_ID)).check(doesNotExist());
+ onView(withId(PREVIEW_GIF_ID)).check(doesNotExist());
+ }
+ }
+
+ @Test
+ public void testPreview_multiSelect_longPress_select() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ final int position = 1;
+ // Navigate to preview
+ longClickItem(PICKER_TAB_RECYCLERVIEW_ID, position, ICON_THUMBNAIL_ID);
+
+ final int selectButtonId = PREVIEW_ADD_OR_SELECT_BUTTON_ID;
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ // Select the item within Preview
+ onView(withId(selectButtonId)).perform(click());
+ // Check that button text is changed to "deselect"
+ onView(withId(selectButtonId)).check(matches(withText(R.string.deselect)));
+ }
+
+ // Navigate back to PhotoGrid and check that item is selected
+ onView(withContentDescription("Navigate up")).perform(click());
+
+ final int iconCheckId = R.id.icon_check;
+ assertItemSelected(PICKER_TAB_RECYCLERVIEW_ID, position, iconCheckId);
+
+ // Navigate to Preview and check the select button text
+ longClickItem(PICKER_TAB_RECYCLERVIEW_ID, position, ICON_THUMBNAIL_ID);
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ // Check that button text is set to "deselect" and common layout matches
+ assertMultiSelectLongPressCommonLayoutMatches(/* isSelected */ true);
+
+ // Click on "Deselect" and verify text changes to "Select"
+ onView(withId(selectButtonId)).perform(click());
+ // Check that button text is changed to "select"
+ onView(withId(selectButtonId)).check(matches(withText(R.string.select)));
+ }
+
+ // Navigate back to Photo grid and verify the item is not selected
+ onView(withContentDescription("Navigate up")).perform(click());
+
+ assertItemNotSelected(PICKER_TAB_RECYCLERVIEW_ID, position, iconCheckId);
+ }
+
+ @Test
+ public void testPreview_multiSelect_longPress_showsOnlyOne() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Select two items - first image and video item
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
+ assertItemSelected(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, VIDEO_POSITION, ICON_THUMBNAIL_ID);
+ assertItemSelected(PICKER_TAB_RECYCLERVIEW_ID, VIDEO_POSITION, ICON_THUMBNAIL_ID);
+
+ // Long press second image item to preview the item.
+ longClickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_2_POSITION, ICON_THUMBNAIL_ID);
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ mRule.getScenario().onActivity(activity -> {
+ Selection selection
+ = new ViewModelProvider(activity).get(PickerViewModel.class).getSelection();
+ // Verify that we have two items(first image and video) as selected items and
+ // 1 item (second image) as item for preview
+ assertThat(selection.getSelectedItemCount().getValue()).isEqualTo(2);
+ assertThat(selection.getSelectedItemsForPreview().size()).isEqualTo(1);
+ });
+
+ final int imageViewId = R.id.preview_imageView;
+ onView(withId(imageViewId)).check(matches(isDisplayed()));
+
+ // Verify that only one item is being previewed. Swipe left and right, and verify we
+ // still have ImageView in preview.
+ swipeLeftAndWait(PREVIEW_VIEW_PAGER_ID);
+ onView(withId(imageViewId)).check(matches(isDisplayed()));
+
+ swipeRightAndWait(PREVIEW_VIEW_PAGER_ID);
+ onView(withId(imageViewId)).check(matches(isDisplayed()));
+ }
+ }
+
+ @Test
+ public void testPreview_selectButtonWidth() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+ // Navigate to preview
+ longClickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ // Check that Select button is visible
+ onView(withId(PREVIEW_ADD_OR_SELECT_BUTTON_ID)).check(matches(isDisplayed()));
+ onView(withId(PREVIEW_ADD_OR_SELECT_BUTTON_ID)).check(
+ matches(withText(R.string.select)));
+ }
+
+ setPortraitOrientation(mRule);
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ mRule.getScenario().onActivity(activity -> {
+ final Button addOrSelectButton
+ = activity.findViewById(PREVIEW_ADD_OR_SELECT_BUTTON_ID);
+ final int expectedAddOrSelectButtonWidth = activity.getResources()
+ .getDimensionPixelOffset(DIMEN_PREVIEW_ADD_OR_SELECT_WIDTH);
+ // Check that button width in portrait mode = R.dimen.preview_add_or_select_width
+ assertThat(addOrSelectButton.getWidth()).isEqualTo(expectedAddOrSelectButtonWidth);
+ });
+ }
+
+ setLandscapeOrientation(mRule);
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ mRule.getScenario().onActivity(activity -> {
+ final Button addOrSelectButton
+ = activity.findViewById(PREVIEW_ADD_OR_SELECT_BUTTON_ID);
+ final int expectedAddOrSelectButtonWidth = activity.getResources()
+ .getDimensionPixelOffset(DIMEN_PREVIEW_ADD_OR_SELECT_WIDTH);
+ // Check that button width in landscape mode is R.dimen.preview_add_or_select_width
+ assertThat(addOrSelectButton.getWidth()).isEqualTo(expectedAddOrSelectButtonWidth);
+ });
+ }
+ }
+
+ private void assertMultiSelectLongPressCommonLayoutMatches() {
+ assertMultiSelectLongPressCommonLayoutMatches(/* isSelected */ false);
+ }
+
+ private void assertMultiSelectLongPressCommonLayoutMatches(boolean isSelected) {
+ onView(withId(R.id.preview_viewPager)).check(matches(isDisplayed()));
+ onView(withId(PREVIEW_ADD_OR_SELECT_BUTTON_ID)).check(matches(isDisplayed()));
+ // Verify that the text in AddOrSelect button
+ if (isSelected) {
+ onView(withId(PREVIEW_ADD_OR_SELECT_BUTTON_ID))
+ .check(matches(withText(R.string.deselect)));
+ } else {
+ onView(withId(PREVIEW_ADD_OR_SELECT_BUTTON_ID))
+ .check(matches(withText(R.string.select)));
+ }
+
+ onView(withId(R.id.preview_selected_check_button)).check(matches(not(isDisplayed())));
+ onView(withId(R.id.preview_add_button)).check(matches(not(isDisplayed())));
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/PreviewMultiSelectTest.java b/tests/src/com/android/providers/media/photopicker/espresso/PreviewMultiSelectTest.java
new file mode 100644
index 0000000..3920059
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/PreviewMultiSelectTest.java
@@ -0,0 +1,420 @@
+/*
+ * 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.espresso;
+
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isNotSelected;
+import static androidx.test.espresso.matcher.ViewMatchers.isSelected;
+import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static com.android.providers.media.photopicker.espresso.BottomSheetTestUtils.assertBottomSheetState;
+import static com.android.providers.media.photopicker.espresso.CustomSwipeAction.swipeLeftAndWait;
+import static com.android.providers.media.photopicker.espresso.CustomSwipeAction.swipeRightAndWait;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.assertItemNotSelected;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.assertItemSelected;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.clickItem;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.longClickItem;
+
+import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.not;
+
+import android.view.View;
+
+import androidx.lifecycle.ViewModelProvider;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+import androidx.viewpager2.widget.ViewPager2;
+
+import com.android.providers.media.R;
+import com.android.providers.media.photopicker.data.Selection;
+import com.android.providers.media.photopicker.viewmodel.PickerViewModel;
+
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4ClassRunner.class)
+public class PreviewMultiSelectTest extends PhotoPickerBaseTest {
+ private static final int VIDEO_PREVIEW_THUMBNAIL_ID = R.id.preview_video_image;
+
+ @Rule
+ public ActivityScenarioRule<PhotoPickerTestActivity> mRule
+ = new ActivityScenarioRule<>(PhotoPickerBaseTest.getMultiSelectionIntent());
+
+ @Test
+ public void testPreview_multiSelect_common() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+ onView(withId(DRAG_BAR_ID)).check(matches(isDisplayed()));
+ onView(withId(PRIVACY_TEXT_ID)).check(matches(isDisplayed()));
+
+ // Select two items and Navigate to preview
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_2_POSITION, ICON_THUMBNAIL_ID);
+ onView(withId(VIEW_SELECTED_BUTTON_ID)).perform(click());
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ // No dragBar in preview
+ onView(withId(DRAG_BAR_ID)).check(matches(not(isDisplayed())));
+
+ // No privacy text in preview
+ onView(withId(PRIVACY_TEXT_ID)).check(matches(not(isDisplayed())));
+ mRule.getScenario().onActivity(activity -> {
+ assertBottomSheetState(activity, STATE_EXPANDED);
+ });
+
+ assertMultiSelectPreviewCommonLayoutDisplayed();
+ onView(withId(PREVIEW_ADD_OR_SELECT_BUTTON_ID)).check(matches(not(isDisplayed())));
+
+ // Verify ImageView is displayed
+ onView(withId(PREVIEW_IMAGE_VIEW_ID)).check(matches(isCompletelyDisplayed()));
+ }
+
+ // Click back button and verify we are back to photos tab
+ onView(withContentDescription("Navigate up")).perform(click());
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+ // Shows dragBar and privacy text after we are back to Photos tab
+ onView(withId(DRAG_BAR_ID)).check(matches(isDisplayed()));
+ onView(withId(PRIVACY_TEXT_ID)).check(matches(isDisplayed()));
+
+ }
+
+ @Test
+ public void testPreview_multiSelect_deselect() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Select first and second image
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_2_POSITION, ICON_THUMBNAIL_ID);
+ // Navigate to preview
+ onView(withId(VIEW_SELECTED_BUTTON_ID)).perform(click());
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ final String addButtonString =
+ getTargetContext().getResources().getString(R.string.add);
+ final int previewAddButtonId = R.id.preview_add_button;
+ final int previewSelectButtonId = R.id.preview_selected_check_button;
+ final String selectedString =
+ getTargetContext().getResources().getString(R.string.selected);
+
+ // Verify that, initially, we show "selected" check button
+ onView(withId(previewSelectButtonId)).check(matches(isSelected()));
+ onView(withId(previewSelectButtonId)).check(matches(withText(selectedString)));
+ // Verify that the text in Add button matches
+ onView(withId(previewAddButtonId))
+ .check(matches(withText(addButtonString + " (2)")));
+
+ // Deselect item in preview
+ onView(withId(previewSelectButtonId)).perform(click());
+ onView(withId(previewSelectButtonId)).check(matches(isNotSelected()));
+ onView(withId(previewSelectButtonId)).check(matches(withText(R.string.deselected)));
+ // Verify that the text in Add button now changes to "Add (1)"
+ onView(withId(previewAddButtonId))
+ .check(matches(withText(addButtonString + " (1)")));
+ // Verify that we have one item in selected items
+ mRule.getScenario().onActivity(activity -> {
+ Selection selection
+ = new ViewModelProvider(activity).get(PickerViewModel.class).getSelection();
+ assertThat(selection.getSelectedItemCount().getValue()).isEqualTo(1);
+ });
+
+ // Select the item again
+ onView(withId(previewSelectButtonId)).perform(click());
+ onView(withId(previewSelectButtonId)).check(matches(isSelected()));
+ onView(withId(previewSelectButtonId)).check(matches(withText(selectedString)));
+ // Verify that the text in Add button now changes back to "Add (2)"
+ onView(withId(previewAddButtonId))
+ .check(matches(withText(addButtonString + " (2)")));
+ // Verify that we have 2 items in selected items
+ mRule.getScenario().onActivity(activity -> {
+ Selection selection
+ = new ViewModelProvider(activity).get(PickerViewModel.class).getSelection();
+ assertThat(selection.getSelectedItemCount().getValue()).isEqualTo(2);
+ });
+ }
+ }
+
+ @Test
+ @Ignore("Enable after b/218806007 is fixed")
+ public void testPreview_multiSelect_navigation() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Select items
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, VIDEO_POSITION, ICON_THUMBNAIL_ID);
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_2_POSITION, ICON_THUMBNAIL_ID);
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
+ // Navigate to preview
+ onView(withId(VIEW_SELECTED_BUTTON_ID)).perform(click());
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ // Preview Order
+ // 1 - Image
+ // 2 - Image
+ // 3 - Video
+ // Navigate from Image -> Image -> Video -> Image -> Image -> Image and verify the
+ // layout matches
+
+ // 1. Image
+ assertMultiSelectPreviewCommonLayoutDisplayed();
+ onView(ViewPagerMatcher(PREVIEW_VIEW_PAGER_ID, PREVIEW_IMAGE_VIEW_ID))
+ .check(matches(isDisplayed()));
+ // Verify no special format icon is previewed
+ assertSpecialFormatBadgeDoesNotExist();
+
+ swipeLeftAndWait(PREVIEW_VIEW_PAGER_ID);
+ // 2. Image
+ assertMultiSelectPreviewCommonLayoutDisplayed();
+ onView(ViewPagerMatcher(PREVIEW_VIEW_PAGER_ID, PREVIEW_IMAGE_VIEW_ID))
+ .check(matches(isDisplayed()));
+ // Verify no special format icon is previewed
+ assertSpecialFormatBadgeDoesNotExist();
+
+ swipeLeftAndWait(PREVIEW_VIEW_PAGER_ID);
+ // 3. Video item
+ assertMultiSelectPreviewCommonLayoutDisplayed();
+ // TODO(b/197083539): We don't check the video image to be visible or not because its
+ // visibility is time sensitive. Try waiting till player is ready and assert that video
+ // image is no more visible.
+ onView(ViewPagerMatcher(PREVIEW_VIEW_PAGER_ID, VIDEO_PREVIEW_THUMBNAIL_ID))
+ .check(matches(isDisplayed()));
+ // Verify no special format icon is previewed
+ assertSpecialFormatBadgeDoesNotExist();
+
+ swipeRightAndWait(PREVIEW_VIEW_PAGER_ID);
+ // 2. Image
+ assertMultiSelectPreviewCommonLayoutDisplayed();
+ onView(ViewPagerMatcher(PREVIEW_VIEW_PAGER_ID, PREVIEW_IMAGE_VIEW_ID))
+ .check(matches(isDisplayed()));
+ // Verify no special format icon is previewed
+ assertSpecialFormatBadgeDoesNotExist();
+
+ swipeRightAndWait(PREVIEW_VIEW_PAGER_ID);
+ // 1. Image
+ assertMultiSelectPreviewCommonLayoutDisplayed();
+ onView(ViewPagerMatcher(PREVIEW_VIEW_PAGER_ID, PREVIEW_IMAGE_VIEW_ID))
+ .check(matches(isDisplayed()));
+ // Verify no special format icon is previewed
+ assertSpecialFormatBadgeDoesNotExist();
+
+ swipeLeftAndWait(PREVIEW_VIEW_PAGER_ID);
+ // 2. Image
+ assertMultiSelectPreviewCommonLayoutDisplayed();
+ onView(ViewPagerMatcher(PREVIEW_VIEW_PAGER_ID, PREVIEW_IMAGE_VIEW_ID))
+ .check(matches(isDisplayed()));
+ // Verify no special format icon is previewed
+ assertSpecialFormatBadgeDoesNotExist();
+ }
+ }
+
+ @Test
+ public void testPreview_multiSelect_fromAlbumsTab() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Select 1 item in Photos tab
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
+ final int iconCheckId = R.id.icon_check;
+ assertItemSelected(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, iconCheckId);
+
+ // Navigate to Albums tab
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .perform(click());
+ // The Albums tab item is selected
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .check(matches(isSelected()));
+ final int cameraStringId = R.string.picker_category_camera;
+ // Camera album is shown
+ onView(allOf(withText(cameraStringId),
+ isDescendantOfA(withId(PICKER_TAB_RECYCLERVIEW_ID)))).check(matches(isDisplayed()));
+
+ // Navigate to preview
+ onView(withId(VIEW_SELECTED_BUTTON_ID)).perform(click());
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ assertMultiSelectPreviewCommonLayoutDisplayed();
+ // Verify ImageView is displayed
+ onView(withId(PREVIEW_IMAGE_VIEW_ID)).check(matches(isCompletelyDisplayed()));
+ }
+
+ // Click back button and verify we are back to Albums tab
+ onView(withContentDescription("Navigate up")).perform(click());
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .check(matches(isSelected()));
+ onView(allOf(withText(cameraStringId),
+ isDescendantOfA(withId(PICKER_TAB_RECYCLERVIEW_ID)))).check(matches(isDisplayed()));
+ }
+
+ @Test
+ public void testPreview_viewSelectedAfterLongPress() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Select video item
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, VIDEO_POSITION, ICON_THUMBNAIL_ID);
+ assertItemSelected(PICKER_TAB_RECYCLERVIEW_ID, VIDEO_POSITION, ICON_THUMBNAIL_ID);
+
+ // Preview second image item using preview on long press
+ longClickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_2_POSITION, ICON_THUMBNAIL_ID);
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ // Verify that we have one item as selected item and 1 item as item for preview, and
+ // verify they are not the same.
+ mRule.getScenario().onActivity(activity -> {
+ Selection selection
+ = new ViewModelProvider(activity).get(PickerViewModel.class).getSelection();
+ assertThat(selection.getSelectedItemCount().getValue()).isEqualTo(1);
+ assertThat(selection.getSelectedItemsForPreview().size()).isEqualTo(1);
+ assertThat(selection.getSelectedItems().get(0))
+ .isNotEqualTo(selection.getSelectedItemsForPreview().get(0));
+ });
+ }
+
+ // Click back button to go back to Photos tab
+ onView(withContentDescription("Navigate up")).perform(click());
+
+ // Navigate to preview by clicking "View Selected" button.
+ onView(withId(VIEW_SELECTED_BUTTON_ID)).perform(click());
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ assertMultiSelectPreviewCommonLayoutDisplayed();
+
+ // Verify that "View Selected" shows the video item, not the image item that was
+ // previewed earlier with preview on long press
+ onView(ViewPagerMatcher(PREVIEW_VIEW_PAGER_ID, VIDEO_PREVIEW_THUMBNAIL_ID))
+ .check(matches(isDisplayed()));
+
+ // Swipe and verify we don't preview the image item
+ swipeLeftAndWait(PREVIEW_VIEW_PAGER_ID);
+ onView(ViewPagerMatcher(PREVIEW_VIEW_PAGER_ID, VIDEO_PREVIEW_THUMBNAIL_ID))
+ .check(matches(isDisplayed()));
+ swipeRightAndWait(PREVIEW_VIEW_PAGER_ID);
+ onView(ViewPagerMatcher(PREVIEW_VIEW_PAGER_ID, VIDEO_PREVIEW_THUMBNAIL_ID))
+ .check(matches(isDisplayed()));
+ // TODO (b/232792753): Assert video player visibility using custom IdlingResource
+ }
+ }
+
+ @Test
+ public void testPreview_multiSelect_acrossAlbums() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Select second image and video item from Photos tab
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_2_POSITION, ICON_THUMBNAIL_ID);
+ assertItemSelected(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_2_POSITION, ICON_THUMBNAIL_ID);
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, VIDEO_POSITION, ICON_THUMBNAIL_ID);
+ assertItemSelected(PICKER_TAB_RECYCLERVIEW_ID, VIDEO_POSITION, ICON_THUMBNAIL_ID);
+
+ // Navigate to albums
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .perform(click());
+
+ final int cameraStringId = R.string.picker_category_camera;
+ // Navigate to photos in Camera album
+ onView(allOf(withText(cameraStringId),
+ isDescendantOfA(withId(PICKER_TAB_RECYCLERVIEW_ID)))).perform(click());
+ // Select image item from Camera category
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, 1, ICON_THUMBNAIL_ID);
+ assertItemSelected(PICKER_TAB_RECYCLERVIEW_ID, /* position */ 1, ICON_THUMBNAIL_ID);
+
+ // Navigate to preview
+ onView(withId(VIEW_SELECTED_BUTTON_ID)).perform(click());
+
+ final int previewSelectedButtonId = R.id.preview_selected_check_button;
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ // Deselect the image item
+ onView(withId(previewSelectedButtonId)).perform(click());
+
+ // Go back to Camera album and verify that item is deselected
+ onView(withContentDescription("Navigate up")).perform(click());
+ assertItemNotSelected(PICKER_TAB_RECYCLERVIEW_ID, /* position */ 1, ICON_THUMBNAIL_ID);
+ }
+
+ // Go back to photo grid and verify that item is deselected
+ onView(withContentDescription("Navigate up")).perform(click());
+ // Navigate to Photo grid
+ onView(allOf(withText(PICKER_PHOTOS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .perform(click());
+
+ assertItemNotSelected(PICKER_TAB_RECYCLERVIEW_ID, /* position */ 1, ICON_THUMBNAIL_ID);
+
+ // Go back to preview and deselect another item
+ onView(withId(VIEW_SELECTED_BUTTON_ID)).perform(click());
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ // Deselect the second image item
+ onView(withId(previewSelectedButtonId)).perform(click());
+ }
+
+ // Go back to Photos tab and verify that second image item is deselected
+ onView(withContentDescription("Navigate up")).perform(click());
+ assertItemNotSelected(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_2_POSITION, ICON_THUMBNAIL_ID);
+ }
+
+ private void assertSpecialFormatBadgeDoesNotExist() {
+ onView(withId(PREVIEW_MOTION_PHOTO_ID)).check(doesNotExist());
+ onView(withId(PREVIEW_GIF_ID)).check(doesNotExist());
+ }
+
+ private void assertMultiSelectPreviewCommonLayoutDisplayed() {
+ onView(withId(PREVIEW_VIEW_PAGER_ID)).check(matches(isDisplayed()));
+ onView(withId(R.id.preview_add_button)).check(matches(isDisplayed()));
+ onView(withId(R.id.preview_selected_check_button)).check(matches(isDisplayed()));
+ onView(withId(R.id.preview_selected_check_button)).check(matches(isSelected()));
+ }
+
+ private Matcher<View> ViewPagerMatcher(int viewPagerId, int itemViewId) {
+ return new TypeSafeMatcher<View>() {
+ @Override
+ public void describeTo(Description description) {
+ // This is a minimal implementation which is enough to debug current test failures
+ description.appendText("is assignable from class: " + ViewPager2.class + " with "
+ + itemViewId);
+ }
+
+ @Override
+ public boolean matchesSafely(View view) {
+ final ViewPager2 viewPager = view.getRootView().findViewById(viewPagerId);
+ if (viewPager == null) {
+ return false;
+ }
+
+ return view == viewPager.findViewById(itemViewId);
+ }
+ };
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/PreviewSingleSelectTest.java b/tests/src/com/android/providers/media/photopicker/espresso/PreviewSingleSelectTest.java
new file mode 100644
index 0000000..df11c29
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/PreviewSingleSelectTest.java
@@ -0,0 +1,286 @@
+/*
+ * 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.espresso;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withParent;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static com.android.providers.media.photopicker.espresso.BottomSheetTestUtils.assertBottomSheetState;
+import static com.android.providers.media.photopicker.espresso.OrientationUtils.setLandscapeOrientation;
+import static com.android.providers.media.photopicker.espresso.OrientationUtils.setPortraitOrientation;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.longClickItem;
+
+import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED;
+import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.not;
+
+import android.app.Activity;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.widget.Button;
+
+import androidx.appcompat.widget.Toolbar;
+import androidx.test.espresso.IdlingRegistry;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+
+import com.android.providers.media.R;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4ClassRunner.class)
+public class PreviewSingleSelectTest extends PhotoPickerBaseTest {
+
+ @Rule
+ public ActivityScenarioRule<PhotoPickerTestActivity> mRule
+ = new ActivityScenarioRule<>(PhotoPickerBaseTest.getSingleSelectionIntent());
+
+ @Test
+ public void testPreview_singleSelect_image() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Bottomsheet assertions are different for landscape mode
+ setPortraitOrientation(mRule);
+
+ final BottomSheetIdlingResource bottomSheetIdlingResource =
+ BottomSheetIdlingResource.register(mRule);
+
+ try {
+ // TODO(b/226318844): When accessibility is enabled, we always launch the photo picker
+ // in full screen mode. Accessibility is enabled in Espresso test, we can't check the
+ // COLLAPSED state.
+// bottomSheetIdlingResource.setExpectedState(STATE_COLLAPSED);
+// onView(withId(DRAG_BAR_ID)).check(matches(isDisplayed()));
+// onView(withId(PRIVACY_TEXT_ID)).check(matches(isDisplayed()));
+// mRule.getScenario().onActivity(activity -> {
+// assertBottomSheetState(activity, STATE_COLLAPSED);
+// });
+
+ // Navigate to preview
+ longClickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ // No dragBar in preview
+ bottomSheetIdlingResource.setExpectedState(STATE_EXPANDED);
+ onView(withId(DRAG_BAR_ID)).check(matches(not(isDisplayed())));
+ // No privacy text in preview
+ onView(withId(PRIVACY_TEXT_ID)).check(matches(not(isDisplayed())));
+ mRule.getScenario().onActivity(activity -> {
+ assertBottomSheetState(activity, STATE_EXPANDED);
+ });
+
+ // Verify image is previewed
+ assertSingleSelectCommonLayoutMatches();
+ onView(withId(R.id.preview_imageView)).check(matches(isDisplayed()));
+ // Verify no special format icon is previewed
+ onView(withId(PREVIEW_MOTION_PHOTO_ID)).check(doesNotExist());
+ onView(withId(PREVIEW_GIF_ID)).check(doesNotExist());
+ }
+ // Navigate back to Photo grid
+ onView(withContentDescription("Navigate up")).perform(click());
+
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+ onView(withId(DRAG_BAR_ID)).check(matches(isDisplayed()));
+ onView(withId(PRIVACY_TEXT_ID)).check(matches(isDisplayed()));
+
+ // TODO(b/226318844): When accessibility is enabled, we always launch the photo picker
+ // in full screen mode. Accessibility is enabled in Espresso test, we can't check the
+ // COLLAPSED state.
+// bottomSheetIdlingResource.setExpectedState(STATE_COLLAPSED);
+// // Shows dragBar and privacy text after we are back to Photos tab
+// mRule.getScenario().onActivity(activity -> {
+// assertBottomSheetState(activity, STATE_COLLAPSED);
+// });
+ } finally {
+ IdlingRegistry.getInstance().unregister(bottomSheetIdlingResource);
+ }
+ }
+
+ @Test
+ public void testPreview_singleSelect_video() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Navigate to preview
+ longClickItem(PICKER_TAB_RECYCLERVIEW_ID, VIDEO_POSITION, ICON_THUMBNAIL_ID);
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ assertSingleSelectCommonLayoutMatches();
+ // Verify thumbnail view is displayed
+ onView(withId(R.id.preview_video_image)).check(matches(isDisplayed()));
+ // TODO (b/232792753): Assert video player visibility using custom IdlingResource
+
+ // Verify no special format icon is previewed
+ onView(withId(PREVIEW_MOTION_PHOTO_ID)).check(doesNotExist());
+ onView(withId(PREVIEW_GIF_ID)).check(doesNotExist());
+ }
+ }
+
+ @Test
+ public void testPreview_singleSelect_fromAlbumsPhoto() throws Exception {
+ // Navigate to Albums tab
+ onView(allOf(withText(PICKER_ALBUMS_STRING_ID), isDescendantOfA(withId(TAB_LAYOUT_ID))))
+ .perform(click());
+
+ final int cameraStringId = R.string.picker_category_camera;
+ // Navigate to photos in Camera album
+ onView(allOf(withText(cameraStringId),
+ isDescendantOfA(withId(PICKER_TAB_RECYCLERVIEW_ID)))).perform(click());
+
+ // Verify that toolbar has the title as category name Camera
+ onView(allOf(withText(cameraStringId), withParent(withId(R.id.toolbar))))
+ .check(matches(isDisplayed()));
+
+ // Navigate to preview
+ longClickItem(PICKER_TAB_RECYCLERVIEW_ID, /* position */ 1, ICON_THUMBNAIL_ID);
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ // Verify image is previewed
+ assertSingleSelectCommonLayoutMatches();
+ onView(withId(R.id.preview_imageView)).check(matches(isDisplayed()));
+ }
+
+ // Navigate back to Camera album
+ onView(withContentDescription("Navigate up")).perform(click());
+
+ // Verify that toolbar has the title as category name Camera
+ onView(allOf(withText(cameraStringId), withParent(withId(R.id.toolbar))))
+ .check(matches(isDisplayed()));
+ }
+
+ @Test
+ public void testPreview_noScrimLayerAndHasSolidColorInPortrait() throws Exception {
+ setPortraitOrientation(mRule);
+
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+ // Navigate to preview
+ longClickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ onView(withId(R.id.preview_top_scrim)).check(matches(not(isDisplayed())));
+ onView(withId(R.id.preview_bottom_scrim)).check(matches(not(isDisplayed())));
+
+ mRule.getScenario().onActivity(activity -> {
+ assertBackgroundColorOnToolbarAndBottomBar(activity,
+ R.color.preview_scrim_solid_color);
+ });
+ }
+ }
+
+ @Test
+ public void testPreview_showScrimLayerInLandscape() throws Exception {
+ setLandscapeOrientation(mRule);
+
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Navigate to preview
+ longClickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ onView(withId(R.id.preview_top_scrim)).check(matches(isDisplayed()));
+ onView(withId(R.id.preview_bottom_scrim)).check(matches(isDisplayed()));
+
+ mRule.getScenario().onActivity(activity -> {
+ assertBackgroundColorOnToolbarAndBottomBar(activity, android.R.color.transparent);
+ });
+ }
+ }
+
+ @Test
+ public void testPreview_addButtonWidth() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+ // Navigate to preview
+ longClickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ // Check that Add button is visible
+ onView(withId(PREVIEW_ADD_OR_SELECT_BUTTON_ID)).check(matches(isDisplayed()));
+ onView(withId(PREVIEW_ADD_OR_SELECT_BUTTON_ID)).check(matches(withText(R.string.add)));
+ }
+
+ setPortraitOrientation(mRule);
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ mRule.getScenario().onActivity(activity -> {
+ final Button addOrSelectButton
+ = activity.findViewById(PREVIEW_ADD_OR_SELECT_BUTTON_ID);
+ final int expectedAddOrSelectButtonWidth = activity.getResources()
+ .getDimensionPixelOffset(DIMEN_PREVIEW_ADD_OR_SELECT_WIDTH);
+ // Check that button width in portrait mode is = R.dimen.preview_add_or_select_width
+ assertThat(addOrSelectButton.getWidth()).isEqualTo(expectedAddOrSelectButtonWidth);
+ });
+ }
+
+ setLandscapeOrientation(mRule);
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ mRule.getScenario().onActivity(activity -> {
+ final Button addOrSelectButton
+ = activity.findViewById(PREVIEW_ADD_OR_SELECT_BUTTON_ID);
+ final int expectedAddOrSelectButtonWidth = activity.getResources()
+ .getDimensionPixelOffset(DIMEN_PREVIEW_ADD_OR_SELECT_WIDTH);
+ // Check that button width in landscape mode is R.dimen.preview_add_or_select_width
+ assertThat(addOrSelectButton.getWidth()).isEqualTo(expectedAddOrSelectButtonWidth);
+ });
+ }
+ }
+
+ private void assertBackgroundColorOnToolbarAndBottomBar(Activity activity, int colorResId) {
+ final Toolbar toolbar = activity.findViewById(R.id.toolbar);
+ final Drawable toolbarDrawable = toolbar.getBackground();
+
+ assertThat(toolbarDrawable).isInstanceOf(ColorDrawable.class);
+
+ final int expectedColor = activity.getColor(colorResId);
+ assertThat(((ColorDrawable) toolbarDrawable).getColor()).isEqualTo(expectedColor);
+
+ final View bottomBar = activity.findViewById(R.id.preview_bottom_bar);
+ final Drawable bottomBarDrawable = bottomBar.getBackground();
+
+ assertThat(bottomBarDrawable).isInstanceOf(ColorDrawable.class);
+ assertThat(((ColorDrawable) bottomBarDrawable).getColor()).isEqualTo(expectedColor);
+ }
+
+ private void assertSingleSelectCommonLayoutMatches() {
+ onView(withId(R.id.preview_viewPager)).check(matches(isDisplayed()));
+ onView(withId(PREVIEW_ADD_OR_SELECT_BUTTON_ID)).check(matches(isDisplayed()));
+ // Verify that the text in Add button
+ onView(withId(PREVIEW_ADD_OR_SELECT_BUTTON_ID)).check(matches(withText(R.string.add)));
+
+ onView(withId(R.id.preview_selected_check_button)).check(matches(not(isDisplayed())));
+ onView(withId(R.id.preview_add_button)).check(matches(not(isDisplayed())));
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/RecyclerViewItemCountAssertion.java b/tests/src/com/android/providers/media/photopicker/espresso/RecyclerViewItemCountAssertion.java
new file mode 100644
index 0000000..ded61c7
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/RecyclerViewItemCountAssertion.java
@@ -0,0 +1,48 @@
+/*
+ * 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.espresso;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.view.View;
+
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.test.espresso.NoMatchingViewException;
+import androidx.test.espresso.ViewAssertion;
+
+/**
+ * A {@link ViewAssertion} that asserts for item count of {@link RecyclerView}
+ * Shamelessly borrowed from various codebase.
+ */
+class RecyclerViewItemCountAssertion implements ViewAssertion {
+ private final int mExpectedCount;
+
+ public RecyclerViewItemCountAssertion(int expectedCount) {
+ mExpectedCount = expectedCount;
+ }
+
+ @Override
+ public void check(View view, NoMatchingViewException noMatchingViewException) {
+ if (noMatchingViewException != null) {
+ throw noMatchingViewException;
+ }
+
+ RecyclerView.Adapter adapter = ((RecyclerView) view).getAdapter();
+ assertThat(adapter).isNotNull();
+ assertThat(adapter.getItemCount()).isEqualTo(mExpectedCount);
+ }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/RecyclerViewMatcher.java b/tests/src/com/android/providers/media/photopicker/espresso/RecyclerViewMatcher.java
new file mode 100644
index 0000000..55c3ef0
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/RecyclerViewMatcher.java
@@ -0,0 +1,88 @@
+/*
+ * 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.espresso;
+
+import android.view.View;
+
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.RecyclerView;
+
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+/**
+ * A {@link org.hamcrest.Matcher} that checks the given View is assignable from
+ * {@link RecyclerView}
+ * <p>
+ * Shamelessly borrowed from various codebase.
+ */
+class RecyclerViewMatcher {
+ private final int mRecyclerViewId;
+
+ RecyclerViewMatcher(int recyclerViewId) {
+ mRecyclerViewId = recyclerViewId;
+ }
+
+ public static RecyclerViewMatcher withRecyclerView(int recyclerViewId) {
+ return new RecyclerViewMatcher(recyclerViewId);
+ }
+
+ public Matcher<View> atPositionOnView(int position, int targetViewId) {
+ return new TypeSafeMatcher<View>() {
+ @Nullable
+ private View mRecyclerViewChildView;
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("is assignable from class: " + RecyclerView.class
+ + ", at given position: " + position
+ + ", with given targetViewId: " + targetViewId);
+ }
+
+ @Override
+ public boolean matchesSafely(View view) {
+ if (mRecyclerViewChildView == null) {
+ RecyclerView recyclerView =
+ view.getRootView().findViewById(mRecyclerViewId);
+ if (recyclerView == null) {
+ // No RecyclerView with given Id, hence no match
+ return false;
+ }
+
+ RecyclerView.ViewHolder viewHolder =
+ recyclerView.findViewHolderForAdapterPosition(position);
+ if (viewHolder == null) {
+ // No viewHolder, hence no match
+ return false;
+ }
+
+ // Get itemView at given position from RecyclerView ViewHolder
+ mRecyclerViewChildView = viewHolder.itemView;
+ }
+
+ if (targetViewId == -1) {
+ return view == mRecyclerViewChildView;
+ } else {
+ // Returns specific view in the given RecyclerView item
+ View targetView = mRecyclerViewChildView.findViewById(targetViewId);
+ return view == targetView;
+ }
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/RecyclerViewTestUtils.java b/tests/src/com/android/providers/media/photopicker/espresso/RecyclerViewTestUtils.java
new file mode 100644
index 0000000..d41dd24
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/RecyclerViewTestUtils.java
@@ -0,0 +1,67 @@
+/*
+ * 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.espresso;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.longClick;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isNotSelected;
+import static androidx.test.espresso.matcher.ViewMatchers.isSelected;
+
+import static com.android.providers.media.photopicker.espresso.RecyclerViewMatcher.withRecyclerView;
+
+import static org.hamcrest.Matchers.not;
+
+class RecyclerViewTestUtils {
+ public static void assertItemDisplayed(int recyclerViewId, int position, int targetViewId) {
+ onView(withRecyclerView(recyclerViewId)
+ .atPositionOnView(position, targetViewId))
+ .check(matches(isDisplayed()));
+ }
+
+ public static void assertItemNotDisplayed(int recyclerViewId, int position, int targetViewId) {
+ onView(withRecyclerView(recyclerViewId)
+ .atPositionOnView(position, targetViewId))
+ .check(matches(not(isDisplayed())));
+ }
+
+ public static void assertItemSelected(int recyclerViewId, int position, int targetViewId) {
+ onView(withRecyclerView(recyclerViewId)
+ .atPositionOnView(position, targetViewId))
+ .check(matches(isSelected()));
+ }
+
+ public static void assertItemNotSelected(int recyclerViewId, int position, int targetViewId) {
+ onView(withRecyclerView(recyclerViewId)
+ .atPositionOnView(position, targetViewId))
+ .check(matches(isNotSelected()));
+ }
+
+ public static void clickItem(int recyclerViewId, int position, int targetViewId) {
+ onView(withRecyclerView(recyclerViewId)
+ .atPositionOnView(position, targetViewId))
+ .perform(click());
+ }
+
+ public static void longClickItem(int recyclerViewId, int position, int targetViewId) {
+ onView(withRecyclerView(recyclerViewId)
+ .atPositionOnView(position, targetViewId))
+ .perform(longClick());
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/SpecialFormatBaseTest.java b/tests/src/com/android/providers/media/photopicker/espresso/SpecialFormatBaseTest.java
new file mode 100644
index 0000000..4c654bf
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/SpecialFormatBaseTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+package com.android.providers.media.photopicker.espresso;
+
+import static com.android.providers.media.scan.MediaScannerTest.stage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.net.Uri;
+import android.os.Environment;
+import android.provider.MediaStore;
+
+import androidx.test.espresso.Espresso;
+import androidx.test.espresso.IdlingRegistry;
+
+import com.android.providers.media.R;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import java.io.File;
+import java.io.IOException;
+
+public class SpecialFormatBaseTest extends PhotoPickerBaseTest {
+
+ protected static final int ICON_GIF_ID = R.id.icon_gif;
+ protected static final int ICON_MOTION_PHOTO_ID = R.id.icon_motion_photo;
+ protected static final int VIDEO_CONTAINER_ID = R.id.video_container;
+ protected static final int OVERLAY_GRADIENT_ID = R.id.overlay_gradient;
+ protected static final int PREVIEW_GIF_ID = R.id.preview_gif;
+ protected static final int PREVIEW_MOTION_PHOTO_ID = R.id.preview_motion_photo;
+
+ protected static final File MOTION_PHOTO_FILE =
+ new File(Environment.getExternalStorageDirectory(),
+ Environment.DIRECTORY_PICTURES
+ + "/motionphoto_" + System.currentTimeMillis() + ".jpeg");
+ protected static final File GIF_FILE = new File(Environment.getExternalStorageDirectory(),
+ Environment.DIRECTORY_DOWNLOADS + "/gif_" + System.currentTimeMillis() + ".gif");
+ protected static final File ANIMATED_WEBP_FILE = new File(
+ Environment.getExternalStorageDirectory(), Environment.DIRECTORY_DOWNLOADS
+ + "/animatedWebp_" + System.currentTimeMillis() + ".webp");
+ protected static final File NON_ANIMATED_WEBP_FILE = new File(
+ Environment.getExternalStorageDirectory(), Environment.DIRECTORY_DOWNLOADS
+ + "/nonAnimatedWebp_" + System.currentTimeMillis() + ".webp");
+
+ /**
+ * The position of the gif item in the grid on the Photos tab
+ */
+ protected static final int GIF_POSITION = 1;
+
+ /**
+ * The position of the animated webp item in the grid on the Photos tab
+ */
+ protected static final int ANIMATED_WEBP_POSITION = 2;
+
+ /**
+ * The position of the motion photo item in the grid on the Photos tab
+ */
+ protected static final int MOTION_PHOTO_POSITION = 3;
+
+ /**
+ * The position of the non-animated webp item in the grid on the Photos tab
+ */
+ protected static final int NON_ANIMATED_WEBP_POSITION = 4;
+
+ @BeforeClass
+ public static void setupClass() throws Exception {
+ PhotoPickerBaseTest.setupClass();
+ // Reduce the number of files for this test. This is to reduce the dependency on other
+ // PhotoPicker components. We don't need to swipe up bottomsheet to view more files.
+ deleteFiles(/* invalidateMediaStore */ true);
+ createSpecialFormatFiles();
+ }
+
+ @AfterClass
+ public static void destroyClass() {
+ PhotoPickerBaseTest.destroyClass();
+ NON_ANIMATED_WEBP_FILE.delete();
+ ANIMATED_WEBP_FILE.delete();
+ MOTION_PHOTO_FILE.delete();
+ GIF_FILE.delete();
+ }
+
+ private static void createSpecialFormatFiles() throws Exception {
+ createFile(NON_ANIMATED_WEBP_FILE, R.raw.test_non_animated_webp);
+ createFile(ANIMATED_WEBP_FILE, R.raw.test_animated_webp);
+ createFile(MOTION_PHOTO_FILE, R.raw.test_motion_photo);
+ createFile(GIF_FILE, R.raw.test_gif);
+ }
+
+ private static void createFile(File file, int resId) throws IOException {
+ File parentFile = file.getParentFile();
+ parentFile.mkdirs();
+
+ assertThat(parentFile.exists()).isTrue();
+ file = stage(resId, file);
+ assertThat(file.exists()).isTrue();
+
+ final Uri uri = MediaStore.scanFile(getIsolatedContext().getContentResolver(), file);
+ MediaStore.waitForIdle(getIsolatedContext().getContentResolver());
+ assertThat(uri).isNotNull();
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/SpecialFormatMultiSelectTest.java b/tests/src/com/android/providers/media/photopicker/espresso/SpecialFormatMultiSelectTest.java
new file mode 100644
index 0000000..276f74d
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/SpecialFormatMultiSelectTest.java
@@ -0,0 +1,179 @@
+/*
+ * 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.espresso;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+
+import static com.android.providers.media.photopicker.espresso.CustomSwipeAction.swipeLeftAndWait;
+import static com.android.providers.media.photopicker.espresso.CustomSwipeAction.swipeRightAndWait;
+
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.clickItem;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.longClickItem;
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+
+import com.android.providers.media.R;
+
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class SpecialFormatMultiSelectTest extends SpecialFormatBaseTest {
+
+ @Rule
+ public ActivityScenarioRule<PhotoPickerTestActivity> mRule
+ = new ActivityScenarioRule<>(PhotoPickerBaseTest.getMultiSelectionIntent());
+
+ @Test
+ public void testPreview_multiSelect_longPress_gif() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Navigate to preview
+ longClickItem(PICKER_TAB_RECYCLERVIEW_ID, GIF_POSITION, ICON_THUMBNAIL_ID);
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ // Verify imageView is displayed for gif preview
+ onView(withId(PREVIEW_GIF_ID)).check(matches(isDisplayed()));
+ onView(withId(R.id.preview_imageView)).check(matches(isDisplayed()));
+ onView(withId(PREVIEW_MOTION_PHOTO_ID)).check(doesNotExist());
+ }
+ }
+
+ @Test
+ public void testPreview_multiSelect_longPress_animatedWebp() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Navigate to preview
+ longClickItem(PICKER_TAB_RECYCLERVIEW_ID, ANIMATED_WEBP_POSITION, ICON_THUMBNAIL_ID);
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ // Verify imageView is displayed for animated webp preview
+ onView(withId(R.id.preview_imageView)).check(matches(isDisplayed()));
+
+ // Verify GIF icon is shown for animated webp preview
+ onView(withId(PREVIEW_GIF_ID)).check(matches(isDisplayed()));
+
+ // Verify Motion Photo icon is not shown for animated webp preview
+ onView(withId(PREVIEW_MOTION_PHOTO_ID)).check(doesNotExist());
+ }
+ }
+
+ @Test
+ public void testPreview_multiSelect_longPress_nonAnimatedWebp() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Navigate to preview
+ longClickItem(PICKER_TAB_RECYCLERVIEW_ID, NON_ANIMATED_WEBP_POSITION, ICON_THUMBNAIL_ID);
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ // Verify imageView is displayed for non-animated webp preview
+ onView(withId(R.id.preview_imageView)).check(matches(isDisplayed()));
+
+ // Verify GIF icon is not shown for non-animated webp preview
+ onView(withId(PREVIEW_GIF_ID)).check(doesNotExist());
+
+ // Verify Motion Photo icon is not shown for non-animated webp preview
+ onView(withId(PREVIEW_MOTION_PHOTO_ID)).check(doesNotExist());
+ }
+ }
+
+ @Test
+ public void testPreview_multiSelect_longPress_motionPhoto() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Navigate to preview
+ longClickItem(PICKER_TAB_RECYCLERVIEW_ID, MOTION_PHOTO_POSITION, ICON_THUMBNAIL_ID);
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ // Verify imageView is displayed for motion photo preview
+ onView(withId(PREVIEW_MOTION_PHOTO_ID)).check(matches(isDisplayed()));
+ onView(withId(R.id.preview_imageView)).check(matches(isDisplayed()));
+ onView(withId(PREVIEW_GIF_ID)).check(doesNotExist());
+ }
+ }
+
+ @Test
+ @Ignore("Enable after b/218806007 is fixed")
+ public void testPreview_multiSelect_navigation() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Select items
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, GIF_POSITION, ICON_THUMBNAIL_ID);
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, ANIMATED_WEBP_POSITION, ICON_THUMBNAIL_ID);
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, MOTION_PHOTO_POSITION, ICON_THUMBNAIL_ID);
+ clickItem(PICKER_TAB_RECYCLERVIEW_ID, NON_ANIMATED_WEBP_POSITION, ICON_THUMBNAIL_ID);
+ // Navigate to preview
+ onView(withId(VIEW_SELECTED_BUTTON_ID)).perform(click());
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ // Preview Order
+ // 1 - Gif
+ // 2 - Animated Webp
+ // 3 - MotionPhoto
+ // 4 - Non-Animated Webp
+ // Navigate from Gif -> Motion Photo -> Animated Webp -> Non-Animated Webp ->
+ // Animated Webp -> Gif and verify the layout matches.
+ // This test does not check for common layout as that is already covered in
+ // other tests.
+
+ // 1. Gif
+ onView(withId(PREVIEW_GIF_ID)).check(matches(isDisplayed()));
+ onView(withId(PREVIEW_MOTION_PHOTO_ID)).check(doesNotExist());
+
+ swipeLeftAndWait(PREVIEW_VIEW_PAGER_ID);
+ // 2. Animated Webp
+ onView(withId(PREVIEW_GIF_ID)).check(matches(isDisplayed()));
+ onView(withId(PREVIEW_MOTION_PHOTO_ID)).check(doesNotExist());
+
+ swipeLeftAndWait(PREVIEW_VIEW_PAGER_ID);
+ // 3. Motion Photo
+ onView(withId(PREVIEW_MOTION_PHOTO_ID)).check(matches(isDisplayed()));
+ onView(withId(PREVIEW_GIF_ID)).check(doesNotExist());
+
+ swipeLeftAndWait(PREVIEW_VIEW_PAGER_ID);
+ // 4. Non-Animated Webp
+ onView(withId(PREVIEW_GIF_ID)).check(doesNotExist());
+ onView(withId(PREVIEW_MOTION_PHOTO_ID)).check(doesNotExist());
+
+ swipeRightAndWait(PREVIEW_VIEW_PAGER_ID);
+ // 3. Motion Photo
+ onView(withId(PREVIEW_MOTION_PHOTO_ID)).check(matches(isDisplayed()));
+ onView(withId(PREVIEW_GIF_ID)).check(doesNotExist());
+
+ swipeRightAndWait(PREVIEW_VIEW_PAGER_ID);
+ // 2. Animated Webp
+ onView(withId(PREVIEW_GIF_ID)).check(matches(isDisplayed()));
+ onView(withId(PREVIEW_MOTION_PHOTO_ID)).check(doesNotExist());
+
+ swipeRightAndWait(PREVIEW_VIEW_PAGER_ID);
+ // 1. Gif
+ onView(withId(PREVIEW_GIF_ID)).check(matches(isDisplayed()));
+ onView(withId(PREVIEW_MOTION_PHOTO_ID)).check(doesNotExist());
+ }
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/SpecialFormatSingleSelectTest.java b/tests/src/com/android/providers/media/photopicker/espresso/SpecialFormatSingleSelectTest.java
new file mode 100644
index 0000000..548478b
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/SpecialFormatSingleSelectTest.java
@@ -0,0 +1,199 @@
+/*
+ * 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.espresso;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.assertItemDisplayed;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.assertItemNotDisplayed;
+import static com.android.providers.media.photopicker.espresso.RecyclerViewTestUtils.longClickItem;
+
+import static org.hamcrest.Matchers.not;
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+
+import com.android.providers.media.R;
+
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class SpecialFormatSingleSelectTest extends SpecialFormatBaseTest {
+
+ @Rule
+ public ActivityScenarioRule<PhotoPickerTestActivity> mRule
+ = new ActivityScenarioRule<>(PhotoPickerBaseTest.getSingleSelectionIntent());
+
+ @Test
+ public void testPhotoGridLayout_motionPhoto() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Verify we have the thumbnail
+ assertItemDisplayed(PICKER_TAB_RECYCLERVIEW_ID, MOTION_PHOTO_POSITION, ICON_THUMBNAIL_ID);
+ // Verify motion photo icon is displayed
+ assertItemDisplayed(PICKER_TAB_RECYCLERVIEW_ID, MOTION_PHOTO_POSITION, OVERLAY_GRADIENT_ID);
+ assertItemDisplayed(PICKER_TAB_RECYCLERVIEW_ID, MOTION_PHOTO_POSITION,
+ ICON_MOTION_PHOTO_ID);
+
+ // Verify check icon, video icon and gif icon are not displayed
+ assertSingleSelectImageThumbnailCommonLayout(MOTION_PHOTO_POSITION);
+ assertItemNotDisplayed(PICKER_TAB_RECYCLERVIEW_ID, MOTION_PHOTO_POSITION, ICON_GIF_ID);
+ }
+
+ @Test
+ public void testPhotoGridLayout_gif() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Verify we have the thumbnail
+ assertItemDisplayed(PICKER_TAB_RECYCLERVIEW_ID, GIF_POSITION, ICON_THUMBNAIL_ID);
+ // Verify gif icon is displayed
+ assertItemDisplayed(PICKER_TAB_RECYCLERVIEW_ID, GIF_POSITION, OVERLAY_GRADIENT_ID);
+ assertItemDisplayed(PICKER_TAB_RECYCLERVIEW_ID, GIF_POSITION, ICON_GIF_ID);
+
+ // Verify check icon, video icon and motion photo icon are not displayed
+ assertSingleSelectImageThumbnailCommonLayout(GIF_POSITION);
+ assertItemNotDisplayed(PICKER_TAB_RECYCLERVIEW_ID, GIF_POSITION, ICON_MOTION_PHOTO_ID);
+ }
+
+ @Test
+ public void testPhotoGridLayout_animatedWebp() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Verify we have the animated webp thumbnail
+ assertItemDisplayed(PICKER_TAB_RECYCLERVIEW_ID, ANIMATED_WEBP_POSITION, ICON_THUMBNAIL_ID);
+ // Verify gif icon is displayed for animated webp
+ assertItemDisplayed(PICKER_TAB_RECYCLERVIEW_ID, ANIMATED_WEBP_POSITION,
+ OVERLAY_GRADIENT_ID);
+ assertItemDisplayed(PICKER_TAB_RECYCLERVIEW_ID, ANIMATED_WEBP_POSITION, ICON_GIF_ID);
+
+ // Verify check icon, video icon and motion photo icon are not displayed
+ assertSingleSelectImageThumbnailCommonLayout(ANIMATED_WEBP_POSITION);
+ assertItemNotDisplayed(PICKER_TAB_RECYCLERVIEW_ID, ANIMATED_WEBP_POSITION,
+ ICON_MOTION_PHOTO_ID);
+ }
+
+ @Test
+ public void testPhotoGridLayout_nonAnimatedWebp() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Verify we have the non-animated webp thumbnail
+ assertItemDisplayed(PICKER_TAB_RECYCLERVIEW_ID, NON_ANIMATED_WEBP_POSITION,
+ ICON_THUMBNAIL_ID);
+ // Verify gif icon is not displayed for non-animated webp
+ assertItemNotDisplayed(PICKER_TAB_RECYCLERVIEW_ID, NON_ANIMATED_WEBP_POSITION,
+ OVERLAY_GRADIENT_ID);
+ assertItemNotDisplayed(PICKER_TAB_RECYCLERVIEW_ID, NON_ANIMATED_WEBP_POSITION, ICON_GIF_ID);
+
+ // Verify check icon, video icon and motion photo icon are not displayed
+ assertSingleSelectImageThumbnailCommonLayout(NON_ANIMATED_WEBP_POSITION);
+ assertItemNotDisplayed(PICKER_TAB_RECYCLERVIEW_ID, NON_ANIMATED_WEBP_POSITION,
+ ICON_MOTION_PHOTO_ID);
+ }
+
+ @Test
+ public void testPreview_singleSelect_gif() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Navigate to preview
+ longClickItem(PICKER_TAB_RECYCLERVIEW_ID, GIF_POSITION, ICON_THUMBNAIL_ID);
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ // Verify gif icon is displayed for gif preview
+ assertSingleSelectImagePreviewCommonLayout();
+ onView(withId(PREVIEW_GIF_ID)).check(matches(isDisplayed()));
+ onView(withId(PREVIEW_MOTION_PHOTO_ID)).check(doesNotExist());
+ }
+ }
+
+ @Test
+ public void testPreview_singleSelect_animatedWebp() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Navigate to preview
+ longClickItem(PICKER_TAB_RECYCLERVIEW_ID, ANIMATED_WEBP_POSITION, ICON_THUMBNAIL_ID);
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ // Verify gif icon is displayed for animated preview
+ assertSingleSelectImagePreviewCommonLayout();
+ onView(withId(PREVIEW_GIF_ID)).check(matches(isDisplayed()));
+ onView(withId(PREVIEW_MOTION_PHOTO_ID)).check(doesNotExist());
+ }
+ }
+
+ @Test
+ @Ignore("Enable after b/222013536 is fixed")
+ public void testPreview_singleSelect_nonAnimatedWebp() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // This is the 4th item which is on the second row
+ BottomSheetTestUtils.swipeUp(mRule);
+
+ // Navigate to preview
+ longClickItem(PICKER_TAB_RECYCLERVIEW_ID, NON_ANIMATED_WEBP_POSITION, ICON_THUMBNAIL_ID);
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ // Verify gif icon is not displayed for non-animated webp preview
+ assertSingleSelectImagePreviewCommonLayout();
+ onView(withId(PREVIEW_GIF_ID)).check(doesNotExist());
+ onView(withId(PREVIEW_MOTION_PHOTO_ID)).check(doesNotExist());
+ }
+ }
+
+ @Test
+ public void testPreview_singleSelect_motionPhoto() throws Exception {
+ onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
+
+ // Navigate to preview
+ longClickItem(PICKER_TAB_RECYCLERVIEW_ID, MOTION_PHOTO_POSITION, ICON_THUMBNAIL_ID);
+
+ try (ViewPager2IdlingResource idlingResource
+ = ViewPager2IdlingResource.register(mRule, PREVIEW_VIEW_PAGER_ID)) {
+ // Verify motion photo icon is displayed for motion photo preview
+ assertSingleSelectImagePreviewCommonLayout();
+ onView(withId(PREVIEW_MOTION_PHOTO_ID)).check(matches(isDisplayed()));
+ onView(withId(PREVIEW_GIF_ID)).check(doesNotExist());
+ }
+ }
+
+ private void assertSingleSelectCommonLayoutMatches() {
+ onView(withId(R.id.preview_viewPager)).check(matches(isDisplayed()));
+ onView(withId(PREVIEW_ADD_OR_SELECT_BUTTON_ID)).check(matches(isDisplayed()));
+ // Verify that the text in Add button
+ onView(withId(PREVIEW_ADD_OR_SELECT_BUTTON_ID)).check(matches(withText(R.string.add)));
+
+ onView(withId(R.id.preview_selected_check_button)).check(matches(not(isDisplayed())));
+ onView(withId(R.id.preview_add_button)).check(matches(not(isDisplayed())));
+ }
+
+ private void assertSingleSelectImagePreviewCommonLayout() {
+ onView(withId(R.id.preview_imageView)).check(matches(isDisplayed()));
+ assertSingleSelectCommonLayoutMatches();
+ }
+
+ private void assertSingleSelectImageThumbnailCommonLayout(int position) {
+ assertItemNotDisplayed(PICKER_TAB_RECYCLERVIEW_ID, position, ICON_CHECK_ID);
+ assertItemNotDisplayed(PICKER_TAB_RECYCLERVIEW_ID, position, VIDEO_CONTAINER_ID);
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/ViewPager2IdlingResource.java b/tests/src/com/android/providers/media/photopicker/espresso/ViewPager2IdlingResource.java
new file mode 100644
index 0000000..f3b6d86
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/ViewPager2IdlingResource.java
@@ -0,0 +1,80 @@
+/*
+ * 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.espresso;
+
+import androidx.test.espresso.IdlingRegistry;
+import androidx.test.espresso.IdlingResource;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.viewpager2.widget.ViewPager2;
+
+/**
+ * An {@link IdlingResource} waiting for the {@link ViewPager2} swipe to enter
+ * {@link ViewPager2#SCROLL_STATE_IDLE} state.
+ */
+public class ViewPager2IdlingResource implements IdlingResource, AutoCloseable {
+ private final ViewPager2 mViewPager;
+ private ResourceCallback mResourceCallback;
+
+ public ViewPager2IdlingResource(ViewPager2 viewPager) {
+ mViewPager = viewPager;
+ viewPager.registerOnPageChangeCallback(new IdleStateListener());
+ }
+
+ @Override
+ public String getName() {
+ return ViewPager2IdlingResource.class.getSimpleName();
+ }
+
+ @Override
+ public boolean isIdleNow() {
+ return mViewPager.getScrollState() == ViewPager2.SCROLL_STATE_IDLE;
+ }
+
+ @Override
+ public void registerIdleTransitionCallback(ResourceCallback callback) {
+ mResourceCallback = callback;
+ }
+
+ @Override
+ public void close() throws Exception {
+ IdlingRegistry.getInstance().unregister(this);
+ }
+
+ private final class IdleStateListener extends ViewPager2.OnPageChangeCallback {
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ if (state == ViewPager2.SCROLL_STATE_IDLE && mResourceCallback != null) {
+ mResourceCallback.onTransitionToIdle();
+ }
+ }
+ }
+
+ /**
+ * @return {@link ViewPager2IdlingResource} that is registered to the activity
+ * related to the given {@link ActivityScenarioRule} and the resource ID of the ViewPager2.
+ */
+ public static ViewPager2IdlingResource register(
+ ActivityScenarioRule<PhotoPickerTestActivity> rule, int viewPager2Id) {
+ final ViewPager2IdlingResource[] idlingResources = new ViewPager2IdlingResource[1];
+ rule.getScenario().onActivity((activity -> {
+ idlingResources[0] = new ViewPager2IdlingResource(
+ activity.findViewById(viewPager2Id));
+ }));
+ IdlingRegistry.getInstance().register(idlingResources[0]);
+ return idlingResources[0];
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/WorkAppsOffProfileButtonTest.java b/tests/src/com/android/providers/media/photopicker/espresso/WorkAppsOffProfileButtonTest.java
new file mode 100644
index 0000000..7eb2f7f
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/espresso/WorkAppsOffProfileButtonTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.espresso;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+
+import com.android.providers.media.R;
+
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4ClassRunner.class)
+public class WorkAppsOffProfileButtonTest extends PhotoPickerBaseTest {
+ @BeforeClass
+ public static void setupClass() throws Exception {
+ PhotoPickerBaseTest.setupClass();
+ PhotoPickerBaseTest.setUpWorkAppsOffProfileButton();
+ }
+
+ @Rule
+ public ActivityScenarioRule<PhotoPickerTestActivity> mRule =
+ new ActivityScenarioRule<>(PhotoPickerBaseTest.getSingleSelectionIntent());
+
+ @Test
+ public void testProfileButton_dialog() {
+ final int profileButtonId = R.id.profile_button;
+
+ // Verify profile button is displayed
+ onView(withId(profileButtonId)).check(matches(isDisplayed()));
+ // Check the text on the button. It should be "Switch to work"
+ onView(withText(R.string.picker_work_profile)).check(matches(isDisplayed()));
+
+ // Verify onClick shows a dialog
+ onView(withId(profileButtonId)).check(matches(isDisplayed())).perform(click());
+ onView(withText(R.string.picker_profile_work_paused_title)).check(
+ matches(isDisplayed()));
+ onView(withText(R.string.picker_profile_work_paused_msg)).check(matches(isDisplayed()));
+ onView(withText(android.R.string.ok)).check(matches(isDisplayed())).perform(click());
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/util/DateTimeUtilsTest.java b/tests/src/com/android/providers/media/photopicker/util/DateTimeUtilsTest.java
new file mode 100644
index 0000000..2c29188
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/util/DateTimeUtilsTest.java
@@ -0,0 +1,195 @@
+/*
+ * 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.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.util.Locale;
+
+@RunWith(AndroidJUnit4.class)
+public class DateTimeUtilsTest {
+
+ private static LocalDate FAKE_DATE =
+ LocalDate.of(2020 /* year */, 7 /* month */, 7 /* dayOfMonth */);
+ private static long FAKE_TIME =
+ FAKE_DATE.atStartOfDay().toInstant(ZoneOffset.UTC).toEpochMilli();
+
+ @Test
+ public void testGetDateHeaderString_today() throws Exception {
+ final long when = generateDateTimeMillis(FAKE_DATE);
+
+ String result = DateTimeUtils.getDateHeaderString(when, FAKE_DATE);
+
+ assertThat(result).isEqualTo(DateTimeUtils.getTodayString());
+ }
+
+ @Test
+ public void testGetDateHeaderString_yesterday() throws Exception {
+ final LocalDate whenDate = FAKE_DATE.minusDays(1);
+ final long when = generateDateTimeMillis(whenDate);
+
+ final String result = DateTimeUtils.getDateHeaderString(when, FAKE_DATE);
+
+ assertThat(result).isEqualTo(DateTimeUtils.getYesterdayString());
+ }
+
+ @Test
+ public void testGetDateHeaderString_weekday() throws Exception {
+ final LocalDate whenDate = FAKE_DATE.minusDays(3);
+ final long when = generateDateTimeMillis(whenDate);
+
+ final String result = DateTimeUtils.getDateHeaderString(when, FAKE_DATE);
+
+ assertThat(result).isEqualTo("Saturday");
+ }
+
+ @Test
+ public void testGetDateHeaderString_weekdayAndDate() throws Exception {
+ final LocalDate whenDate = FAKE_DATE.minusMonths(1);
+ final long when = generateDateTimeMillis(whenDate);
+
+ final String result = DateTimeUtils.getDateHeaderString(when, FAKE_DATE);
+
+ assertThat(result).isEqualTo("Sun, Jun 7");
+ }
+
+ @Test
+ public void testGetDateHeaderString_weekdayDateAndYear() throws Exception {
+ final LocalDate whenDate = FAKE_DATE.minusYears(1);
+ long when = generateDateTimeMillis(whenDate);
+
+ final String result = DateTimeUtils.getDateHeaderString(when, FAKE_DATE);
+
+ assertThat(result).isEqualTo("Sun, Jul 7, 2019");
+ }
+
+ /**
+ * Test the capitalized issue in different languages b/208864827.
+ * E.g. For pt-BR
+ * Wrong format: ter, 16 de nov.
+ * Right format: Ter, 16 de nov.
+ */
+ @Test
+ public void testCapitalizedInDifferentLanguages() throws Exception {
+ final LocalDate whenDate = FAKE_DATE.minusMonths(1).minusDays(4);;
+ final long when = generateDateTimeMillis(whenDate);
+ final String skeleton = "EMMMd";
+
+ assertThat(DateTimeUtils.getDateTimeString(when, skeleton, new Locale("PT-BR")))
+ .isEqualTo("Qua., 3 de jun.");
+ assertThat(DateTimeUtils.getDateTimeString(when, skeleton, new Locale("ET")))
+ .isEqualTo("K, 3. juuni");
+ assertThat(DateTimeUtils.getDateTimeString(when, skeleton, new Locale("LV")))
+ .isEqualTo("Trešd., 3. jūn.");
+ assertThat(DateTimeUtils.getDateTimeString(when, skeleton, new Locale("BE")))
+ .isEqualTo("Ср, 3 чэр");
+ assertThat(DateTimeUtils.getDateTimeString(when, skeleton, new Locale("RU")))
+ .isEqualTo("Ср, 3 июн.");
+ assertThat(DateTimeUtils.getDateTimeString(when, skeleton, new Locale("SQ")))
+ .isEqualTo("Mër, 3 qer");
+ assertThat(DateTimeUtils.getDateTimeString(when, skeleton, new Locale("IT")))
+ .isEqualTo("Mer 3 giu");
+ assertThat(DateTimeUtils.getDateTimeString(when, skeleton, new Locale("KK")))
+ .isEqualTo("3 мау., ср");
+ }
+
+ @Test
+ public void testGetDateTimeStringForContentDesc() throws Exception {
+ final long when = generateDateTimeMillis(FAKE_DATE);
+
+ String result = DateTimeUtils.getDateTimeStringForContentDesc(when);
+
+ assertThat(result).isEqualTo("Jul 7, 2020, 12:00:00 AM");
+ }
+
+ @Test
+ public void testGetDateTimeStringForContentDesc_time() throws Exception {
+ long when = generateDateTimeMillisAt(
+ FAKE_DATE, /* hour */ 10, /* minute */ 10, /* second */ 10);
+
+ final String result = DateTimeUtils.getDateTimeStringForContentDesc(when);
+
+ assertThat(result).isEqualTo("Jul 7, 2020, 10:10:10 AM");
+ }
+
+ @Test
+ public void testGetDateTimeStringForContentDesc_singleDigitHour() throws Exception {
+ long when = generateDateTimeMillisAt(
+ FAKE_DATE, /* hour */ 1, /* minute */ 0, /* second */ 0);
+
+ final String result = DateTimeUtils.getDateTimeStringForContentDesc(when);
+
+ assertThat(result).isEqualTo("Jul 7, 2020, 1:00:00 AM");
+ }
+
+ @Test
+ public void testGetDateTimeStringForContentDesc_timePM() throws Exception {
+ long when = generateDateTimeMillisAt(
+ FAKE_DATE, /* hour */ 22, /* minute */ 0, /* second */ 0);
+
+ final String result = DateTimeUtils.getDateTimeStringForContentDesc(when);
+
+ assertThat(result).isEqualTo("Jul 7, 2020, 10:00:00 PM");
+ }
+
+ @Test
+ public void testIsSameDay_differentYear_false() throws Exception {
+ final LocalDate whenDate = FAKE_DATE.minusYears(1);
+ long when = generateDateTimeMillis(whenDate);
+
+ assertThat(DateTimeUtils.isSameDate(when, FAKE_TIME)).isFalse();
+ }
+
+ @Test
+ public void testIsSameDay_differentMonth_false() throws Exception {
+ final LocalDate whenDate = FAKE_DATE.minusMonths(1);
+ final long when = generateDateTimeMillis(whenDate);
+
+ assertThat(DateTimeUtils.isSameDate(when, FAKE_TIME)).isFalse();
+ }
+
+ @Test
+ public void testIsSameDay_differentDay_false() throws Exception {
+ final LocalDate whenDate = FAKE_DATE.minusDays(2);
+ final long when = generateDateTimeMillis(whenDate);
+
+ assertThat(DateTimeUtils.isSameDate(when, FAKE_TIME)).isFalse();
+ }
+
+ @Test
+ public void testIsSameDay_true() throws Exception {
+ assertThat(DateTimeUtils.isSameDate(FAKE_TIME, FAKE_TIME)).isTrue();
+ }
+
+ private static long generateDateTimeMillis(LocalDate when) {
+ return when.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
+ }
+
+ private long generateDateTimeMillisAt(LocalDate when, int hour, int minute, int second) {
+ return ZonedDateTime.of(when.atTime(hour, minute, second), ZoneId.systemDefault())
+ .toInstant().toEpochMilli();
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/viewmodel/InstantTaskExecutorRule.java b/tests/src/com/android/providers/media/photopicker/viewmodel/InstantTaskExecutorRule.java
new file mode 100644
index 0000000..b35075e
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/viewmodel/InstantTaskExecutorRule.java
@@ -0,0 +1,59 @@
+/*
+ * 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.viewmodel;
+
+import androidx.arch.core.executor.ArchTaskExecutor;
+import androidx.arch.core.executor.TaskExecutor;
+
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+/**
+ * A JUnit Test Rule that swaps the background executor used by the Architecture Components with a
+ * different one which executes each task synchronously.
+ *
+ * We can't refer it in prebuilt androidX library.
+ * Copied it from androidx/arch/core/executor/testing/InstantTaskExecutorRule.java
+ */
+public class InstantTaskExecutorRule extends TestWatcher {
+ @Override
+ protected void starting(Description description) {
+ super.starting(description);
+ ArchTaskExecutor.getInstance().setDelegate(new TaskExecutor() {
+ @Override
+ public void executeOnDiskIO(Runnable runnable) {
+ runnable.run();
+ }
+
+ @Override
+ public void postToMainThread(Runnable runnable) {
+ runnable.run();
+ }
+
+ @Override
+ public boolean isMainThread() {
+ return true;
+ }
+ });
+ }
+
+ @Override
+ protected void finished(Description description) {
+ super.finished(description);
+ ArchTaskExecutor.getInstance().setDelegate(null);
+ }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/viewmodel/PickerViewModelTest.java b/tests/src/com/android/providers/media/photopicker/viewmodel/PickerViewModelTest.java
new file mode 100644
index 0000000..ec4afa1
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/viewmodel/PickerViewModelTest.java
@@ -0,0 +1,398 @@
+/*
+ * 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.viewmodel;
+
+import static android.provider.CloudMediaProviderContract.AlbumColumns;
+import static android.provider.CloudMediaProviderContract.MediaColumns;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.Application;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.provider.CloudMediaProviderContract;
+import android.text.format.DateUtils;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.LiveData;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.providers.media.photopicker.PickerSyncController;
+import com.android.providers.media.photopicker.data.ItemsProvider;
+import com.android.providers.media.photopicker.data.UserIdManager;
+import com.android.providers.media.photopicker.data.model.Category;
+import com.android.providers.media.photopicker.data.model.Item;
+import com.android.providers.media.photopicker.data.model.ItemTest;
+import com.android.providers.media.photopicker.data.model.UserId;
+import com.android.providers.media.util.ForegroundThread;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class PickerViewModelTest {
+
+ private static final String FAKE_IMAGE_MIME_TYPE = "image/jpg";
+ private static final String FAKE_CATEGORY_NAME = "testCategoryName";
+ private static final String FAKE_ID = "5";
+
+ private static final Category FAKE_CATEGORY =
+ new Category(FAKE_ID, PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY,
+ FAKE_CATEGORY_NAME, Uri.parse("content://media/foo"), 0, true);
+
+ @Rule
+ public InstantTaskExecutorRule instantTaskExecutorRule = new InstantTaskExecutorRule();
+
+ @Mock
+ private Application mApplication;
+
+ private PickerViewModel mPickerViewModel;
+ private TestItemsProvider mItemsProvider;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ final Context context = InstrumentationRegistry.getTargetContext();
+ when(mApplication.getApplicationContext()).thenReturn(context);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mPickerViewModel = new PickerViewModel(mApplication);
+ });
+ mItemsProvider = new TestItemsProvider(context);
+ mPickerViewModel.setItemsProvider(mItemsProvider);
+ UserIdManager userIdManager = mock(UserIdManager.class);
+ when(userIdManager.getCurrentUserProfileId()).thenReturn(UserId.CURRENT_USER);
+ mPickerViewModel.setUserIdManager(userIdManager);
+ }
+
+ @Test
+ public void testGetItems_noItems() throws Exception {
+ final int itemCount = 0;
+ mItemsProvider.setItems(generateFakeImageItemList(itemCount));
+ mPickerViewModel.updateItems();
+ // We use ForegroundThread to execute the loadItems in updateItems(), wait for the thread
+ // idle
+ ForegroundThread.waitForIdle();
+
+ final List<Item> itemList = mPickerViewModel.getItems().getValue();
+
+ // No date headers, the size should be 0
+ assertThat(itemList.size()).isEqualTo(itemCount);
+ }
+
+ @Test
+ public void testGetItems_hasRecentItem() throws Exception {
+ final int itemCount = 1;
+ final List<Item> fakeItemList = generateFakeImageItemList(itemCount);
+ final Item fakeItem = fakeItemList.get(0);
+ mItemsProvider.setItems(generateFakeImageItemList(itemCount));
+ mPickerViewModel.updateItems();
+ // We use ForegroundThread to execute the loadItems in updateItems(), wait for the thread
+ // idle
+ ForegroundThread.waitForIdle();
+
+ final List<Item> itemList = mPickerViewModel.getItems().getValue();
+
+ // Original one item + 1 Recent item
+ assertThat(itemList.size()).isEqualTo(itemCount + 1);
+ // Check the first item is recent item
+ final Item recentItem = itemList.get(0);
+ assertThat(recentItem.isDate()).isTrue();
+ assertThat(recentItem.getDateTaken()).isEqualTo(0);
+ // Check the second item is fakeItem
+ final Item firstPhotoItem = itemList.get(1);
+ assertThat(firstPhotoItem.getId()).isEqualTo(fakeItem.getId());
+ }
+
+ @Test
+ public void testGetItems_exceedMinCount_notSameDay_hasRecentItemAndOneDateItem()
+ throws Exception {
+ final int itemCount = 13;
+ mItemsProvider.setItems(generateFakeImageItemList(itemCount));
+ mPickerViewModel.updateItems();
+ // We use ForegroundThread to execute the loadItems in updateItems(), wait for the thread
+ // idle
+ ForegroundThread.waitForIdle();
+
+ final List<Item> itemList = mPickerViewModel.getItems().getValue();
+
+ // Original item count + 1 Recent item + 1 date item
+ assertThat(itemList.size()).isEqualTo(itemCount + 2);
+ assertThat(itemList.get(0).isDate()).isTrue();
+ assertThat(itemList.get(0).getDateTaken()).isEqualTo(0);
+ // The index 13 is the next date header because the minimum item count in recent section is
+ // 12
+ assertThat(itemList.get(13).isDate()).isTrue();
+ assertThat(itemList.get(13).getDateTaken()).isNotEqualTo(0);
+ }
+
+ /**
+ * Test that The total number in `Recent` may exceed the minimum count. If the photo items are
+ * taken on same day, they should not be split apart.
+ */
+ @Test
+ public void testGetItems_exceedMinCount_sameDay_hasRecentItemNoDateItem() throws Exception {
+ final int originalItemCount = 12;
+ final String lastItemId = "13";
+ final List<Item> fakeItemList = generateFakeImageItemList(originalItemCount);
+ final long dateTakenMs = fakeItemList.get(originalItemCount - 1).getDateTaken();
+ final long generationModified = 1L;
+ final Item lastItem = ItemTest.generateItem(lastItemId, FAKE_IMAGE_MIME_TYPE,
+ dateTakenMs, generationModified, /* duration= */ 1000L);
+ fakeItemList.add(lastItem);
+ final int itemCount = fakeItemList.size();
+ mItemsProvider.setItems(fakeItemList);
+ mPickerViewModel.updateItems();
+ // We use ForegroundThread to execute the loadItems in updateItems(), wait for the thread
+ // idle
+ ForegroundThread.waitForIdle();
+
+ final List<Item> itemList = mPickerViewModel.getItems().getValue();
+
+ // Original item count + 1 new Recent item
+ assertThat(itemList.size()).isEqualTo(itemCount + 1);
+ assertThat(itemList.get(0).isDate()).isTrue();
+ assertThat(itemList.get(0).getDateTaken()).isEqualTo(0);
+ }
+
+ @Test
+ public void testGetCategoryItems() throws Exception {
+ final int itemCount = 3;
+ final LiveData<List<Item>> categoryItems = mPickerViewModel.getCategoryItems(FAKE_CATEGORY);
+ mItemsProvider.setItems(generateFakeImageItemList(itemCount));
+ mPickerViewModel.updateCategoryItems();
+ // We use ForegroundThread to execute the loadItems in updateCategoryItems(), wait for the
+ // thread idle
+ ForegroundThread.waitForIdle();
+
+ final List<Item> itemList = categoryItems.getValue();
+
+ // Original item count + 3 date items
+ assertThat(itemList.size()).isEqualTo(itemCount + 3);
+ // Test the first item is date item
+ final Item firstDateItem = itemList.get(0);
+ assertThat(firstDateItem.isDate()).isTrue();
+ assertThat(firstDateItem.getDateTaken()).isNotEqualTo(0);
+ // Test the third item is date item and the dateTaken is larger than previous date item
+ final Item secondDateItem = itemList.get(2);
+ assertThat(secondDateItem.isDate()).isTrue();
+ assertThat(secondDateItem.getDateTaken()).isGreaterThan(firstDateItem.getDateTaken());
+ // Test the fifth item is date item and the dateTaken is larger than previous date item
+ final Item thirdDateItem = itemList.get(4);
+ assertThat(thirdDateItem.isDate()).isTrue();
+ assertThat(thirdDateItem.getDateTaken()).isGreaterThan(secondDateItem.getDateTaken());
+ }
+
+ @Test
+ public void testGetCategoryItems_dataIsUpdated() throws Exception {
+ final int itemCount = 3;
+ final LiveData<List<Item>> categoryItems = mPickerViewModel.getCategoryItems(FAKE_CATEGORY);
+ mItemsProvider.setItems(generateFakeImageItemList(itemCount));
+ mPickerViewModel.updateCategoryItems();
+ // We use ForegroundThread to execute the loadItems in updateCategoryItems(), wait for the
+ // thread idle
+ ForegroundThread.waitForIdle();
+
+ final List<Item> itemList = categoryItems.getValue();
+
+ // Original item count + 3 date items
+ assertThat(itemList.size()).isEqualTo(itemCount + 3);
+
+ final int updatedItemCount = 5;
+ mItemsProvider.setItems(generateFakeImageItemList(updatedItemCount));
+
+ // trigger updateCategoryItems and wait the idle
+ mPickerViewModel.updateCategoryItems();
+
+ // We use ForegroundThread to execute the loadItems in updateCategoryItems(), wait for the
+ // thread idle
+ ForegroundThread.waitForIdle();
+
+ // Get the result again to check the result is as expected
+ final List<Item> updatedItemList = categoryItems.getValue();
+
+ // Original item count + 5 date items
+ assertThat(updatedItemList.size()).isEqualTo(updatedItemCount + 5);
+ }
+
+ @Test
+ public void testGetCategories() throws Exception {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final int categoryCount = 2;
+ try (final Cursor fakeCursor = generateCursorForFakeCategories(categoryCount)) {
+ fakeCursor.moveToFirst();
+ final Category fakeFirstCategory = Category.fromCursor(fakeCursor, UserId.CURRENT_USER);
+ fakeCursor.moveToNext();
+ final Category fakeSecondCategory = Category.fromCursor(fakeCursor,
+ UserId.CURRENT_USER);
+ mItemsProvider.setCategoriesCursor(fakeCursor);
+ // move the cursor to original position
+ fakeCursor.moveToPosition(-1);
+ mPickerViewModel.updateCategories();
+ // We use ForegroundThread to execute the loadCategories in updateCategories(), wait for
+ // the thread idle
+ ForegroundThread.waitForIdle();
+
+ final List<Category> categoryList = mPickerViewModel.getCategories().getValue();
+
+ assertThat(categoryList.size()).isEqualTo(categoryCount);
+ // Verify the first category
+ final Category firstCategory = categoryList.get(0);
+ assertThat(firstCategory.getDisplayName(context)).isEqualTo(
+ fakeFirstCategory.getDisplayName(context));
+ assertThat(firstCategory.getItemCount()).isEqualTo(fakeFirstCategory.getItemCount());
+ assertThat(firstCategory.getCoverUri()).isEqualTo(fakeFirstCategory.getCoverUri());
+ // Verify the second category
+ final Category secondCategory = categoryList.get(1);
+ assertThat(secondCategory.getDisplayName(context)).isEqualTo(
+ fakeSecondCategory.getDisplayName(context));
+ assertThat(secondCategory.getItemCount()).isEqualTo(fakeSecondCategory.getItemCount());
+ assertThat(secondCategory.getCoverUri()).isEqualTo(fakeSecondCategory.getCoverUri());
+ }
+ }
+
+
+ private static Item generateFakeImageItem(String id) {
+ final long dateTakenMs = System.currentTimeMillis() + Long.parseLong(id)
+ * DateUtils.DAY_IN_MILLIS;
+ final long generationModified = 1L;
+
+ return ItemTest.generateItem(id, FAKE_IMAGE_MIME_TYPE, dateTakenMs, generationModified,
+ /* duration= */ 1000L);
+ }
+
+ private static List<Item> generateFakeImageItemList(int num) {
+ final List<Item> itemList = new ArrayList<>();
+ for (int i = 0; i < num; i++) {
+ itemList.add(generateFakeImageItem(String.valueOf(i)));
+ }
+ return itemList;
+ }
+
+ private static Cursor generateCursorForFakeCategories(int num) {
+ final MatrixCursor cursor = new MatrixCursor(AlbumColumns.ALL_PROJECTION);
+ final int itemCount = 5;
+ for (int i = 0; i < num; i++) {
+ cursor.addRow(new Object[]{
+ FAKE_ID + String.valueOf(i),
+ System.currentTimeMillis(),
+ FAKE_CATEGORY_NAME + i,
+ FAKE_ID + String.valueOf(i),
+ itemCount + i,
+ PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY
+ });
+ }
+ return cursor;
+ }
+
+ private static class TestItemsProvider extends ItemsProvider {
+
+ private List<Item> mItemList = new ArrayList<>();
+ private Cursor mCategoriesCursor;
+
+ public TestItemsProvider(Context context) {
+ super(context);
+ }
+
+ @Override
+ public Cursor getItems(Category category, int offset,
+ int limit, @Nullable String mimeType, @Nullable UserId userId) throws
+ IllegalArgumentException, IllegalStateException {
+ final MatrixCursor c = new MatrixCursor(MediaColumns.ALL_PROJECTION);
+
+ for (Item item : mItemList) {
+ c.addRow(new String[] {
+ item.getId(),
+ String.valueOf(item.getDateTaken()),
+ String.valueOf(item.getGenerationModified()),
+ item.getMimeType(),
+ String.valueOf(item.getSpecialFormat()),
+ "1", // size_bytes
+ null, // media_store_uri
+ String.valueOf(item.getDuration()),
+ "0", // is_favorite
+ "/storage/emulated/0/foo",
+ PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY
+ });
+ }
+
+ return c;
+ }
+
+ @Nullable
+ public Cursor getCategories(@Nullable String mimeType, @Nullable UserId userId) {
+ if (mCategoriesCursor != null) {
+ return mCategoriesCursor;
+ }
+
+ final MatrixCursor c = new MatrixCursor(AlbumColumns.ALL_PROJECTION);
+ return c;
+ }
+
+ public void setItems(@NonNull List<Item> itemList) {
+ mItemList = itemList;
+ }
+
+ public void setCategoriesCursor(@NonNull Cursor cursor) {
+ mCategoriesCursor = cursor;
+ }
+ }
+
+ @Test
+ public void testParseValuesFromIntent_noMimeType_defaultFalse() {
+ final Intent intent = new Intent();
+
+ mPickerViewModel.parseValuesFromIntent(intent);
+
+ assertThat(mPickerViewModel.hasMimeTypeFilter()).isFalse();
+ }
+
+ @Test
+ public void testParseValuesFromIntent_validMimeType() {
+ final Intent intent = new Intent();
+ intent.setType("image/png");
+
+ mPickerViewModel.parseValuesFromIntent(intent);
+
+ assertThat(mPickerViewModel.hasMimeTypeFilter()).isTrue();
+ }
+
+ @Test
+ public void testParseValuesFromIntent_ignoreInvalidMimeType() {
+ final Intent intent = new Intent();
+ intent.setType("audio/*");
+
+ mPickerViewModel.parseValuesFromIntent(intent);
+
+ assertThat(mPickerViewModel.hasMimeTypeFilter()).isFalse();
+ }
+}
diff --git a/tests/src/com/android/providers/media/scan/MediaScannerTest.java b/tests/src/com/android/providers/media/scan/MediaScannerTest.java
index 98cbdcc..f8c27e6 100644
--- a/tests/src/com/android/providers/media/scan/MediaScannerTest.java
+++ b/tests/src/com/android/providers/media/scan/MediaScannerTest.java
@@ -33,6 +33,7 @@
import android.os.Bundle;
import android.os.Environment;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.provider.BaseColumns;
import android.provider.DeviceConfig.OnPropertiesChangedListener;
import android.provider.MediaStore;
@@ -45,9 +46,13 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.providers.media.DatabaseHelper;
import com.android.providers.media.MediaDocumentsProvider;
import com.android.providers.media.MediaProvider;
+import com.android.providers.media.PickerUriResolver;
import com.android.providers.media.R;
+import com.android.providers.media.photopicker.PhotoPickerProvider;
+import com.android.providers.media.photopicker.PickerSyncController;
import com.android.providers.media.util.FileUtils;
import org.junit.Before;
@@ -71,14 +76,22 @@
private final MockContentResolver mResolver;
private final MediaProvider mProvider;
private final MediaDocumentsProvider mDocumentsProvider;
+ private final PhotoPickerProvider mPhotoPickerProvider;
+ private final UserHandle mUserHandle;
public IsolatedContext(Context base, String tag, boolean asFuseThread) {
+ this(base, tag, asFuseThread, base.getUser());
+ }
+
+ public IsolatedContext(Context base, String tag, boolean asFuseThread,
+ UserHandle userHandle) {
super(base);
mDir = new File(base.getFilesDir(), tag);
mDir.mkdirs();
FileUtils.deleteContents(mDir);
mResolver = new MockContentResolver(this);
+ mUserHandle = userHandle;
final ProviderInfo info = base.getPackageManager()
.resolveContentProvider(MediaStore.AUTHORITY, 0);
@@ -104,9 +117,19 @@
}
@Override
+ public int getIntDeviceConfig(String namespace, String key, int defaultValue) {
+ return 0;
+ }
+
+ @Override
public void addOnPropertiesChangedListener(OnPropertiesChangedListener listener) {
// Ignore
}
+
+ @Override
+ protected void updateNextRowIdXattr(DatabaseHelper helper, long id) {
+ // Ignoring this as test app would not have access to update xattr.
+ }
};
mProvider.attachInfo(this, info);
mResolver.addProvider(MediaStore.AUTHORITY, mProvider);
@@ -124,6 +147,14 @@
}
});
+ final ProviderInfo photoPickerProviderInfo = base.getPackageManager()
+ .resolveContentProvider(PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY,
+ 0);
+ mPhotoPickerProvider = new PhotoPickerProvider();
+ mPhotoPickerProvider.attachInfo(this, photoPickerProviderInfo);
+ mResolver.addProvider(PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY,
+ mPhotoPickerProvider);
+
MediaStore.waitForIdle(mResolver);
}
@@ -136,6 +167,15 @@
public ContentResolver getContentResolver() {
return mResolver;
}
+
+ @Override
+ public UserHandle getUser() {
+ return mUserHandle;
+ }
+
+ public void setPickerUriResolver(PickerUriResolver resolver) {
+ mProvider.setUriResolver(resolver);
+ }
}
private MediaScanner mLegacy;
diff --git a/tests/src/com/android/providers/media/scan/ModernMediaScannerTest.java b/tests/src/com/android/providers/media/scan/ModernMediaScannerTest.java
index ca53f65..0450c1c 100644
--- a/tests/src/com/android/providers/media/scan/ModernMediaScannerTest.java
+++ b/tests/src/com/android/providers/media/scan/ModernMediaScannerTest.java
@@ -38,6 +38,7 @@
import static com.android.providers.media.util.FileUtils.isFileHidden;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -384,7 +385,13 @@
private static void assertShouldScanPathAndIsPathHidden(boolean isScannable, boolean isHidden,
File dir) {
- assertEquals(Pair.create(isScannable, isHidden), shouldScanPathAndIsPathHidden(dir));
+ Pair<Boolean, Boolean> actual = shouldScanPathAndIsPathHidden(dir);
+ assertWithMessage("assert should scan for dir: " + dir.getAbsolutePath())
+ .that(actual.first)
+ .isEqualTo(isScannable);
+ assertWithMessage("assert is hidden for dir: " + dir.getAbsolutePath())
+ .that(actual.second)
+ .isEqualTo(isHidden);
}
@Test
@@ -421,12 +428,16 @@
final File nomediaFile = new File(dir, ".nomedia");
if (!nomediaFile.getParentFile().exists()) {
- assertTrue(nomediaFile.getParentFile().mkdirs());
+ assertWithMessage("cannot create dir: " + nomediaFile.getParentFile().getAbsolutePath())
+ .that(nomediaFile.getParentFile().mkdirs())
+ .isTrue();
}
try {
if (!nomediaFile.exists()) {
executeShellCommand("touch " + nomediaFile.getAbsolutePath());
- assertTrue(nomediaFile.exists());
+ assertWithMessage("cannot create nomedia file: " + nomediaFile.getAbsolutePath())
+ .that(nomediaFile.exists())
+ .isTrue();
}
assertShouldScanPathAndIsPathHidden(true, false, dir);
} finally {
@@ -435,12 +446,14 @@
}
/**
- * b/168830497: Test that default folders and Camera folder are always visible
+ * b/168830497: Test that root folder, default folders and Camera folder are always visible
*/
@Test
public void testVisibleDefaultFolders() throws Exception {
final File root = new File("storage/emulated/0");
+ assertVisibleFolder(root);
+
// Top level directories should always be visible
for (String dirName : FileUtils.DEFAULT_FOLDER_NAMES) {
final File defaultFolder = new File(root, dirName);
@@ -450,6 +463,35 @@
// DCIM/Camera should always be visible
final File cameraDir = new File(root, Environment.DIRECTORY_DCIM + "/" + "Camera");
assertVisibleFolder(cameraDir);
+
+ // Screenshots should always be visible
+ for (String dirName : FileUtils.DEFAULT_FOLDER_NAMES) {
+ File screenshotsDir = new File(root, dirName + "/" + Environment.DIRECTORY_SCREENSHOTS);
+ assertVisibleFolder(screenshotsDir);
+ }
+ }
+
+ /**
+ * b/192799231: Test that root folder which has .nomedia directory is always visible
+ */
+ @Test
+ public void testVisibleRootWithNoMediaDirectory() throws Exception {
+ final File root = new File("storage/emulated/0");
+ final File nomediaDir = new File(root, ".nomedia");
+ final File file = new File(nomediaDir, "test.jpg");
+
+ try {
+ if (!nomediaDir.exists()) {
+ executeShellCommand("mkdir -p " + nomediaDir.getAbsolutePath());
+ }
+ if (!file.exists()) {
+ executeShellCommand("touch " + file.getAbsolutePath());
+ assertTrue(file.exists());
+ }
+ assertShouldScanPathAndIsPathHidden(true, false, root);
+ } finally {
+ executeShellCommand("rm -rf " + nomediaDir.getAbsolutePath());
+ }
}
private static void assertShouldScanDirectory(File file) {
@@ -669,6 +711,7 @@
redNomedia.createNewFile();
mModern.scanDirectory(mDir, REASON_UNKNOWN);
assertQueryCount(1, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
+ assertThat(FileUtils.readString(redNomedia)).isEqualTo(Optional.of(redDir.getPath()));
// Unhide, rescan, and confirm visible again
redNomedia.delete();
diff --git a/tests/src/com/android/providers/media/util/BackgroundThreadTest.java b/tests/src/com/android/providers/media/util/BackgroundThreadTest.java
deleted file mode 100644
index 91514c0..0000000
--- a/tests/src/com/android/providers/media/util/BackgroundThreadTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2020 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.util;
-
-import static org.junit.Assert.assertNotNull;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class BackgroundThreadTest {
- @Test
- public void testSimple() {
- assertNotNull(BackgroundThread.get());
- assertNotNull(BackgroundThread.getExecutor());
- assertNotNull(BackgroundThread.getHandler());
- }
-}
diff --git a/tests/src/com/android/providers/media/util/FileUtilsTest.java b/tests/src/com/android/providers/media/util/FileUtilsTest.java
index 41e9b00..a1f2b82 100644
--- a/tests/src/com/android/providers/media/util/FileUtilsTest.java
+++ b/tests/src/com/android/providers/media/util/FileUtilsTest.java
@@ -46,10 +46,12 @@
import static com.android.providers.media.util.FileUtils.extractTopLevelDir;
import static com.android.providers.media.util.FileUtils.extractVolumeName;
import static com.android.providers.media.util.FileUtils.extractVolumePath;
+import static com.android.providers.media.util.FileUtils.fromFuseFile;
import static com.android.providers.media.util.FileUtils.isDataOrObbPath;
import static com.android.providers.media.util.FileUtils.isDataOrObbRelativePath;
import static com.android.providers.media.util.FileUtils.isExternalMediaDirectory;
import static com.android.providers.media.util.FileUtils.isObbOrChildRelativePath;
+import static com.android.providers.media.util.FileUtils.toFuseFile;
import static com.android.providers.media.util.FileUtils.translateModeAccessToPosix;
import static com.android.providers.media.util.FileUtils.translateModePfdToPosix;
import static com.android.providers.media.util.FileUtils.translateModePosixToPfd;
@@ -76,7 +78,6 @@
import androidx.test.runner.AndroidJUnit4;
import com.google.common.collect.Range;
-import com.google.common.truth.Truth;
import org.junit.After;
import org.junit.Assume;
@@ -542,15 +543,7 @@
extractRelativePath(prefix + "DCIM/foo.jpg"));
assertEquals("DCIM/My Vacation/",
extractRelativePath(prefix + "DCIM/My Vacation/foo.jpg"));
- assertEquals("Pictures/",
- extractRelativePath(prefix + "DCIM/../Pictures/.//foo.jpg"));
- assertEquals("/",
- extractRelativePath(prefix + "DCIM/Pictures/./..//..////foo.jpg"));
- assertEquals("Android/data/",
- extractRelativePath(prefix + "DCIM/foo.jpg/.//../../Android/data/poc"));
}
-
- assertEquals(null, extractRelativePath("/sdcard/\\\u0000"));
}
@Test
@@ -758,7 +751,7 @@
FileUtils.computeDateExpires(values);
final long target = (System.currentTimeMillis()
+ FileUtils.DEFAULT_DURATION_PENDING) / 1_000;
- Truth.assertThat(values.getAsLong(MediaColumns.DATE_EXPIRES))
+ assertThat(values.getAsLong(MediaColumns.DATE_EXPIRES))
.isIn(Range.closed(target - 5, target + 5));
}
@@ -782,7 +775,7 @@
FileUtils.computeDateExpires(values);
final long target = (System.currentTimeMillis()
+ FileUtils.DEFAULT_DURATION_TRASHED) / 1_000;
- Truth.assertThat(values.getAsLong(MediaColumns.DATE_EXPIRES))
+ assertThat(values.getAsLong(MediaColumns.DATE_EXPIRES))
.isIn(Range.closed(target - 5, target + 5));
}
@@ -813,7 +806,25 @@
File nomedia = new File(dirInDownload, ".nomedia");
assertTrue(nomedia.createNewFile());
- assertEquals(dirInDownload, FileUtils.getTopLevelNoMedia(new File(dirInDownload, "foo")));
+ assertThat(FileUtils.getTopLevelNoMedia(dirInDownload))
+ .isEqualTo(dirInDownload);
+ assertThat(FileUtils.getTopLevelNoMedia(new File(dirInDownload, "foo")))
+ .isEqualTo(dirInDownload);
+ }
+
+ @Test
+ public void testGetTopLevelNoMedia_CurrentNestedDir() throws Exception {
+ File topDirInDownload = getNewDirInDownload("testGetTopLevelNoMedia_CurrentNestedDir");
+
+ File dirInTopDirInDownload = new File(topDirInDownload, "foo");
+ assertTrue(dirInTopDirInDownload.mkdirs());
+ File nomedia = new File(dirInTopDirInDownload, ".nomedia");
+ assertTrue(nomedia.createNewFile());
+
+ assertThat(FileUtils.getTopLevelNoMedia(dirInTopDirInDownload))
+ .isEqualTo(dirInTopDirInDownload);
+ assertThat(FileUtils.getTopLevelNoMedia(new File(dirInTopDirInDownload, "foo")))
+ .isEqualTo(dirInTopDirInDownload);
}
@Test
@@ -827,8 +838,10 @@
File nomedia = new File(dirInTopDirInDownload, ".nomedia");
assertTrue(nomedia.createNewFile());
- assertEquals(topDirInDownload,
- FileUtils.getTopLevelNoMedia(new File(dirInTopDirInDownload, "foo")));
+ assertThat(FileUtils.getTopLevelNoMedia(dirInTopDirInDownload))
+ .isEqualTo(topDirInDownload);
+ assertThat(FileUtils.getTopLevelNoMedia(new File(dirInTopDirInDownload, "foo")))
+ .isEqualTo(topDirInDownload);
}
@Test
@@ -839,20 +852,87 @@
assertEquals(null,
FileUtils.getTopLevelNoMedia(new File(dirInTopDirInDownload, "foo")));
+ assertThat(FileUtils.getTopLevelNoMedia(dirInTopDirInDownload))
+ .isNull();
+ assertThat(FileUtils.getTopLevelNoMedia(new File(dirInTopDirInDownload, "foo")))
+ .isNull();
+ }
+
+ @Test
+ public void testShouldFileBeHidden() throws Exception {
+ File dir = getNewDirInDownload("testDirectory2");
+
+ // We don't create the files since shouldFileBeHidden needs to work even if the file has
+ // not been created yet.
+
+ File file = new File(dir, ".test-file");
+ assertThat(FileUtils.shouldFileBeHidden(file)).isTrue();
+
+ File hiddenFile = new File(dir, ".hidden-file");
+ assertThat(FileUtils.shouldFileBeHidden(hiddenFile)).isTrue();
+ }
+
+ @Test
+ public void testShouldFileBeHidden_hiddenParent() throws Exception {
+ File hiddenDirName = getNewDirInDownload(".testDirectory");
+
+ // We don't create the file since shouldFileBeHidden needs to work even if the file has
+ // not been created yet.
+
+ File fileInHiddenParent = new File(hiddenDirName, "testDirectory.txt");
+ assertThat(FileUtils.shouldFileBeHidden(fileInHiddenParent)).isTrue();
+ }
+
+ // Visibility of default dirs is tested in ModernMediaScannerTest#testVisibleDefaultFolders.
+ @Test
+ public void testShouldDirBeHidden() throws Exception {
+ final File root = new File("storage/emulated/0");
+ assertThat(FileUtils.shouldDirBeHidden(root)).isFalse();
+
+ // We don't create the dirs since shouldDirBeHidden needs to work even if the dir has
+ // not been created yet.
+
+ File visibleDir = new File(mTestDownloadDir, "testDirectory");
+ assertThat(FileUtils.shouldDirBeHidden(visibleDir)).isFalse();
+
+ File hiddenDir = new File(mTestDownloadDir, ".testDirectory");
+ assertThat(FileUtils.shouldDirBeHidden(hiddenDir)).isTrue();
+ }
+
+ @Test
+ public void testShouldDirBeHidden_hiddenParent() throws Exception {
+ File hiddenDirName = getNewDirInDownload(".testDirectory");
+
+ // We don't create the dirs since shouldDirBeHidden needs to work even if the dir has
+ // not been created yet.
+
+ File dirInHiddenParent = new File(hiddenDirName, "testDirectory");
+ assertThat(FileUtils.shouldDirBeHidden(dirInHiddenParent)).isTrue();
+ }
+
+ // Visibility of default dirs is tested in ModernMediaScannerTest#testVisibleDefaultFolders.
+ @Test
+ public void testIsDirectoryHidden() throws Exception {
+ File visibleDir = getNewDirInDownload("testDirectory");
+ assertThat(FileUtils.isDirectoryHidden(visibleDir)).isFalse();
+
+ File hiddenDirName = getNewDirInDownload(".testDirectory");
+ assertThat(FileUtils.isDirectoryHidden(hiddenDirName)).isTrue();
+
+ File hiddenDirNomedia = getNewDirInDownload("testDirectory2");
+ File nomedia = new File(hiddenDirNomedia, ".nomedia");
+ assertThat(nomedia.createNewFile()).isTrue();
+ assertThat(FileUtils.isDirectoryHidden(hiddenDirNomedia)).isTrue();
}
@Test
public void testDirectoryDirty() throws Exception {
File dirInDownload = getNewDirInDownload("testDirectoryDirty");
- // All directories are considered dirty, unless hidden
- assertTrue(FileUtils.isDirectoryDirty(dirInDownload));
+ // Directory without nomedia is not dirty
+ assertFalse(FileUtils.isDirectoryDirty(dirInDownload));
- // Marking a directory as clean has no effect without a .nomedia file
- FileUtils.setDirectoryDirty(dirInDownload, false);
- assertTrue(FileUtils.isDirectoryDirty(dirInDownload));
-
- // Creating an empty .nomedia file still keeps a directory dirty
+ // Creating an empty .nomedia file makes directory dirty
File nomedia = new File(dirInDownload, ".nomedia");
assertTrue(nomedia.createNewFile());
assertTrue(FileUtils.isDirectoryDirty(dirInDownload));
@@ -867,19 +947,49 @@
}
@Test
+ public void testDirectoryDirty_noMediaDirectory() throws Exception {
+ File dirInDownload = getNewDirInDownload("testDirectoryDirty");
+
+ // Directory without nomedia is clean
+ assertFalse(FileUtils.isDirectoryDirty(dirInDownload));
+
+ // Creating a .nomedia directory makes directory dirty
+ File nomedia = new File(dirInDownload, ".nomedia");
+ assertTrue(nomedia.mkdir());
+ assertTrue(FileUtils.isDirectoryDirty(dirInDownload));
+
+ // Marking as clean with a .nomedia directory has no effect
+ FileUtils.setDirectoryDirty(dirInDownload, false);
+ assertTrue(FileUtils.isDirectoryDirty(dirInDownload));
+ }
+
+ @Test
+ public void testDirectoryDirty_nullDir() throws Exception {
+ assertThat(FileUtils.isDirectoryDirty(null)).isFalse();
+ }
+
+ @Test
public void testExtractPathOwnerPackageName() {
assertThat(extractPathOwnerPackageName("/storage/emulated/0/Android/data/foo"))
.isEqualTo("foo");
+ assertThat(extractPathOwnerPackageName("/storage/emulated/0/android/data/foo"))
+ .isEqualTo("foo");
assertThat(extractPathOwnerPackageName("/storage/emulated/0/Android/obb/foo"))
.isEqualTo("foo");
+ assertThat(extractPathOwnerPackageName("/storage/emulated/0/android/obb/foo"))
+ .isEqualTo("foo");
assertThat(extractPathOwnerPackageName("/storage/emulated/0/Android/media/foo"))
.isEqualTo("foo");
+ assertThat(extractPathOwnerPackageName("/storage/emulated/0/android/media/foo"))
+ .isEqualTo("foo");
assertThat(extractPathOwnerPackageName("/storage/ABCD-1234/Android/data/foo"))
.isEqualTo("foo");
assertThat(extractPathOwnerPackageName("/storage/ABCD-1234/Android/obb/foo"))
.isEqualTo("foo");
assertThat(extractPathOwnerPackageName("/storage/ABCD-1234/Android/media/foo"))
.isEqualTo("foo");
+ assertThat(extractPathOwnerPackageName("/storage/ABCD-1234/android/media/foo"))
+ .isEqualTo("foo");
assertThat(extractPathOwnerPackageName("/storage/emulated/0/Android/data")).isNull();
assertThat(extractPathOwnerPackageName("/storage/emulated/0/Android/obb")).isNull();
@@ -1015,22 +1125,89 @@
final String result = FileUtils.extractDisplayName(data);
// after adding the prefix .pending-timestamp or .trashed-timestamp,
// the largest length of the file name is MAX_FILENAME_BYTES 255
- Truth.assertThat(result.length()).isAtMost(MAX_FILENAME_BYTES);
- Truth.assertThat(result).isNotEqualTo(originalName);
+ assertThat(result.length()).isAtMost(MAX_FILENAME_BYTES);
+ assertThat(result).isNotEqualTo(originalName);
}
@Test
public void testIsExternalMediaDirectory() throws Exception {
for (String prefix : new String[] {
- "/storage/emulated/0/AppClone/",
- "/storage/0000-0000/AppClone/"
+ "/storage/emulated/0/",
+ "/storage/0000-0000/",
}) {
+ assertTrue(isExternalMediaDirectory(prefix + "Android/media/foo.jpg", null));
+ assertTrue(isExternalMediaDirectory(prefix + "Android/media/foo.jpg", ""));
+ assertTrue(isExternalMediaDirectory(prefix + "Android/mEdia/foo.jpg", ""));
+ assertFalse(isExternalMediaDirectory(prefix + "Android/data/foo.jpg", ""));
assertTrue(isExternalMediaDirectory(prefix + "Android/media/foo.jpg", "AppClone"));
- assertFalse(isExternalMediaDirectory(prefix + "Android/media/foo.jpg", "NotAppClone"));
+ assertTrue(isExternalMediaDirectory(prefix + "android/mEdia/foo.jpg", "AppClone"));
+ assertTrue(isExternalMediaDirectory(prefix + "AppClone/Android/media/foo.jpg", "AppClone"));
+ assertTrue(isExternalMediaDirectory(prefix + "AppClone/Android/mEdia/foo.jpg", "AppClone"));
+ assertTrue(isExternalMediaDirectory(prefix + "Appclone/Android/mEdia/foo.jpg", "AppClone"));
+ assertFalse(isExternalMediaDirectory(prefix + "AppClone/Android/media/foo.jpg", null));
+ assertFalse(isExternalMediaDirectory(prefix + "AppClone/Android/mEdia/foo.jpg", null));
+ assertFalse(isExternalMediaDirectory(prefix + "AppClone/Android/media/foo.jpg", ""));
+ assertFalse(isExternalMediaDirectory(prefix + "AppClone/Android/media/foo.jpg", "NotAppClone"));
}
}
@Test
+ public void testToAndFromFuseFile() throws Exception {
+ final File fuseFilePrimary = new File("/mnt/user/0/emulated/0/foo");
+ final File fuseFileSecondary = new File("/mnt/user/0/0000-0000/foo");
+
+ final File lowerFsFilePrimary = new File("/storage/emulated/0/foo");
+ final File lowerFsFileSecondary = new File("/storage/0000-0000/foo");
+
+ final File unexpectedFile = new File("/mnt/pass_through/0/emulated/0/foo");
+
+ assertThat(fromFuseFile(fuseFilePrimary)).isEqualTo(lowerFsFilePrimary);
+ assertThat(fromFuseFile(fuseFileSecondary)).isEqualTo(lowerFsFileSecondary);
+ assertThat(fromFuseFile(lowerFsFilePrimary)).isEqualTo(lowerFsFilePrimary);
+
+ assertThat(toFuseFile(lowerFsFilePrimary)).isEqualTo(fuseFilePrimary);
+ assertThat(toFuseFile(lowerFsFileSecondary)).isEqualTo(fuseFileSecondary);
+ assertThat(toFuseFile(fuseFilePrimary)).isEqualTo(fuseFilePrimary);
+
+ assertThat(toFuseFile(unexpectedFile)).isEqualTo(unexpectedFile);
+ assertThat(fromFuseFile(unexpectedFile)).isEqualTo(unexpectedFile);
+ }
+
+ @Test
+ public void testComputeValuesFromData() {
+ final ContentValues values = new ContentValues();
+ values.put(MediaColumns.DATA, "/storage/emulated/0/Pictures/foo.jpg");
+
+ FileUtils.computeValuesFromData(values, false);
+
+ assertEquals("external_primary", values.getAsString(MediaColumns.VOLUME_NAME));
+ assertEquals("Pictures/", values.getAsString(MediaColumns.RELATIVE_PATH));
+ assertEquals(0, (int) values.getAsInteger(MediaColumns.IS_TRASHED));
+ assertTrue(values.containsKey(MediaColumns.DATE_EXPIRES));
+ assertNull(values.get(MediaColumns.DATE_EXPIRES));
+ assertEquals("foo.jpg", values.getAsString(MediaColumns.DISPLAY_NAME));
+ assertTrue(values.containsKey(MediaColumns.BUCKET_DISPLAY_NAME));
+ assertEquals("Pictures", values.get(MediaColumns.BUCKET_DISPLAY_NAME));
+ }
+
+ @Test
+ public void testComputeValuesFromData_withTopLevelFile() {
+ final ContentValues values = new ContentValues();
+ values.put(MediaColumns.DATA, "/storage/emulated/0/foo.jpg");
+
+ FileUtils.computeValuesFromData(values, false);
+
+ assertEquals("external_primary", values.getAsString(MediaColumns.VOLUME_NAME));
+ assertEquals("/", values.getAsString(MediaColumns.RELATIVE_PATH));
+ assertEquals(0, (int) values.getAsInteger(MediaColumns.IS_TRASHED));
+ assertTrue(values.containsKey(MediaColumns.DATE_EXPIRES));
+ assertNull(values.get(MediaColumns.DATE_EXPIRES));
+ assertEquals("foo.jpg", values.getAsString(MediaColumns.DISPLAY_NAME));
+ 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");
diff --git a/tests/src/com/android/providers/media/util/MimeUtilsTest.java b/tests/src/com/android/providers/media/util/MimeUtilsTest.java
index b57b5c5..9491a98 100644
--- a/tests/src/com/android/providers/media/util/MimeUtilsTest.java
+++ b/tests/src/com/android/providers/media/util/MimeUtilsTest.java
@@ -16,9 +16,6 @@
package com.android.providers.media.util;
-import static com.android.providers.media.util.MimeUtils.equalIgnoreCase;
-import static com.android.providers.media.util.MimeUtils.startsWithIgnoreCase;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -42,30 +39,6 @@
}
@Test
- public void testEqualIgnoreCase() throws Exception {
- assertTrue(equalIgnoreCase("image/jpg", "image/jpg"));
- assertTrue(equalIgnoreCase("image/jpg", "Image/Jpg"));
-
- assertFalse(equalIgnoreCase("image/jpg", "image/png"));
- assertFalse(equalIgnoreCase("image/jpg", null));
- assertFalse(equalIgnoreCase(null, "image/jpg"));
- assertFalse(equalIgnoreCase(null, null));
- }
-
- @Test
- public void testStartsWithIgnoreCase() throws Exception {
- assertTrue(startsWithIgnoreCase("image/jpg", "image/"));
- assertTrue(startsWithIgnoreCase("Image/Jpg", "image/"));
-
- assertFalse(startsWithIgnoreCase("image/", "image/jpg"));
-
- assertFalse(startsWithIgnoreCase("image/jpg", "audio/"));
- assertFalse(startsWithIgnoreCase("image/jpg", null));
- assertFalse(startsWithIgnoreCase(null, "audio/"));
- assertFalse(startsWithIgnoreCase(null, null));
- }
-
- @Test
public void testResolveMimeType() throws Exception {
assertEquals("image/jpeg",
MimeUtils.resolveMimeType(new File("foo.jpg")));
diff --git a/tests/src/com/android/providers/media/util/PermissionUtilsTest.java b/tests/src/com/android/providers/media/util/PermissionUtilsTest.java
index c434bd2..45e9316 100644
--- a/tests/src/com/android/providers/media/util/PermissionUtilsTest.java
+++ b/tests/src/com/android/providers/media/util/PermissionUtilsTest.java
@@ -51,11 +51,8 @@
import static com.android.providers.media.util.PermissionUtils.checkPermissionWriteStorage;
import static com.android.providers.media.util.PermissionUtils.checkPermissionWriteVideo;
import static com.android.providers.media.util.PermissionUtils.checkWriteImagesOrVideoAppOps;
-import static com.android.providers.media.util.TestUtils.QUERY_TYPE;
-import static com.android.providers.media.util.TestUtils.RUN_INFINITE_ACTIVITY;
import static com.android.providers.media.util.TestUtils.adoptShellPermission;
import static com.android.providers.media.util.TestUtils.dropShellPermission;
-import static com.android.providers.media.util.TestUtils.getPid;
import static com.google.common.truth.Truth.assertThat;
@@ -63,26 +60,29 @@
import android.app.AppOpsManager;
import android.content.Context;
-import android.content.Intent;
import androidx.test.filters.SdkSuppress;
import androidx.test.runner.AndroidJUnit4;
import com.android.cts.install.lib.TestApp;
+import com.android.modules.utils.build.SdkLevel;
-import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.HashMap;
-import java.util.Map;
-
@RunWith(AndroidJUnit4.class)
public class PermissionUtilsTest {
private static final TestApp TEST_APP_WITH_STORAGE_PERMS = new TestApp(
"TestAppWithStoragePerms",
"com.android.providers.media.testapp.withstorageperms", 1, false,
"MediaProviderTestAppWithStoragePerms.apk");
+ private static final TestApp TEST_APP_WITH_MEDIA_PERMS =
+ new TestApp(
+ "TestAppWithMediaPerms",
+ "com.android.providers.media.testapp.withmediaperms",
+ 1,
+ false,
+ "MediaProviderTestAppWithMediaPerms.apk");
private static final TestApp TEST_APP_WITHOUT_PERMS = new TestApp("TestAppWithoutPerms",
"com.android.providers.media.testapp.withoutperms", 1, false,
"MediaProviderTestAppWithoutPerms.apk");
@@ -120,11 +120,11 @@
assertThat(checkPermissionReadStorage(context, pid, uid, packageName, null)).isTrue();
assertThat(checkPermissionWriteStorage(context, pid, uid, packageName, null)).isTrue();
- assertThat(checkPermissionReadAudio(context, pid, uid, packageName, null)).isTrue();
+ assertThat(checkPermissionReadAudio(context, pid, uid, packageName, null, false)).isTrue();
assertThat(checkPermissionWriteAudio(context, pid, uid, packageName, null)).isFalse();
- assertThat(checkPermissionReadVideo(context, pid, uid, packageName, null)).isTrue();
+ assertThat(checkPermissionReadVideo(context, pid, uid, packageName, null, false)).isTrue();
assertThat(checkPermissionWriteVideo(context, pid, uid, packageName, null)).isFalse();
- assertThat(checkPermissionReadImages(context, pid, uid, packageName, null)).isTrue();
+ assertThat(checkPermissionReadImages(context, pid, uid, packageName, null, false)).isTrue();
assertThat(checkPermissionWriteImages(context, pid, uid, packageName, null)).isFalse();
assertThat(checkPermissionInstallPackages(context, pid, uid, packageName, null)).isFalse();
}
@@ -142,6 +142,9 @@
@Test
public void testDefaultPermissionsOnTestAppWithStoragePerms() throws Exception {
+ if (!SdkLevel.isAtLeastT()) {
+ return;
+ }
String packageName = TEST_APP_WITH_STORAGE_PERMS.getPackageName();
int testAppUid = getContext().getPackageManager().getPackageUid(packageName, 0);
adoptShellPermission(UPDATE_APP_OPS_STATS);
@@ -159,9 +162,49 @@
checkPermissionAccessMtp(getContext(), TEST_APP_PID, testAppUid, packageName,
null)).isFalse();
assertThat(
- checkPermissionWriteStorage(getContext(), TEST_APP_PID, testAppUid, packageName,
+ checkPermissionWriteStorage(getContext(), TEST_APP_PID, testAppUid,
+ packageName, null)).isTrue();
+ assertThat(
+ checkPermissionReadStorage(getContext(), TEST_APP_PID, testAppUid, packageName,
null)).isTrue();
- checkReadPermissions(TEST_APP_PID, testAppUid, packageName, true);
+ assertMediaReadPermissions(TEST_APP_PID, testAppUid, packageName,
+ false /* targetSdkIsAtLeastT */, true /* expected */);
+ // APPs with W_E_S can also read media.
+ assertMediaReadPermissions(TEST_APP_PID, testAppUid, packageName,
+ true /* targetSdkIsAtLeastT */, true /* expected */);
+
+ } finally {
+ dropShellPermission();
+ }
+ }
+
+ @Test
+ public void testDefaultPermissionsOnTestAppWithMediaPerms() throws Exception {
+ if (!SdkLevel.isAtLeastT()) {
+ return;
+ }
+ String packageName = TEST_APP_WITH_MEDIA_PERMS.getPackageName();
+ int testAppUid = getContext().getPackageManager().getPackageUid(packageName, 0);
+ adoptShellPermission(UPDATE_APP_OPS_STATS);
+
+ try {
+ assertThat(checkPermissionSelf(getContext(), TEST_APP_PID, testAppUid)).isFalse();
+ assertThat(checkPermissionShell(getContext(), TEST_APP_PID, testAppUid)).isFalse();
+ assertThat(checkIsLegacyStorageGranted(getContext(), testAppUid, packageName, null))
+ .isFalse();
+ assertThat(checkPermissionInstallPackages(
+ getContext(), TEST_APP_PID, testAppUid, packageName, null)).isFalse();
+ assertThat(checkPermissionAccessMtp(
+ getContext(), TEST_APP_PID, testAppUid, packageName, null)).isFalse();
+ assertThat(checkPermissionWriteStorage(
+ getContext(), TEST_APP_PID, testAppUid, packageName, null)).isFalse();
+ assertThat(checkPermissionReadStorage(
+ getContext(), TEST_APP_PID, testAppUid, packageName, null)).isFalse();
+ assertMediaReadPermissions(TEST_APP_PID, testAppUid, packageName,
+ true /* targetSdkIsAtLeastT */, true /* expected */);
+ assertMediaReadPermissions(TEST_APP_PID, testAppUid, packageName,
+ false /* targetSdkIsAtLeastT */, false /* expected */);
+
} finally {
dropShellPermission();
}
@@ -193,15 +236,20 @@
null)).isFalse();
assertThat(
checkPermissionInstallPackages(getContext(), TEST_APP_PID, testAppUid,
- packageName,
- null)).isFalse();
+ packageName, null)).isFalse();
assertThat(
checkPermissionAccessMtp(getContext(), TEST_APP_PID, testAppUid, packageName,
null)).isFalse();
assertThat(
checkPermissionWriteStorage(getContext(), TEST_APP_PID, testAppUid, packageName,
null)).isFalse();
- checkReadPermissions(TEST_APP_PID, testAppUid, packageName, false);
+ assertThat(
+ checkPermissionReadStorage( getContext(), TEST_APP_PID, testAppUid, packageName,
+ null)).isFalse();
+ assertMediaReadPermissions(TEST_APP_PID, testAppUid, packageName,
+ false /* targetSdkIsAtLeastT */, false /* expected */);
+ assertMediaReadPermissions(TEST_APP_PID, testAppUid, packageName,
+ true /* targetSdkIsAtLeastT */, false /* expected */);
} finally {
dropShellPermission();
}
@@ -238,9 +286,12 @@
checkPermissionAccessMtp(getContext(), TEST_APP_PID, testAppUid, packageName,
null)).isFalse();
assertThat(
- checkPermissionWriteStorage(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isFalse();
- checkReadPermissions(TEST_APP_PID, testAppUid, packageName, true);
+ checkPermissionWriteStorage(getContext(), TEST_APP_PID, testAppUid,
+ packageName, null)).isFalse();
+ assertThat(checkPermissionReadStorage(getContext(), TEST_APP_PID, testAppUid,
+ packageName, null)).isTrue();
+ assertMediaReadPermissions(TEST_APP_PID, testAppUid, packageName,
+ false /* targetSdkIsAtLeastT */, true /* expected */);
} finally {
dropShellPermission();
}
@@ -346,26 +397,32 @@
}
@Test
- public void testReadVideoOnTestApp() throws Exception {
- final String packageName = TEST_APP_WITH_STORAGE_PERMS.getPackageName();
- int testAppUid = getContext().getPackageManager().getPackageUid(
- packageName, 0);
+ public void testReadVideoOnTestAppWithStoragePerms() throws Exception {
+ assertReadVideoOnTestApp(TEST_APP_WITH_STORAGE_PERMS);
+ }
+
+ @Test
+ public void testReadVideoOnTestAppWithMediaPerms() throws Exception {
+ if (!SdkLevel.isAtLeastT()) {
+ return;
+ }
+ assertReadVideoOnTestApp(TEST_APP_WITH_MEDIA_PERMS);
+ }
+
+ private static void assertReadVideoOnTestApp(TestApp app) throws Exception {
+ boolean isAtLeastT = (app == TEST_APP_WITH_MEDIA_PERMS) ? true : false;
+ final String packageName = app.getPackageName();
+ int testAppUid = getContext().getPackageManager().getPackageUid(packageName, 0);
adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
-
try {
- assertThat(
- checkPermissionReadVideo(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isTrue();
-
+ assertThat(checkPermissionReadVideo(getContext(), TEST_APP_PID, testAppUid,
+ packageName, null, isAtLeastT)).isTrue();
modifyAppOp(testAppUid, OPSTR_READ_MEDIA_VIDEO, AppOpsManager.MODE_ERRORED);
- assertThat(
- checkPermissionReadVideo(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isFalse();
-
+ assertThat(checkPermissionReadVideo(getContext(), TEST_APP_PID, testAppUid,
+ packageName, null, isAtLeastT)).isFalse();
modifyAppOp(testAppUid, OPSTR_READ_MEDIA_VIDEO, AppOpsManager.MODE_ALLOWED);
- assertThat(
- checkPermissionReadVideo(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isTrue();
+ assertThat(checkPermissionReadVideo(getContext(), TEST_APP_PID, testAppUid,
+ packageName, null, isAtLeastT)).isTrue();
} finally {
dropShellPermission();
}
@@ -398,51 +455,68 @@
}
@Test
- public void testReadAudioOnTestApp() throws Exception {
- final String packageName = TEST_APP_WITH_STORAGE_PERMS.getPackageName();
- int testAppUid = getContext().getPackageManager().getPackageUid(
- packageName, 0);
- adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
+ public void testReadAudioOnTestAppWithStoragePerms() throws Exception {
+ assertReadAudioOnTestApp(TEST_APP_WITH_STORAGE_PERMS);
+ }
+ @Test
+ public void testReadAudioOnTestAppWithMediaPerms() throws Exception {
+ if (!SdkLevel.isAtLeastT()) {
+ return;
+ }
+ assertReadAudioOnTestApp(TEST_APP_WITH_MEDIA_PERMS);
+ }
+
+ private static void assertReadAudioOnTestApp(TestApp app) throws Exception {
+ boolean isAtLeastT = (app == TEST_APP_WITH_MEDIA_PERMS) ? true : false;
+ final String packageName = app.getPackageName();
+ int testAppUid = getContext().getPackageManager().getPackageUid(packageName, 0);
+ adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
try {
- assertThat(
- checkPermissionReadAudio(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isTrue();
+ assertThat(checkPermissionReadAudio(getContext(), TEST_APP_PID, testAppUid,
+ packageName, null, isAtLeastT)).isTrue();
modifyAppOp(testAppUid, OPSTR_READ_MEDIA_AUDIO, AppOpsManager.MODE_ERRORED);
- assertThat(
- checkPermissionReadAudio(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isFalse();
+ assertThat(checkPermissionReadAudio(getContext(), TEST_APP_PID, testAppUid,
+ packageName, null, isAtLeastT)).isFalse();
modifyAppOp(testAppUid, OPSTR_READ_MEDIA_AUDIO, AppOpsManager.MODE_ALLOWED);
- assertThat(
- checkPermissionReadAudio(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isTrue();
+ assertThat(checkPermissionReadAudio(getContext(), TEST_APP_PID, testAppUid,
+ packageName, null, isAtLeastT)).isTrue();
} finally {
dropShellPermission();
}
}
@Test
- public void testReadImagesOnTestApp() throws Exception {
- final String packageName = TEST_APP_WITH_STORAGE_PERMS.getPackageName();
+ public void testReadImagesOnTestAppWithStoragePerms() throws Exception {
+ assertReadImagesOnTestApp(TEST_APP_WITH_STORAGE_PERMS);
+ }
+
+ @Test
+ public void testReadImagesOnTestAppWithMediaPerms() throws Exception {
+ if (!SdkLevel.isAtLeastT()) {
+ return;
+ }
+ assertReadImagesOnTestApp(TEST_APP_WITH_MEDIA_PERMS);
+ }
+
+ private static void assertReadImagesOnTestApp(TestApp app) throws Exception {
+ boolean isAtLeastT = (app == TEST_APP_WITH_MEDIA_PERMS) ? true : false;
+ final String packageName = app.getPackageName();
int testAppUid = getContext().getPackageManager().getPackageUid(packageName, 0);
adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
-
try {
- assertThat(
- checkPermissionReadImages(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isTrue();
+ assertThat(checkPermissionReadImages(getContext(), TEST_APP_PID, testAppUid,
+ packageName, null, isAtLeastT)).isTrue();
modifyAppOp(testAppUid, OPSTR_READ_MEDIA_IMAGES, AppOpsManager.MODE_ERRORED);
- assertThat(
- checkPermissionReadImages(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isFalse();
+ assertThat(checkPermissionReadImages(getContext(), TEST_APP_PID, testAppUid,
+ packageName, null, isAtLeastT)).isFalse();
modifyAppOp(testAppUid, OPSTR_READ_MEDIA_IMAGES, AppOpsManager.MODE_ALLOWED);
- assertThat(
- checkPermissionReadImages(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isTrue();
+ assertThat(checkPermissionReadImages(getContext(), TEST_APP_PID, testAppUid,
+ packageName, null, isAtLeastT)).isTrue();
} finally {
dropShellPermission();
}
@@ -491,15 +565,19 @@
.isFalse();
}
- static private void checkReadPermissions(int pid, int uid, String packageName,
- boolean expected) {
- assertEquals(expected,
- checkPermissionReadStorage(getContext(), pid, uid, packageName, null));
- assertEquals(expected,
- checkPermissionReadAudio(getContext(), pid, uid, packageName, null));
- assertEquals(expected,
- checkPermissionReadImages(getContext(), pid, uid, packageName, null));
- assertEquals(expected,
- checkPermissionReadVideo(getContext(), pid, uid, packageName, null));
+ private static void assertMediaReadPermissions(
+ int pid, int uid, String packageName, boolean targetSdkIsAtLeastT, boolean expected) {
+ assertEquals(
+ expected,
+ checkPermissionReadAudio(
+ getContext(), pid, uid, packageName, null, targetSdkIsAtLeastT));
+ assertEquals(
+ expected,
+ checkPermissionReadImages(
+ getContext(), pid, uid, packageName, null, targetSdkIsAtLeastT));
+ assertEquals(
+ expected,
+ checkPermissionReadVideo(
+ getContext(), pid, uid, packageName, null, targetSdkIsAtLeastT));
}
}
diff --git a/tests/src/com/android/providers/media/util/SpecialFormatDetectorTest.java b/tests/src/com/android/providers/media/util/SpecialFormatDetectorTest.java
new file mode 100644
index 0000000..e90c3c1
--- /dev/null
+++ b/tests/src/com/android/providers/media/util/SpecialFormatDetectorTest.java
@@ -0,0 +1,187 @@
+/*
+ * 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.util;
+
+import static com.android.providers.media.scan.MediaScannerTest.stage;
+import static com.google.common.truth.Truth.assertThat;
+
+import android.Manifest;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Environment;
+import android.provider.MediaStore;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.providers.media.R;
+import com.android.providers.media.scan.MediaScannerTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+@RunWith(AndroidJUnit4.class)
+public class SpecialFormatDetectorTest {
+ private static final String TAG = "SpecialFormatDetectorTest";
+ private ContentResolver mIsolatedResolver;
+
+ @Before
+ public void setUp() throws Exception {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .adoptShellPermissionIdentity(android.Manifest.permission.LOG_COMPAT_CHANGE,
+ android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
+ android.Manifest.permission.READ_DEVICE_CONFIG,
+ Manifest.permission.INTERACT_ACROSS_USERS);
+
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final Context isolatedContext
+ = new MediaScannerTest.IsolatedContext(context, "modern", /*asFuseThread*/ false);
+ mIsolatedResolver = isolatedContext.getContentResolver();
+ }
+
+ @Test
+ public void testDetect_gif() throws Exception {
+ final File dir = Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOWNLOADS);
+ final File file = stage(R.raw.test_gif, new File(dir, TAG + System.nanoTime() + ".jpg"));
+
+ final Uri uri = MediaStore.scanFile(mIsolatedResolver, file);
+ assertThat(uri).isNotNull();
+
+ final Uri filesUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL,
+ ContentUris.parseId(uri));
+
+ try (Cursor cr = mIsolatedResolver.query(filesUri,
+ new String[]{MediaStore.Files.FileColumns.MIME_TYPE,
+ MediaStore.Files.FileColumns._SPECIAL_FORMAT}, null, null, null)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertThat(cr.getString(0)).isEqualTo("image/jpeg");
+ assertThat(cr.getInt(1)).isEqualTo(
+ MediaStore.Files.FileColumns._SPECIAL_FORMAT_GIF);
+ }
+
+ file.delete();
+ }
+
+ @Test
+ public void testDetect_motionPhoto() throws Exception {
+ final File dir = Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOWNLOADS);
+ final File file = stage(R.raw.test_motion_photo, new File(dir, TAG + System.nanoTime() +
+ ".jpg"));
+
+ final Uri uri = MediaStore.scanFile(mIsolatedResolver, file);
+ assertThat(uri).isNotNull();
+
+ final Uri filesUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL,
+ ContentUris.parseId(uri));
+
+ try (Cursor cr = mIsolatedResolver.query(filesUri,
+ new String[]{MediaStore.Files.FileColumns.MIME_TYPE,
+ MediaStore.Files.FileColumns._SPECIAL_FORMAT}, null, null, null)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertThat(cr.getString(0)).isEqualTo("image/jpeg");
+ assertThat(cr.getInt(1)).isEqualTo(
+ MediaStore.Files.FileColumns._SPECIAL_FORMAT_MOTION_PHOTO);
+ }
+
+ file.delete();
+ }
+
+ @Test
+ public void testDetect_animatedWebp() throws Exception {
+ final File dir = Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOWNLOADS);
+ final File file = stage(R.raw.test_animated_webp, new File(dir, TAG + System.nanoTime() +
+ ".webp"));
+
+ final Uri uri = MediaStore.scanFile(mIsolatedResolver, file);
+ assertThat(uri).isNotNull();
+
+ final Uri filesUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL,
+ ContentUris.parseId(uri));
+
+ try (Cursor cr = mIsolatedResolver.query(filesUri,
+ new String[]{MediaStore.Files.FileColumns.MIME_TYPE,
+ MediaStore.Files.FileColumns._SPECIAL_FORMAT}, null, null, null)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertThat(cr.getString(0)).isEqualTo("image/webp");
+ assertThat(cr.getInt(1)).isEqualTo(
+ MediaStore.Files.FileColumns._SPECIAL_FORMAT_ANIMATED_WEBP);
+ }
+
+ file.delete();
+ }
+
+ @Test
+ public void testDetect_nonAnimatedWebp() throws Exception {
+ final File dir = Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOWNLOADS);
+ final File file = stage(R.raw.test_non_animated_webp, new File(dir, TAG + System.nanoTime()
+ + ".webp"));
+
+ final Uri uri = MediaStore.scanFile(mIsolatedResolver, file);
+ assertThat(uri).isNotNull();
+
+ final Uri filesUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL,
+ ContentUris.parseId(uri));
+
+ try (Cursor cr = mIsolatedResolver.query(filesUri,
+ new String[]{MediaStore.Files.FileColumns.MIME_TYPE,
+ MediaStore.Files.FileColumns._SPECIAL_FORMAT}, null, null, null)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertThat(cr.getString(0)).isEqualTo("image/webp");
+ assertThat(cr.getInt(1)).isEqualTo(MediaStore.Files.FileColumns._SPECIAL_FORMAT_NONE);
+ }
+
+ file.delete();
+ }
+
+ @Test
+ public void testDetect_notSpecialFormat() throws Exception {
+ final File dir = Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOWNLOADS);
+ final File file = stage(R.raw.test_image, new File(dir, TAG + System.nanoTime() + ".jpg"));
+
+ final Uri uri = MediaStore.scanFile(mIsolatedResolver, file);
+ assertThat(uri).isNotNull();
+
+ final Uri filesUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL,
+ ContentUris.parseId(uri));
+
+ try (Cursor cr = mIsolatedResolver.query(filesUri,
+ new String[]{MediaStore.Files.FileColumns.MIME_TYPE,
+ MediaStore.Files.FileColumns._SPECIAL_FORMAT}, null, null, null)) {
+ assertThat(cr.getCount()).isEqualTo(1);
+ cr.moveToFirst();
+ assertThat(cr.getString(0)).isEqualTo("image/jpeg");
+ assertThat(cr.getInt(1)).isEqualTo(MediaStore.Files.FileColumns._SPECIAL_FORMAT_NONE);
+ }
+
+ file.delete();
+ }
+}
diff --git a/tests/src/com/android/providers/media/util/StringUtilsTest.java b/tests/src/com/android/providers/media/util/StringUtilsTest.java
new file mode 100644
index 0000000..c4556c4
--- /dev/null
+++ b/tests/src/com/android/providers/media/util/StringUtilsTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.util;
+
+import static com.android.providers.media.util.StringUtils.equalIgnoreCase;
+import static com.android.providers.media.util.StringUtils.startsWithIgnoreCase;
+import static com.android.providers.media.util.StringUtils.verifySupportedUncachedRelativePaths;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class StringUtilsTest {
+ @Test
+ public void testEqualIgnoreCase() throws Exception {
+ assertTrue(equalIgnoreCase("image/jpg", "image/jpg"));
+ assertTrue(equalIgnoreCase("image/jpg", "Image/Jpg"));
+
+ assertFalse(equalIgnoreCase("image/jpg", "image/png"));
+ assertFalse(equalIgnoreCase("image/jpg", null));
+ assertFalse(equalIgnoreCase(null, "image/jpg"));
+ assertFalse(equalIgnoreCase(null, null));
+ }
+
+ @Test
+ public void testStartsWithIgnoreCase() throws Exception {
+ assertTrue(startsWithIgnoreCase("image/jpg", "image/"));
+ assertTrue(startsWithIgnoreCase("Image/Jpg", "image/"));
+
+ assertFalse(startsWithIgnoreCase("image/", "image/jpg"));
+
+ assertFalse(startsWithIgnoreCase("image/jpg", "audio/"));
+ assertFalse(startsWithIgnoreCase("image/jpg", null));
+ assertFalse(startsWithIgnoreCase(null, "audio/"));
+ assertFalse(startsWithIgnoreCase(null, null));
+ }
+
+ @Test public void testVerifySupportedUncachedRelativePaths() throws Exception {
+ assertEquals(
+ new ArrayList<String>(Arrays.asList("path/", "path/path/")),
+ verifySupportedUncachedRelativePaths(
+ new ArrayList<String>(
+ Arrays.asList(null, "", "/",
+ "path", "/path", "path/", "/path/",
+ "path/path", "/path/path", "path/path/"))));
+ }
+}
diff --git a/tests/src/com/android/providers/media/util/SyntheticPathUtilsTest.java b/tests/src/com/android/providers/media/util/SyntheticPathUtilsTest.java
new file mode 100644
index 0000000..b913114
--- /dev/null
+++ b/tests/src/com/android/providers/media/util/SyntheticPathUtilsTest.java
@@ -0,0 +1,138 @@
+/*
+ * 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.util;
+
+import static com.android.providers.media.util.SyntheticPathUtils.extractSyntheticRelativePathSegements;
+import static com.android.providers.media.util.SyntheticPathUtils.getPickerRelativePath;
+import static com.android.providers.media.util.SyntheticPathUtils.getRedactedRelativePath;
+import static com.android.providers.media.util.SyntheticPathUtils.getSyntheticRelativePath;
+import static com.android.providers.media.util.SyntheticPathUtils.isPickerPath;
+import static com.android.providers.media.util.SyntheticPathUtils.isRedactedPath;
+import static com.android.providers.media.util.SyntheticPathUtils.isSyntheticPath;
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SyntheticPathUtilsTest {
+ // Needs to match the redacted ids specification in SyntheticPathUtilsTest#REDACTED_URI_ID_SIZE
+ private static final String REDACTED_ID = "ruidaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+
+ @Test
+ public void testGetSyntheticRelativePath() throws Exception {
+ assertThat(getSyntheticRelativePath()).isEqualTo(".transforms/synthetic");
+ }
+
+ @Test
+ public void testGetRedactedRelativePath() throws Exception {
+ assertThat(getRedactedRelativePath()).isEqualTo(".transforms/synthetic/redacted");
+ }
+
+ @Test
+ public void testGetPickerRelativePath() throws Exception {
+ assertThat(getPickerRelativePath()).isEqualTo(".transforms/synthetic/picker");
+ }
+
+ @Test
+ public void testIsSyntheticPath() throws Exception {
+ assertThat(isSyntheticPath("/storage/emulated/0/.transforms/synthetic", /* userId */ 0))
+ .isTrue();
+ assertThat(isSyntheticPath("/storage/emulated/10/.transforms/synthetic", /* userId */ 10))
+ .isTrue();
+ assertThat(isSyntheticPath("/storage/emulated/0/.transforms/SYNTHETIC/",/* userId */ 0))
+ .isTrue();
+
+ assertThat(isSyntheticPath("/storage/emulated/0/.transforms/synthetic", /* userId */ 10))
+ .isFalse();
+ assertThat(isSyntheticPath("/storage/emulated/10/.transforms/synthetic", /* userId */ 0))
+ .isFalse();
+ assertThat(isSyntheticPath("/storage/emulated/0/.transforms", /* userId */ 0)).isFalse();
+ assertThat(isSyntheticPath("/storage/emulated/0/synthetic", /* userId */ 0)).isFalse();
+ }
+
+ @Test
+ public void testIsRedactedPath() throws Exception {
+ assertThat(isRedactedPath("/storage/emulated/0/.transforms/synthetic/redacted/"
+ + REDACTED_ID, /* userId */ 0)).isTrue();
+ assertThat(isRedactedPath("/storage/emulated/10/.transforms/synthetic/redacted/"
+ + REDACTED_ID, /* userId */ 10)).isTrue();
+ assertThat(isRedactedPath("/storage/emulated/0/.transforms/synthetic/REDACTED/"
+ + REDACTED_ID, /* userId */ 0)).isTrue();
+
+ assertThat(isRedactedPath("/storage/emulated/0/.transforms/synthetic/redacted/"
+ + REDACTED_ID, /* userId */ 10)).isFalse();
+ assertThat(isRedactedPath("/storage/emulated/10/.transforms/synthetic/redacted/"
+ + REDACTED_ID, /* userId */ 0)).isFalse();
+ assertThat(isRedactedPath("/storage/emulated/0/.transforms/synthetic/picker/"
+ + REDACTED_ID, /* userId */ 0)).isFalse();
+ assertThat(isRedactedPath("/storage/emulated/0/.transforms/redacted/" + REDACTED_ID,
+ /* userId */ 0)).isFalse();
+ assertThat(isRedactedPath("/storage/emulated/0/synthetic/redacted/" + REDACTED_ID,
+ /* userId */ 0)).isFalse();
+ }
+
+ @Test
+ public void testIsPickerPath() throws Exception {
+ assertThat(isPickerPath("/storage/emulated/0/.transforms/synthetic/picker/foo",
+ /* userId */ 0)).isTrue();
+ assertThat(isPickerPath("/storage/emulated/10/.transforms/synthetic/picker/foo",
+ /* userId */ 10)).isTrue();
+ assertThat(isPickerPath("/storage/emulated/0/.transforms/synthetic/PICKER/bar/baz",
+ /* userId */ 0)).isTrue();
+
+ assertThat(isPickerPath("/storage/emulated/0/.transforms/synthetic/picker/foo",
+ /* userId */ 10)).isFalse();
+ assertThat(isPickerPath("/storage/emulated/10/.transforms/synthetic/picker/foo",
+ /* userId */ 0)).isFalse();
+ assertThat(isPickerPath("/storage/emulated/0/.transforms/synthetic/redacted/foo",
+ /* userId */ 0)).isFalse();
+ assertThat(isPickerPath("/storage/emulated/0/.transforms/picker/foo", /* userId */ 0))
+ .isFalse();
+ assertThat(isPickerPath("/storage/emulated/0/synthetic/picker/foo", /* userId */ 0))
+ .isFalse();
+ }
+
+ @Test
+ public void testExtractSyntheticRelativePathSegments() throws Exception {
+ assertThat(extractSyntheticRelativePathSegements(
+ "/storage/emulated/10/.transforms/synthetic/picker",
+ /* userId */ 0)).isEmpty();
+ assertThat(extractSyntheticRelativePathSegements(
+ "/storage/emulated/0/.transforms/synthetic",
+ /* userId */ 0)).isEmpty();
+
+ assertThat(extractSyntheticRelativePathSegements(
+ "/storage/emulated/0/.transforms/synthetic/picker",
+ /* userId */ 0)).containsExactly("picker").inOrder();
+ assertThat(extractSyntheticRelativePathSegements(
+ "/storage/emulated/0/.transforms/synthetic/picker/",
+ /* userId */ 0)).containsExactly("picker").inOrder();
+
+ assertThat(extractSyntheticRelativePathSegements(
+ "/storage/emulated/0/.transforms/synthetic/picker/foo",
+ /* userId */ 0)).containsExactly("picker", "foo").inOrder();
+ assertThat(extractSyntheticRelativePathSegements(
+ "/storage/emulated/0/.transforms/synthetic/picker//foo/",
+ /* userId */ 0)).containsExactly("picker", "foo").inOrder();
+
+ assertThat(extractSyntheticRelativePathSegements(
+ "/storage/emulated/0/.transforms/synthetic/picker/foo/com.bar",
+ /* userId */ 0)).containsExactly("picker", "foo", "com.bar").inOrder();
+ }
+}
diff --git a/tests/test_app/TestAppForPermissionActivity33.xml b/tests/test_app/TestAppForPermissionActivity33.xml
new file mode 100644
index 0000000..4e8e74b
--- /dev/null
+++ b/tests/test_app/TestAppForPermissionActivity33.xml
@@ -0,0 +1,42 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.providers.media.testapp.permissionmedia"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="33" />
+
+ <uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION"/>
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.MANAGE_MEDIA"/>
+ <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
+ <uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
+ <uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/>
+
+ <application android:label="TestAppPerms33">
+ <activity android:name="com.android.providers.media.util.TestAppActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
\ No newline at end of file
diff --git a/tests/test_app/TestAppWithMediaPerms.xml b/tests/test_app/TestAppWithMediaPerms.xml
new file mode 100644
index 0000000..5671919
--- /dev/null
+++ b/tests/test_app/TestAppWithMediaPerms.xml
@@ -0,0 +1,38 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.providers.media.testapp.withmediaperms"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="32" />
+ <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
+ <uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
+ <uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/>
+
+ <application android:label="TestAppWithMediaPerms">
+ <activity android:name="com.android.providers.media.util.TestAppActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/transcode/src/com/android/providers/media/transcode/TranscodeTest.java b/tests/transcode/src/com/android/providers/media/transcode/TranscodeTest.java
deleted file mode 100644
index a77fd55..0000000
--- a/tests/transcode/src/com/android/providers/media/transcode/TranscodeTest.java
+++ /dev/null
@@ -1,898 +0,0 @@
-/*
- * Copyright (C) 2020 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.transcode;
-
-import static androidx.test.InstrumentationRegistry.getContext;
-
-import static com.android.providers.media.transcode.TranscodeTestUtils.assertFileContent;
-import static com.android.providers.media.transcode.TranscodeTestUtils.assertTranscode;
-import static com.android.providers.media.transcode.TranscodeTestUtils.installAppWithStoragePermissions;
-import static com.android.providers.media.transcode.TranscodeTestUtils.open;
-import static com.android.providers.media.transcode.TranscodeTestUtils.openFileAs;
-import static com.android.providers.media.transcode.TranscodeTestUtils.uninstallApp;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.Manifest;
-import android.media.ApplicationMediaCapabilities;
-import android.media.MediaFormat;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.ParcelFileDescriptor;
-import android.os.SystemProperties;
-import android.provider.MediaStore;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.cts.install.lib.TestApp;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Random;
-
-import org.junit.After;
-import org.junit.Assume;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class TranscodeTest {
- private static final File EXTERNAL_STORAGE_DIRECTORY
- = Environment.getExternalStorageDirectory();
- private static final File DIR_CAMERA
- = new File(EXTERNAL_STORAGE_DIRECTORY, Environment.DIRECTORY_DCIM + "/Camera");
- // TODO(b/169546642): Test other directories like /sdcard and /sdcard/foo
- // These are the only transcode unsupported directories we can stage files in given our
- // test app permissions
- private static final File[] DIRS_NO_TRANSCODE = {
- new File(EXTERNAL_STORAGE_DIRECTORY, Environment.DIRECTORY_PICTURES),
- new File(EXTERNAL_STORAGE_DIRECTORY, Environment.DIRECTORY_MOVIES),
- new File(EXTERNAL_STORAGE_DIRECTORY, Environment.DIRECTORY_DOWNLOADS),
- new File(EXTERNAL_STORAGE_DIRECTORY, Environment.DIRECTORY_DCIM),
- new File(EXTERNAL_STORAGE_DIRECTORY, Environment.DIRECTORY_DOCUMENTS),
- };
-
- static final String NONCE = String.valueOf(System.nanoTime());
- private static final String HEVC_FILE_NAME = "TranscodeTestHEVC_" + NONCE + ".mp4";
- private static final String SMALL_HEVC_FILE_NAME = "TranscodeTestHevcSmall_" + NONCE + ".mp4";
- private static final String LEGACY_FILE_NAME = "TranscodeTestLegacy_" + NONCE + ".mp4";
-
- private static final TestApp TEST_APP_HEVC = new TestApp("TestAppHevc",
- "com.android.providers.media.transcode.testapp", 1, false,
- "TranscodeTestAppSupportsHevc.apk");
-
- private static final TestApp TEST_APP_SLOW_MOTION = new TestApp("TestAppSlowMotion",
- "com.android.providers.media.transcode.testapp", 1, false,
- "TranscodeTestAppSupportsSlowMotion.apk");
-
- @Before
- public void setUp() throws Exception {
- Assume.assumeTrue(SystemProperties.getBoolean("sys.fuse.transcode_enabled", false));
-
- TranscodeTestUtils.pollForExternalStorageState();
- TranscodeTestUtils.grantPermission(getContext().getPackageName(),
- Manifest.permission.READ_EXTERNAL_STORAGE);
- TranscodeTestUtils.pollForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, true);
- TranscodeTestUtils.enableSeamlessTranscoding();
- TranscodeTestUtils.disableTranscodingForAllPackages();
- }
-
- @After
- public void tearDown() throws Exception {
- TranscodeTestUtils.disableSeamlessTranscoding();
- }
-
- /**
- * Tests that we return FD of transcoded file for legacy apps
- * @throws Exception
- */
- @Test
- public void testTranscoded_FilePath() throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- try {
- TranscodeTestUtils.stageHEVCVideoFile(modernFile);
-
- ParcelFileDescriptor pfdOriginal = open(modernFile, false);
-
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
- ParcelFileDescriptor pfdTranscoded = open(modernFile, false);
-
- assertFileContent(modernFile, modernFile, pfdOriginal, pfdTranscoded, false);
- } finally {
- modernFile.delete();
- }
- }
-
- /**
- * Tests that we don't transcode files outside DCIM/Camera
- * @throws Exception
- */
- @Test
- public void testNoTranscodeOutsideCamera_FilePath() throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- List<File> noTranscodeFiles = new ArrayList<>();
- for (File file : DIRS_NO_TRANSCODE) {
- noTranscodeFiles.add(new File(file, HEVC_FILE_NAME));
- }
- noTranscodeFiles.add(new File(getContext().getExternalFilesDir(null), HEVC_FILE_NAME));
-
- try {
- TranscodeTestUtils.stageHEVCVideoFile(modernFile);
- for (File file : noTranscodeFiles) {
- TranscodeTestUtils.stageHEVCVideoFile(file);
- }
- ParcelFileDescriptor pfdOriginal1 = open(modernFile, false);
-
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
-
- for (File file : noTranscodeFiles) {
- pfdOriginal1.seekTo(0);
- ParcelFileDescriptor pfdOriginal2 = open(file, false);
- assertFileContent(modernFile, file, pfdOriginal1, pfdOriginal2, true);
- }
- } finally {
- modernFile.delete();
- for (File file : noTranscodeFiles) {
- file.delete();
- }
- }
- }
-
- /**
- * Tests that same transcoded file is used for multiple open() from same app
- * @throws Exception
- */
- @Test
- public void testSameTranscoded_FilePath() throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- try {
- TranscodeTestUtils.stageHEVCVideoFile(modernFile);
-
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
- ParcelFileDescriptor pfdTranscoded1 = open(modernFile, false);
- ParcelFileDescriptor pfdTranscoded2 = open(modernFile, false);
-
- assertFileContent(modernFile, modernFile, pfdTranscoded1, pfdTranscoded2, true);
- } finally {
- modernFile.delete();
- }
- }
-
- /**
- * Tests that we return FD of transcoded file for legacy apps
- * @throws Exception
- */
- @Test
- public void testTranscoded_ContentResolver() throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- try {
- Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
-
- ParcelFileDescriptor pfdOriginal = open(uri, false, null /* bundle */);
-
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
-
- ParcelFileDescriptor pfdTranscoded = open(uri, false, null /* bundle */);
-
- assertFileContent(modernFile, modernFile, pfdOriginal, pfdTranscoded, false);
- } finally {
- modernFile.delete();
- }
- }
-
- /**
- * Tests that we don't transcode files outside DCIM/Camera
- * @throws Exception
- */
- @Test
- public void testNoTranscodeOutsideCamera_ConentResolver() throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- List<File> noTranscodeFiles = new ArrayList<>();
- for (File file : DIRS_NO_TRANSCODE) {
- noTranscodeFiles.add(new File(file, HEVC_FILE_NAME));
- }
-
- try {
- Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
- ArrayList<Uri> noTranscodeUris = new ArrayList<>();
- for (File file : noTranscodeFiles) {
- noTranscodeUris.add(TranscodeTestUtils.stageHEVCVideoFile(file));
- }
-
- ParcelFileDescriptor pfdOriginal1 = open(uri, false, null /* bundle */);
-
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
-
- for (int i = 0; i < noTranscodeUris.size(); i++) {
- pfdOriginal1.seekTo(0);
- ParcelFileDescriptor pfdOriginal2 =
- open(noTranscodeUris.get(i), false, null /* bundle */);
- assertFileContent(modernFile, noTranscodeFiles.get(1), pfdOriginal1, pfdOriginal2,
- true);
- }
- } finally {
- modernFile.delete();
- for (File file : noTranscodeFiles) {
- file.delete();
- }
- }
- }
-
- /**
- * Tests that same transcoded file is used for multiple open() from same app
- * @throws Exception
- */
- @Test
- public void testSameTranscodedFile_ContentResolver() throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- try {
- Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
-
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
-
- ParcelFileDescriptor pfdTranscoded1 = open(uri, false, null /* bundle */);
- ParcelFileDescriptor pfdTranscoded2 = open(uri, false, null /* bundle */);
-
- assertFileContent(modernFile, modernFile, pfdTranscoded1, pfdTranscoded2, true);
- } finally {
- modernFile.delete();
- }
- }
-
- /**
- * Tests that deletes are visible across legacy and modern apps
- * @throws Exception
- */
- @Test
- public void testDeleteTranscodedFile_FilePath() throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- try {
- TranscodeTestUtils.stageHEVCVideoFile(modernFile);
-
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
-
- assertTrue(modernFile.delete());
- assertFalse(modernFile.exists());
-
- TranscodeTestUtils.disableTranscodingForAllPackages();
-
- assertFalse(modernFile.exists());
- } finally {
- modernFile.delete();
- }
- }
-
- /**
- * Tests that renames are visible across legacy and modern apps
- * @throws Exception
- */
- @Test
- public void testRenameTranscodedFile_FilePath() throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- File destFile = new File(DIR_CAMERA, "renamed_" + HEVC_FILE_NAME);
- try {
- TranscodeTestUtils.stageHEVCVideoFile(modernFile);
-
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
-
- assertTrue(modernFile.renameTo(destFile));
- assertTrue(destFile.exists());
- assertFalse(modernFile.exists());
-
- TranscodeTestUtils.disableTranscodingForAllPackages();
-
- assertTrue(destFile.exists());
- assertFalse(modernFile.exists());
- } finally {
- modernFile.delete();
- destFile.delete();
- }
- }
-
- /**
- * Tests that transcode doesn't start until read(2)
- * @throws Exception
- */
- @Test
- public void testLazyTranscodedFile_FilePath() throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- try {
- TranscodeTestUtils.stageHEVCVideoFile(modernFile);
-
- assertTranscode(modernFile, false);
-
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
-
- assertTranscode(modernFile, true);
- } finally {
- modernFile.delete();
- }
- }
-
- /**
- * Tests that transcode cache is reused after file path transcode
- * @throws Exception
- */
- @Test
- public void testTranscodedCacheReuse_FilePath() throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- try {
- TranscodeTestUtils.stageHEVCVideoFile(modernFile);
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
-
- assertTranscode(modernFile, true);
- assertTranscode(modernFile, false);
- } finally {
- modernFile.delete();
- }
- }
-
- /**
- * Tests that transcode cache is reused after ContentResolver transcode
- * @throws Exception
- */
- @Test
- public void testTranscodedCacheReuse_ContentResolver() throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- try {
- Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
-
- assertTranscode(uri, true);
- assertTranscode(uri, false);
- } finally {
- modernFile.delete();
- }
- }
-
- /**
- * Tests that transcode cache is reused after ContentResolver transcode
- * and file path opens
- * @throws Exception
- */
- @Test
- public void testTranscodedCacheReuse_ContentResolverFilePath() throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- try {
- Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
-
- assertTranscode(uri, true);
- assertTranscode(modernFile, false);
- } finally {
- modernFile.delete();
- }
- }
-
- /**
- * Tests that transcode cache is reused after file path transcode
- * and ContentResolver opens
- * @throws Exception
- */
- @Test
- public void testTranscodedCacheReuse_FilePathContentResolver() throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- try {
- Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
-
- assertTranscode(modernFile, true);
- assertTranscode(uri, false);
- } finally {
- modernFile.delete();
- }
- }
-
- /**
- * Tests that transcode cache is reused after rename
- * @throws Exception
- */
- @Test
- public void testTranscodedCacheReuseAfterRename_FilePath() throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- File destFile = new File(DIR_CAMERA, "renamed_" + HEVC_FILE_NAME);
- try {
- TranscodeTestUtils.stageHEVCVideoFile(modernFile);
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
-
- assertTranscode(modernFile, true);
-
- assertTrue(modernFile.renameTo(destFile));
-
- assertTranscode(destFile, false);
- } finally {
- modernFile.delete();
- destFile.delete();
- }
- }
-
- @Test
- public void testExtraAcceptOriginalFormatTrue_ContentResolver() throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- try {
- Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
-
- ParcelFileDescriptor pfdOriginal1 = open(uri, false, null /* bundle */);
-
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
-
- Bundle bundle = new Bundle();
- bundle.putBoolean(MediaStore.EXTRA_ACCEPT_ORIGINAL_MEDIA_FORMAT, true);
- ParcelFileDescriptor pfdOriginal2 = open(uri, false, bundle);
-
- assertFileContent(modernFile, modernFile, pfdOriginal1, pfdOriginal2, true);
- } finally {
- modernFile.delete();
- }
- }
-
- @Test
- public void testExtraAcceptOriginalFormatFalse_ContentResolver() throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- try {
- Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
-
- ParcelFileDescriptor pfdOriginal = open(uri, false, null /* bundle */);
-
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
-
- Bundle bundle = new Bundle();
- bundle.putBoolean(MediaStore.EXTRA_ACCEPT_ORIGINAL_MEDIA_FORMAT, false);
- ParcelFileDescriptor pfdTranscoded = open(uri, false, bundle);
-
- assertFileContent(modernFile, modernFile, pfdOriginal, pfdTranscoded, false);
- } finally {
- modernFile.delete();
- }
- }
-
- @Test
- public void testExtraMediaCapabilitiesHevcSupportedTrue_ContentResolver() throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- try {
- Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
-
- ParcelFileDescriptor pfdOriginal1 = open(uri, false, null /* bundle */);
-
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
-
- Bundle bundle = new Bundle();
- ApplicationMediaCapabilities capabilities =
- new ApplicationMediaCapabilities.Builder()
- .addSupportedVideoMimeType(MediaFormat.MIMETYPE_VIDEO_HEVC).build();
- bundle.putParcelable(MediaStore.EXTRA_MEDIA_CAPABILITIES, capabilities);
- ParcelFileDescriptor pfdOriginal2 = open(uri, false, bundle);
-
- assertFileContent(modernFile, modernFile, pfdOriginal1, pfdOriginal2, true);
- } finally {
- modernFile.delete();
- }
- }
-
- @Test
- public void testExtraMediaCapabilitiesHevcUnsupportedFalse_ContentResolver() throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- try {
- Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
-
- ParcelFileDescriptor pfdOriginal1 = open(uri, false, null /* bundle */);
-
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
-
- Bundle bundle = new Bundle();
- ApplicationMediaCapabilities capabilities =
- new ApplicationMediaCapabilities.Builder()
- .addUnsupportedVideoMimeType(MediaFormat.MIMETYPE_VIDEO_HEVC).build();
- bundle.putParcelable(MediaStore.EXTRA_MEDIA_CAPABILITIES, capabilities);
- ParcelFileDescriptor pfdOriginal2 = open(uri, false, bundle);
-
- assertFileContent(modernFile, modernFile, pfdOriginal1, pfdOriginal2, false);
- } finally {
- modernFile.delete();
- }
- }
-
- @Test
- public void testExtraMediaCapabilitiesHevcUnspecifiedFalse_ContentResolver() throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- try {
- Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
-
- ParcelFileDescriptor pfdOriginal1 = open(uri, false, null /* bundle */);
-
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
-
- Bundle bundle = new Bundle();
- ApplicationMediaCapabilities capabilities =
- new ApplicationMediaCapabilities.Builder().build();
- bundle.putParcelable(MediaStore.EXTRA_MEDIA_CAPABILITIES, capabilities);
- ParcelFileDescriptor pfdTranscoded = open(uri, false, bundle);
-
- assertFileContent(modernFile, modernFile, pfdOriginal1, pfdTranscoded, false);
- } finally {
- modernFile.delete();
- }
- }
-
- @Test
- public void testExtraAcceptOriginalTrueAndMediaCapabilitiesHevcFalse_ContentResolver()
- throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- try {
- Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
-
- ParcelFileDescriptor pfdOriginal1 = open(uri, false, null /* bundle */);
-
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
-
- Bundle bundle = new Bundle();
- ApplicationMediaCapabilities capabilities =
- new ApplicationMediaCapabilities.Builder().build();
- bundle.putParcelable(MediaStore.EXTRA_MEDIA_CAPABILITIES, capabilities);
- bundle.putBoolean(MediaStore.EXTRA_ACCEPT_ORIGINAL_MEDIA_FORMAT, true);
- ParcelFileDescriptor pfdOriginal2 = open(uri, false, bundle);
-
- assertFileContent(modernFile, modernFile, pfdOriginal1, pfdOriginal2, true);
- } finally {
- modernFile.delete();
- }
- }
-
- @Test
- public void testMediaCapabilitiesManifestHevc()
- throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- ParcelFileDescriptor pfdOriginal2 = null;
- try {
- installAppWithStoragePermissions(TEST_APP_HEVC);
-
- Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
-
- ParcelFileDescriptor pfdOriginal1 = open(modernFile, false);
-
- TranscodeTestUtils.enableTranscodingForPackage(TEST_APP_HEVC.getPackageName());
-
- pfdOriginal2 = openFileAs(TEST_APP_HEVC, modernFile);
-
- assertFileContent(modernFile, modernFile, pfdOriginal1, pfdOriginal2, true);
- } finally {
- // Explicitly close PFD otherwise instrumention might crash when test_app is uninstalled
- if (pfdOriginal2 != null) {
- pfdOriginal2.close();
- }
- modernFile.delete();
- uninstallApp(TEST_APP_HEVC);
- }
- }
-
- @Test
- public void testMediaCapabilitiesManifestSlowMotion()
- throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- ParcelFileDescriptor pfdOriginal2 = null;
- try {
- installAppWithStoragePermissions(TEST_APP_SLOW_MOTION);
-
- Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
-
- ParcelFileDescriptor pfdOriginal1 = open(modernFile, false);
-
- TranscodeTestUtils.enableTranscodingForPackage(TEST_APP_SLOW_MOTION.getPackageName());
-
- pfdOriginal2 = openFileAs(TEST_APP_SLOW_MOTION, modernFile);
-
- assertFileContent(modernFile, modernFile, pfdOriginal1, pfdOriginal2, false);
- } finally {
- // Explicitly close PFD otherwise instrumention might crash when test_app is uninstalled
- if (pfdOriginal2 != null) {
- pfdOriginal2.close();
- }
- modernFile.delete();
- uninstallApp(TEST_APP_HEVC);
- }
- }
-
- @Test
- public void testAppCompatNoTranscodeHevc() throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- String packageName = TEST_APP_SLOW_MOTION.getPackageName();
- ParcelFileDescriptor pfdOriginal2 = null;
- try {
- installAppWithStoragePermissions(TEST_APP_SLOW_MOTION);
-
- Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
-
- ParcelFileDescriptor pfdOriginal1 = open(modernFile, false);
-
- TranscodeTestUtils.enableTranscodingForPackage(packageName);
- // App compat takes precedence
- TranscodeTestUtils.forceEnableAppCompatHevc(packageName);
-
- Thread.sleep(2000);
-
- pfdOriginal2 = openFileAs(TEST_APP_SLOW_MOTION, modernFile);
-
- assertFileContent(modernFile, modernFile, pfdOriginal1, pfdOriginal2, true);
- } finally {
- // Explicitly close PFD otherwise instrumention might crash when test_app is uninstalled
- if (pfdOriginal2 != null) {
- pfdOriginal2.close();
- }
- modernFile.delete();
- TranscodeTestUtils.resetAppCompat(packageName);
- uninstallApp(TEST_APP_HEVC);
- }
- }
-
- @Test
- public void testAppCompatTranscodeHevc() throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- String packageName = TEST_APP_SLOW_MOTION.getPackageName();
- ParcelFileDescriptor pfdOriginal2 = null;
- try {
- installAppWithStoragePermissions(TEST_APP_SLOW_MOTION);
-
- Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
-
- ParcelFileDescriptor pfdOriginal1 = open(modernFile, false);
-
- // Transcoding is disabled but app compat enables it (disables hevc support)
- TranscodeTestUtils.forceDisableAppCompatHevc(packageName);
-
- pfdOriginal2 = openFileAs(TEST_APP_SLOW_MOTION, modernFile);
-
- assertFileContent(modernFile, modernFile, pfdOriginal1, pfdOriginal2, false);
- } finally {
- // Explicitly close PFD otherwise instrumention might crash when test_app is uninstalled
- if (pfdOriginal2 != null) {
- pfdOriginal2.close();
- }
- modernFile.delete();
- TranscodeTestUtils.resetAppCompat(packageName);
- uninstallApp(TEST_APP_HEVC);
- }
- }
-
- /**
- * Tests that we never initiate tanscoding for legacy formats.
- * This test compares the bytes read before and after enabling transcoding for the test app.
- * @throws Exception
- */
- @Test
- public void testTranscodedNotInitiatedForLegacy_UsingBytesRead() throws Exception {
- File legacyFile = new File(DIR_CAMERA, LEGACY_FILE_NAME);
- try {
- TranscodeTestUtils.stageLegacyVideoFile(legacyFile);
-
- ParcelFileDescriptor pfdOriginal = open(legacyFile, false);
-
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
- ParcelFileDescriptor pfdTranscoded = open(legacyFile, false);
-
- assertFileContent(legacyFile, legacyFile, pfdOriginal, pfdTranscoded, true);
- } finally {
- legacyFile.delete();
- }
- }
-
- /**
- * Tests that we never initiate tanscoding for legacy formats.
- * This test asserts using the time it took to read after enabling transcoding for the test app.
- * The reason for keeping this check separately (than
- * {@link TranscodeTest#testTranscodedNotInitiatedForLegacy_UsingTiming()}) is that this
- * provides a higher level of suret that the timing wasn't favorable because of any caching
- * after open().
- * @throws Exception
- */
- @Test
- public void testTranscodedNotInitiatedForLegacy_UsingTiming() throws Exception {
- File legacyFile = new File(DIR_CAMERA, LEGACY_FILE_NAME);
- try {
- TranscodeTestUtils.stageLegacyVideoFile(legacyFile);
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
-
- assertTranscode(legacyFile, false);
- } finally {
- legacyFile.delete();
- }
- }
-
- /**
- * Tests that we don't timeout while transcoding small HEVC videos.
- * For instance, due to some calculation errors we might incorrectly make timeout to be 0.
- * We test this by making sure that a small HEVC video (< 1 sec long and < 1Mb size) gets
- * transcoded.
- * @throws Exception
- */
- @Test
- public void testNoTranscodeTimeoutForSmallHevcVideos() throws Exception {
- File modernFile = new File(DIR_CAMERA, SMALL_HEVC_FILE_NAME);
- try {
- TranscodeTestUtils.stageSmallHevcVideoFile(modernFile);
- ParcelFileDescriptor pfdOriginal = open(modernFile, false);
-
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
- ParcelFileDescriptor pfdTranscoded = open(modernFile, false);
-
- assertFileContent(modernFile, modernFile, pfdOriginal, pfdTranscoded, false);
- } finally {
- modernFile.delete();
- }
- }
-
- /**
- * Tests that we transcode an HEVC file when a modern app passes the mediaCapabilitiesUid of a
- * legacy app that cannot handle an HEVC file.
- */
- @Test
- public void testOriginalCallingUid_modernAppPassLegacyAppUid()
- throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- ParcelFileDescriptor pfdModernApp = null;
- ParcelFileDescriptor pfdModernAppPassingLegacyUid = null;
- try {
- installAppWithStoragePermissions(TEST_APP_SLOW_MOTION);
- Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
-
- // pfdModernApp is for original content (without transcoding) since this is a modern
- // app.
- pfdModernApp = open(modernFile, false);
-
- // pfdModernAppPassingLegacyUid is for transcoded content since this modern app is
- // passing the UID of a legacy app capable of handling HEVC files.
- Bundle bundle = new Bundle();
- bundle.putInt(MediaStore.EXTRA_MEDIA_CAPABILITIES_UID,
- getContext().getPackageManager().getPackageUid(
- TEST_APP_SLOW_MOTION.getPackageName(), 0));
- pfdModernAppPassingLegacyUid = open(uri, false, bundle);
-
- assertTranscode(pfdModernApp, false);
- assertTranscode(pfdModernAppPassingLegacyUid, true);
-
- // pfdModernApp and pfdModernAppPassingLegacyUid should be different.
- assertFileContent(modernFile, modernFile, pfdModernApp, pfdModernAppPassingLegacyUid,
- false);
- } finally {
- if (pfdModernApp != null) {
- pfdModernApp.close();
- }
-
- if (pfdModernAppPassingLegacyUid != null) {
- pfdModernAppPassingLegacyUid.close();
- }
- modernFile.delete();
- uninstallApp(TEST_APP_SLOW_MOTION);
- }
- }
-
- /**
- * Tests that we don't transcode an HEVC file when a legacy app passes the mediaCapabilitiesUid
- * of a modern app that can handle an HEVC file.
- */
- @Test
- public void testOriginalCallingUid_legacyAppPassModernAppUid()
- throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- ParcelFileDescriptor pfdLegacyApp = null;
- ParcelFileDescriptor pfdLegacyAppPassingModernUid = null;
- try {
- installAppWithStoragePermissions(TEST_APP_HEVC);
- Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
-
- // pfdLegacyApp is for transcoded content since this is a legacy app.
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
- pfdLegacyApp = open(modernFile, false);
-
- // pfdLegacyAppPassingModernUid is for original content (without transcoding) since this
- // legacy app is passing the UID of a modern app capable of handling HEVC files.
- Bundle bundle = new Bundle();
- bundle.putInt(MediaStore.EXTRA_MEDIA_CAPABILITIES_UID,
- getContext().getPackageManager().getPackageUid(TEST_APP_HEVC.getPackageName(),
- 0));
- pfdLegacyAppPassingModernUid = open(uri, false, bundle);
-
- assertTranscode(pfdLegacyApp, true);
- assertTranscode(pfdLegacyAppPassingModernUid, false);
-
- // pfdLegacyApp and pfdLegacyAppPassingModernUid should be different.
- assertFileContent(modernFile, modernFile, pfdLegacyApp, pfdLegacyAppPassingModernUid,
- false);
- } finally {
- if (pfdLegacyApp != null) {
- pfdLegacyApp.close();
- }
-
- if (pfdLegacyAppPassingModernUid != null) {
- pfdLegacyAppPassingModernUid.close();
- }
- modernFile.delete();
- uninstallApp(TEST_APP_HEVC);
- }
- }
-
- /**
- * Tests that we return FD of original file from
- * MediaStore#getOriginalMediaFormatFileDescriptor.
- * @throws Exception
- */
- @Test
- public void testGetOriginalMediaFormatFileDescriptor_returnsOriginalFileDescriptor()
- throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- try {
- TranscodeTestUtils.stageHEVCVideoFile(modernFile);
-
- ParcelFileDescriptor pfdOriginal = open(modernFile, false);
-
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
- ParcelFileDescriptor pfdTranscoded = open(modernFile, false);
-
- ParcelFileDescriptor pfdOriginalMediaFormat =
- MediaStore.getOriginalMediaFormatFileDescriptor(getContext(), pfdTranscoded);
-
- assertFileContent(modernFile, modernFile, pfdOriginal, pfdOriginalMediaFormat, true);
- assertFileContent(modernFile, modernFile, pfdTranscoded, pfdOriginalMediaFormat, false);
- } finally {
- modernFile.delete();
- }
- }
-
- /**
- * Tests that we can successfully write to a transcoded file.
- * We check this by writing something to tanscoded content and then read it back.
- */
- @Test
- public void testWriteSuccessfulToTranscodedContent() throws Exception {
- File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
- ParcelFileDescriptor pfdTranscodedContent = null;
- try {
- TranscodeTestUtils.stageHEVCVideoFile(modernFile);
- TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
- pfdTranscodedContent = open(modernFile, false);
-
- // read some bytes from some random offset
- Random random = new Random(System.currentTimeMillis());
- int byteCount = 512;
- int fileOffset = random.nextInt((int) pfdTranscodedContent.getStatSize() - byteCount);
- byte[] readBytes = TranscodeTestUtils.read(pfdTranscodedContent, byteCount, fileOffset);
-
- // write the bytes at the same offset after some modification
- pfdTranscodedContent = open(modernFile, true);
- byte[] writeBytes = new byte[byteCount];
- for (int i = 0; i < byteCount; ++i) {
- writeBytes[i] = (byte) ~readBytes[i];
- }
- TranscodeTestUtils.write(pfdTranscodedContent, writeBytes, byteCount, fileOffset);
-
- // read back the same number of bytes from the same offset
- readBytes = TranscodeTestUtils.read(pfdTranscodedContent, byteCount, fileOffset);
-
- // assert that read is same as written
- assertTrue(Arrays.equals(readBytes, writeBytes));
- } finally {
- if (pfdTranscodedContent != null) {
- pfdTranscodedContent.close();
- }
- modernFile.delete();
- }
- }
-}
diff --git a/tests/client/src/com/android/providers/media/client/PublicVolumeSetupHelper.java b/tests/utils/src/com/android/providers/media/tests/utils/PublicVolumeSetupHelper.java
similarity index 89%
rename from tests/client/src/com/android/providers/media/client/PublicVolumeSetupHelper.java
rename to tests/utils/src/com/android/providers/media/tests/utils/PublicVolumeSetupHelper.java
index 73bcf41..777abd8 100644
--- a/tests/client/src/com/android/providers/media/client/PublicVolumeSetupHelper.java
+++ b/tests/utils/src/com/android/providers/media/tests/utils/PublicVolumeSetupHelper.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.providers.media.client;
+package com.android.providers.media.tests.utils;
import android.app.UiAutomation;
import android.os.Environment;
@@ -37,16 +37,29 @@
/**
* Helper methods for public volume setup.
*/
-class PublicVolumeSetupHelper {
+public class PublicVolumeSetupHelper {
private static final long POLLING_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(2);
private static final long POLLING_SLEEP_MILLIS = 100;
private static final String TAG = "TestUtils";
private static boolean usingExistingPublicVolume = false;
/**
+ * (Re-)partitions an already created pulic volume
+ */
+ public static void partitionPublicVolume() throws Exception {
+ pollForCondition(() -> partitionDisk(), "Timed out while waiting for"
+ + " disk partitioning");
+ // Poll twice to avoid using previous mount status
+ pollForCondition(() -> isPublicVolumeMounted(), "Timed out while waiting for"
+ + " the public volume to mount");
+ pollForCondition(() -> isExternalStorageStateMounted(), "Timed out while"
+ + " waiting for ExternalStorageState to be MEDIA_MOUNTED");
+ }
+
+ /**
* Creates a new virtual public volume and returns the volume's name.
*/
- static void createNewPublicVolume() throws Exception {
+ public static void createNewPublicVolume() throws Exception {
// Skip public volume setup if we can use already available public volume on the device.
if (getCurrentPublicVolumeString() != null && isPublicVolumeMounted()) {
usingExistingPublicVolume = true;
@@ -54,13 +67,8 @@
}
executeShellCommand("sm set-force-adoptable on");
executeShellCommand("sm set-virtual-disk true");
- pollForCondition(() -> partitionDisk(), "Timed out while waiting for"
- + " disk partitioning");
- // Poll twice to avoid using previous mount status
- pollForCondition(() -> isPublicVolumeMounted(), "Timed out while waiting for"
- + " the public volume to mount");
- pollForCondition(() -> isExternalStorageStateMounted(), "Timed out while"
- + " waiting for ExternalStorageState to be MEDIA_MOUNTED");
+
+ partitionPublicVolume();
}
private static boolean isExternalStorageStateMounted() {
@@ -112,7 +120,7 @@
/**
* @return the currently mounted public volume string, if any.
*/
- private static String getCurrentPublicVolumeString() {
+ static String getCurrentPublicVolumeString() {
final String[] allPublicVolumeDetails;
try {
allPublicVolumeDetails = executeShellCommand("sm list-volumes public")
@@ -134,15 +142,15 @@
return null;
}
- static void mountPublicVolume() throws Exception {
+ public static void mountPublicVolume() throws Exception {
executeShellCommand("sm mount " + getPublicVolumeString());
}
- static void unmountPublicVolume() throws Exception {
+ public static void unmountPublicVolume() throws Exception {
executeShellCommand("sm unmount " + getPublicVolumeString());
}
- static void deletePublicVolumes() throws Exception {
+ public static void deletePublicVolumes() throws Exception {
if (!usingExistingPublicVolume) {
executeShellCommand("sm set-virtual-disk false");
// Wait for the public volume to disappear.
@@ -181,7 +189,7 @@
}
}
- static void pollForCondition(Supplier<Boolean> condition, String errorMessage)
+ public static void pollForCondition(Supplier<Boolean> condition, String errorMessage)
throws Exception {
for (int i = 0; i < POLLING_TIMEOUT_MILLIS / POLLING_SLEEP_MILLIS; i++) {
if (condition.get()) {
diff --git a/tests/utils/src/com/android/providers/media/tests/utils/Timer.java b/tests/utils/src/com/android/providers/media/tests/utils/Timer.java
index 6d8caca..5d56999 100644
--- a/tests/utils/src/com/android/providers/media/tests/utils/Timer.java
+++ b/tests/utils/src/com/android/providers/media/tests/utils/Timer.java
@@ -76,7 +76,7 @@
Log.v(TAG, name + ": " + duration + "ms");
final Bundle results = new Bundle();
- results.putLong(name, duration);
+ results.putLong(name + " (ms)", duration);
InstrumentationRegistry.getInstrumentation().sendStatus(0, results);
}
}
diff --git a/tools/dialogs/Android.bp b/tools/dialogs/Android.bp
index d0ccff2..98267be 100644
--- a/tools/dialogs/Android.bp
+++ b/tools/dialogs/Android.bp
@@ -1,10 +1,6 @@
package {
// See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "packages_providers_MediaProvider_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["packages_providers_MediaProvider_license"],
+ default_applicable_licenses: ["Android-Apache-2.0"],
}
android_app {
diff --git a/tools/dialogs/AndroidManifest.xml b/tools/dialogs/AndroidManifest.xml
index 960cb13..73527cc 100644
--- a/tools/dialogs/AndroidManifest.xml
+++ b/tools/dialogs/AndroidManifest.xml
@@ -4,6 +4,9 @@
package="com.android.providers.media.tools.dialogs">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/>
+ <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
+ <uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_MEDIA"/>
diff --git a/tools/dialogs/src/com/android/providers/media/tools/dialogs/DialogsActivity.java b/tools/dialogs/src/com/android/providers/media/tools/dialogs/DialogsActivity.java
index 867bfaf..f52354d 100644
--- a/tools/dialogs/src/com/android/providers/media/tools/dialogs/DialogsActivity.java
+++ b/tools/dialogs/src/com/android/providers/media/tools/dialogs/DialogsActivity.java
@@ -17,6 +17,9 @@
package com.android.providers.media.tools.dialogs;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.Manifest.permission.READ_MEDIA_AUDIO;
+import static android.Manifest.permission.READ_MEDIA_IMAGES;
+import static android.Manifest.permission.READ_MEDIA_VIDEO;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -25,6 +28,7 @@
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
import android.provider.MediaStore.Files.FileColumns;
@@ -62,11 +66,23 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- if (checkSelfPermission(READ_EXTERNAL_STORAGE) != PERMISSION_GRANTED
- || checkSelfPermission(WRITE_EXTERNAL_STORAGE) != PERMISSION_GRANTED) {
- requestPermissions(new String[] { READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE }, 42);
- finish();
- return;
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) {
+ if (checkSelfPermission(READ_EXTERNAL_STORAGE) != PERMISSION_GRANTED
+ || checkSelfPermission(WRITE_EXTERNAL_STORAGE) != PERMISSION_GRANTED) {
+ requestPermissions(new String[]{READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE}, 42);
+ android.util.Log.d("doc", "finish");
+ finish();
+ return;
+ }
+ } else {
+ if (checkSelfPermission(READ_MEDIA_AUDIO) != PERMISSION_GRANTED
+ || checkSelfPermission(READ_MEDIA_IMAGES) != PERMISSION_GRANTED
+ || checkSelfPermission(READ_MEDIA_VIDEO) != PERMISSION_GRANTED) {
+ requestPermissions(
+ new String[]{READ_MEDIA_AUDIO, READ_MEDIA_IMAGES, READ_MEDIA_VIDEO}, 42);
+ finish();
+ return;
+ }
}
mBody = new LinearLayout(this);
diff --git a/tools/genfiles/genfiles.sh b/tools/genfiles/genfiles.sh
index 0df984a..0e1cff3 100755
--- a/tools/genfiles/genfiles.sh
+++ b/tools/genfiles/genfiles.sh
@@ -20,7 +20,7 @@
# This script queries a media provider database, and generates a script to
# approximately recreate the same file system structure on another device,
-# using dummy files.
+# using placeholder files.
EXTERNAL=$2
if [ "$EXTERNAL" == "" ]
diff --git a/tools/photopicker-gradle/app/.gitignore b/tools/photopicker-gradle/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/tools/photopicker-gradle/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/tools/photopicker-gradle/app/build.gradle b/tools/photopicker-gradle/app/build.gradle
new file mode 100644
index 0000000..255b67e
--- /dev/null
+++ b/tools/photopicker-gradle/app/build.gradle
@@ -0,0 +1,34 @@
+plugins {
+ id 'com.android.application'
+}
+
+android {
+ compileSdk 30
+ compileSdkVersion 'android-SX'
+
+ defaultConfig {
+ applicationId "com.android.providers.media.tools.photopicker"
+ minSdk 16
+ targetSdk 30
+ versionCode 1
+ versionName "1.0"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+}
+
+dependencies {
+
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'com.google.android.material:material:1.3.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
+}
\ No newline at end of file
diff --git a/tools/photopicker-gradle/app/proguard-rules.pro b/tools/photopicker-gradle/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/tools/photopicker-gradle/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/tools/photopicker-gradle/app/src/main/AndroidManifest.xml b/tools/photopicker-gradle/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..8ca3d10
--- /dev/null
+++ b/tools/photopicker-gradle/app/src/main/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.providers.media.tools.photopicker">
+
+ <application
+ android:allowBackup="true"
+ android:label="PhotoPickerTool"
+ android:supportsRtl="true">
+ <activity
+ android:name=".PhotoPickerToolActivity"
+ android:exported="true"
+ android:theme="@style/Theme.MaterialComponents.DayNight">
+ <intent-filter android:label="PhotoPickerTool">
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tools/photopicker-gradle/app/src/main/java/com/android/providers/media/tools/photopicker/PhotoPickerToolActivity.java b/tools/photopicker-gradle/app/src/main/java/com/android/providers/media/tools/photopicker/PhotoPickerToolActivity.java
new file mode 100644
index 0000000..c4adf1d
--- /dev/null
+++ b/tools/photopicker-gradle/app/src/main/java/com/android/providers/media/tools/photopicker/PhotoPickerToolActivity.java
@@ -0,0 +1,314 @@
+package com.android.providers.media.tools.photopicker;
+
+import static android.os.Build.VERSION.SDK_INT;
+import static android.provider.MediaStore.ACTION_PICK_IMAGES;
+import static android.provider.MediaStore.EXTRA_PICK_IMAGES_MAX;
+
+import android.annotation.SuppressLint;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.content.ActivityNotFoundException;
+import android.content.ClipData;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.ext.SdkExtensions;
+import android.provider.MediaStore;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+import android.widget.TextView;
+import android.widget.VideoView;
+
+import com.google.android.material.snackbar.Snackbar;
+
+public class PhotoPickerToolActivity extends AppCompatActivity {
+
+ private static final String TAG = "PhotoPickerToolActivity";
+ private static final int REQUEST_CODE = 42;
+
+ private int mMaxCount = 10;
+ private boolean mIsShowImageOnly;
+ private boolean mIsShowVideoOnly;
+ private boolean mSetMimeType;
+ private ScrollView mScrollView;
+
+ private CheckBox mSetImageOnlyCheckBox;
+ private CheckBox mSetVideoOnlyCheckBox;
+ private CheckBox mSetMimeTypeCheckBox;
+ private CheckBox mSetSelectionCountCheckBox;
+ private CheckBox mAllowMultipleCheckBox;
+ private CheckBox mGetContentCheckBox;
+ private EditText mMaxCountText;
+ private EditText mMimeTypeText;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ mAllowMultipleCheckBox = findViewById(R.id.cbx_allow_multiple);
+ mGetContentCheckBox = findViewById(R.id.cbx_get_content);
+ mSetImageOnlyCheckBox = findViewById(R.id.cbx_set_image_only);
+ mSetMimeTypeCheckBox = findViewById(R.id.cbx_set_mime_type);
+ mSetSelectionCountCheckBox = findViewById(R.id.cbx_set_selection_count);
+ mSetVideoOnlyCheckBox = findViewById(R.id.cbx_set_video_only);
+ mMaxCountText = findViewById(R.id.edittext_max_count);
+ mMimeTypeText = findViewById(R.id.edittext_mime_type);
+ mScrollView = findViewById(R.id.scrollview);
+
+ mSetImageOnlyCheckBox.setOnCheckedChangeListener(this::onShowImageOnlyCheckedChanged);
+ mSetVideoOnlyCheckBox.setOnCheckedChangeListener(this::onShowVideoOnlyCheckedChanged);
+ mSetMimeTypeCheckBox.setOnCheckedChangeListener(this::onSetMimeTypeCheckedChanged);
+ mSetSelectionCountCheckBox.setOnCheckedChangeListener(
+ this::onSetSelectionCountCheckedChanged);
+
+ mMaxCountText.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ try {
+ mMaxCount = Integer.parseInt(mMaxCountText.getText().toString().trim());
+ } catch (NumberFormatException ex) {
+ // The input is not an integer type, set the mMaxCount to -1.
+ mMaxCount = -1;
+ final String wrongFormatWarning =
+ "The count format is wrong! Please input correct number!";
+ Snackbar.make(mMaxCountText, wrongFormatWarning, Snackbar.LENGTH_LONG).show();
+ }
+ }
+ });
+
+ final Button launchButton = findViewById(R.id.launch_button);
+ launchButton.setOnClickListener(this::onLaunchButtonClicked);
+ }
+
+ private void onShowImageOnlyCheckedChanged(View view, boolean isChecked) {
+ if (mIsShowImageOnly == isChecked) {
+ return;
+ }
+
+ mIsShowImageOnly = isChecked;
+ if (isChecked) {
+ mSetVideoOnlyCheckBox.setChecked(false);
+ mSetMimeTypeCheckBox.setChecked(false);
+ }
+ }
+
+ private void onShowVideoOnlyCheckedChanged(View view, boolean isChecked) {
+ if (mIsShowVideoOnly == isChecked) {
+ return;
+ }
+
+ mIsShowVideoOnly = isChecked;
+ if (isChecked) {
+ mSetImageOnlyCheckBox.setChecked(false);
+ mSetMimeTypeCheckBox.setChecked(false);
+ }
+ }
+
+ private void onSetMimeTypeCheckedChanged(View view, boolean isChecked) {
+ if (mSetMimeType == isChecked) {
+ return;
+ }
+
+ mSetMimeType = isChecked;
+ if (isChecked) {
+ mSetImageOnlyCheckBox.setChecked(false);
+ mSetVideoOnlyCheckBox.setChecked(false);
+ }
+ mMimeTypeText.setEnabled(isChecked);
+ }
+
+ private void onSetSelectionCountCheckedChanged(View view, boolean isChecked) {
+ mMaxCountText.setEnabled(isChecked);
+ }
+
+ // We mistakenly get lint warning about using getPickImagesMaxLimit. The API is
+ // actually available in SX.
+ @SuppressLint("NewApi")
+ private void onLaunchButtonClicked(View view) {
+ final Intent intent;
+ if (mGetContentCheckBox.isChecked()) {
+ intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.setType("*/*");
+ } else {
+ if (!isPhotoPickerAvailable()) {
+ logErrorAndShowToast("Photo Picker is not available on this device");
+ return;
+ }
+ intent = new Intent(ACTION_PICK_IMAGES);
+ }
+
+ if (mAllowMultipleCheckBox.isChecked()) {
+ if (mGetContentCheckBox.isChecked()) {
+ intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
+ } else {
+ // We mistakenly get lint warning about using getPickImagesMaxLimit. The API is
+ // actually available in SX.
+ intent.putExtra(EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit());
+ }
+ }
+
+ if (mSetImageOnlyCheckBox.isChecked()) {
+ intent.setType("image/*");
+ } else if (mSetVideoOnlyCheckBox.isChecked()) {
+ intent.setType("video/*");
+ } else if (mSetMimeTypeCheckBox.isChecked()) {
+ final String mimeType = mMimeTypeText.getText().toString().trim();
+ intent.setType(mimeType);
+ }
+
+ if (mSetSelectionCountCheckBox.isChecked()) {
+ intent.putExtra(EXTRA_PICK_IMAGES_MAX, mMaxCount);
+ }
+
+ try {
+ startActivityForResult(intent, REQUEST_CODE);
+ } catch (ActivityNotFoundException ex){
+ final String errorMessage =
+ "No Activity found to handle Intent with type \"" + intent.getType() + "\"";
+ logErrorAndShowToast(errorMessage);
+ }
+ }
+
+ // We mistakenly get lint warning about using getExtensionVersion on API level < 32. The API is
+ // actually available in R+.
+ @SuppressLint("NewApi")
+ private boolean isPhotoPickerAvailable() {
+ return SDK_INT >= Build.VERSION_CODES.R
+ && SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R) >= 2;
+ }
+
+ private void logErrorAndShowToast(String errorMessage) {
+ Log.e(TAG, errorMessage);
+ Snackbar.make(mScrollView, errorMessage, Snackbar.LENGTH_LONG).show();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ if (resultCode == RESULT_CANCELED) {
+ Log.e(TAG, "The result code is canceled");
+ return;
+ };
+
+ if (requestCode != REQUEST_CODE) {
+ logErrorAndShowToast("The request code is not as we expected");
+ return;
+ }
+
+ if (data == null) {
+ logErrorAndShowToast("The result intent is null");
+ return;
+ }
+
+ final Uri uri = data.getData();
+ if (uri == null && data.getClipData() == null) {
+ logErrorAndShowToast("The uri and clipData of result intent is null");
+ return;
+ }
+
+ final LinearLayout itemContainer = findViewById(R.id.item_container);
+ final int itemSize = (int) (300 * getResources().getDisplayMetrics().density);
+ final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(itemSize, itemSize);
+ params.gravity = Gravity.CENTER;
+ itemContainer.removeAllViews();
+ if (uri != null) {
+ itemContainer.addView(generateText(uri.toString()));
+ itemContainer.addView(generateItems(uri, params));
+ } else {
+ final ClipData clipData = data.getClipData();
+ final int count = clipData.getItemCount();
+ for (int i = 0; i < count; i++) {
+ Uri item = (Uri) clipData.getItemAt(i).getUri();
+ itemContainer.addView(generateText("" + i + ". " + item.toString()));
+ itemContainer.addView(generateItems(item, params));
+ }
+ // scroll to first item
+ mScrollView.smoothScrollTo(0, 0);
+ }
+ }
+
+ private TextView generateText(String text) {
+ final TextView textView = new TextView(this);
+ textView.setTextAppearance(R.style.HeaderTitle);
+ textView.setText(text);
+ return textView;
+ }
+
+ private View generateItems(Uri uri, LinearLayout.LayoutParams params) {
+ String mimeType = null;
+ // TODO: after getType issue is fixed, change to use getType
+ try (Cursor cursor = getContentResolver().query(uri,
+ new String[]{MediaStore.Files.FileColumns.MIME_TYPE}, null, null, null, null)) {
+ cursor.moveToFirst();
+ mimeType = cursor.getString(0);
+ }
+
+ if (isVideoMimeType(mimeType)) {
+ return generateVideoView(uri, params);
+ } else {
+ return generateImageView(uri, params);
+ }
+ }
+
+ private VideoView generateVideoView(Uri uri, LinearLayout.LayoutParams params) {
+ final VideoView video = new VideoView(this);
+ video.setLayoutParams(params);
+ video.setVideoURI(uri);
+ video.setOnPreparedListener(mp -> {
+ mp.setLooping(true);
+ mp.seekTo(0);
+ mp.start();
+ });
+ return video;
+ }
+
+ private ImageView generateImageView(Uri uri, LinearLayout.LayoutParams params) {
+ final ImageView image = new ImageView(this);
+ image.setLayoutParams(params);
+ image.setScaleType(ImageView.ScaleType.FIT_CENTER);
+ image.setImageURI(uri);
+ return image;
+ }
+
+ private static boolean isVideoMimeType(@Nullable String mimeType) {
+ if (mimeType == null) {
+ return false;
+ }
+ return startsWithIgnoreCase(mimeType, "video/");
+ }
+
+ /**
+ * Variant of {@link String#startsWith(String)} but which tests with case-insensitivity.
+ */
+ private static boolean startsWithIgnoreCase(@Nullable String target, @Nullable String other) {
+ if (target == null || other == null) {
+ return false;
+ }
+ if (other.length() >= target.length()) {
+ return false;
+ }
+ return target.regionMatches(true, 0, other, 0, other.length());
+ }
+}
diff --git a/tools/photopicker-gradle/app/src/main/res/layout/activity_main.xml b/tools/photopicker-gradle/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..c900517
--- /dev/null
+++ b/tools/photopicker-gradle/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright 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
+
+ https://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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <CheckBox
+ android:id="@+id/cbx_get_content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="ACTION_GET_CONTENT"
+ android:textSize="16sp" />
+
+ <CheckBox
+ android:id="@+id/cbx_allow_multiple"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="ALLOW MULTIPLE"
+ android:textSize="16sp" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <CheckBox
+ android:id="@+id/cbx_set_image_only"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="SHOW IMAGE ONLY"
+ android:textSize="16sp" />
+
+ <CheckBox
+ android:id="@+id/cbx_set_video_only"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="SHOW VIDEO ONLY"
+ android:textSize="16sp" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <CheckBox
+ android:id="@+id/cbx_set_mime_type"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="SET MIME TYPE"
+ android:textSize="16sp" />
+
+ <EditText
+ android:id="@+id/edittext_mime_type"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:enabled="false"
+ android:textSize="16sp" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <CheckBox
+ android:id="@+id/cbx_set_selection_count"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="SET SELECTION COUNT"
+ android:textSize="16sp" />
+
+ <EditText
+ android:id="@+id/edittext_max_count"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:enabled="false"
+ android:text="10"
+ android:textSize="16sp" />
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/launch_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Launch"
+ android:textSize="16sp" />
+
+ <ScrollView
+ android:id="@+id/scrollview"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:id="@+id/item_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" />
+ </ScrollView>
+</LinearLayout>
diff --git a/tools/photopicker-gradle/app/src/main/res/values/styles.xml b/tools/photopicker-gradle/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..3ad1215
--- /dev/null
+++ b/tools/photopicker-gradle/app/src/main/res/values/styles.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <style name="HeaderTitle"
+ parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
+</resources>
diff --git a/tools/photopicker-gradle/build.gradle b/tools/photopicker-gradle/build.gradle
new file mode 100644
index 0000000..7cbb664
--- /dev/null
+++ b/tools/photopicker-gradle/build.gradle
@@ -0,0 +1,17 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:7.0.3"
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
\ No newline at end of file
diff --git a/tools/photopicker-gradle/gradle.properties b/tools/photopicker-gradle/gradle.properties
new file mode 100644
index 0000000..52f5917
--- /dev/null
+++ b/tools/photopicker-gradle/gradle.properties
@@ -0,0 +1,19 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
\ No newline at end of file
diff --git a/tools/photopicker-gradle/gradle/wrapper/gradle-wrapper.jar b/tools/photopicker-gradle/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
--- /dev/null
+++ b/tools/photopicker-gradle/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/tools/photopicker-gradle/gradle/wrapper/gradle-wrapper.properties b/tools/photopicker-gradle/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..1a33dec
--- /dev/null
+++ b/tools/photopicker-gradle/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Nov 24 17:14:38 GMT 2021
+distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
diff --git a/tools/photopicker-gradle/gradlew b/tools/photopicker-gradle/gradlew
new file mode 100755
index 0000000..4f906e0
--- /dev/null
+++ b/tools/photopicker-gradle/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# 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
+#
+# https://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.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/tools/photopicker-gradle/gradlew.bat b/tools/photopicker-gradle/gradlew.bat
new file mode 100644
index 0000000..ac1b06f
--- /dev/null
+++ b/tools/photopicker-gradle/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/tools/photopicker-gradle/settings.gradle b/tools/photopicker-gradle/settings.gradle
new file mode 100644
index 0000000..e09bea4
--- /dev/null
+++ b/tools/photopicker-gradle/settings.gradle
@@ -0,0 +1,10 @@
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ jcenter() // Warning: this repository is going to shut down soon
+ }
+}
+rootProject.name = "photopicker"
+include ':app'
diff --git a/tools/photopicker/Android.bp b/tools/photopicker/Android.bp
new file mode 100644
index 0000000..d05c935
--- /dev/null
+++ b/tools/photopicker/Android.bp
@@ -0,0 +1,25 @@
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_app {
+ name: "PhotoPickerTool",
+ manifest: "AndroidManifest.xml",
+
+ static_libs: [
+ "com.google.android.material_material",
+ "glide-prebuilt",
+ "glide-gifdecoder-prebuilt",
+ "glide-disklrucache-prebuilt",
+ "glide-annotation-and-compiler-prebuilt",
+ "androidx.fragment_fragment",
+ "androidx.vectordrawable_vectordrawable-animated",
+ "androidx.exifinterface_exifinterface",
+ ],
+
+ srcs: ["src/**/*.java"],
+ sdk_version: "current",
+ target_sdk_version: "30",
+ min_sdk_version: "30",
+}
diff --git a/tools/photopicker/AndroidManifest.xml b/tools/photopicker/AndroidManifest.xml
new file mode 100644
index 0000000..81204d2
--- /dev/null
+++ b/tools/photopicker/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.providers.media.tools.photopicker">
+ <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30"/>
+
+ <application android:label="PhotoPickerTool">
+ <activity android:name=".PhotoPickerToolActivity"
+ android:exported="true"
+ android:theme="@style/Theme.MaterialComponents.DayNight">
+ <intent-filter android:label="PhotoPickerTool">
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tools/photopicker/res/layout/activity_main.xml b/tools/photopicker/res/layout/activity_main.xml
new file mode 100644
index 0000000..441cd0f
--- /dev/null
+++ b/tools/photopicker/res/layout/activity_main.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright 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
+
+ https://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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <CheckBox
+ android:id="@+id/cbx_get_content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="ACTION_GET_CONTENT"
+ android:textSize="16sp" />
+
+ <CheckBox
+ android:id="@+id/cbx_allow_multiple"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="ALLOW MULTIPLE"
+ android:textSize="16sp" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <CheckBox
+ android:id="@+id/cbx_set_image_only"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="SHOW IMAGE ONLY"
+ android:textSize="16sp" />
+
+ <CheckBox
+ android:id="@+id/cbx_set_video_only"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="SHOW VIDEO ONLY"
+ android:textSize="16sp" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <CheckBox
+ android:id="@+id/cbx_set_mime_type"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="SET MIME TYPE"
+ android:textSize="16sp" />
+
+ <EditText
+ android:id="@+id/edittext_mime_type"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:enabled="false"
+ android:textSize="16sp" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <CheckBox
+ android:id="@+id/cbx_set_selection_count"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="SET SELECTION COUNT"
+ android:textSize="16sp" />
+
+ <EditText
+ android:id="@+id/edittext_max_count"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:enabled="false"
+ android:text="10"
+ android:textSize="16sp" />
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/launch_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Launch"
+ android:textSize="16sp" />
+
+ <ScrollView
+ android:id="@+id/scrollview"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:id="@+id/item_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" />
+ </ScrollView>
+</LinearLayout>
diff --git a/tools/photopicker/res/values/styles.xml b/tools/photopicker/res/values/styles.xml
new file mode 100644
index 0000000..6449a5c
--- /dev/null
+++ b/tools/photopicker/res/values/styles.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <style name="HeaderTitle"
+ parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/tools/photopicker/src/com/android/providers/media/tools/photopicker/PhotoPickerToolActivity.java b/tools/photopicker/src/com/android/providers/media/tools/photopicker/PhotoPickerToolActivity.java
new file mode 100644
index 0000000..fc91077
--- /dev/null
+++ b/tools/photopicker/src/com/android/providers/media/tools/photopicker/PhotoPickerToolActivity.java
@@ -0,0 +1,313 @@
+/*
+ * 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.tools.photopicker;
+
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.ClipData;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.MediaStore;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+import android.widget.TextView;
+import android.widget.VideoView;
+
+import androidx.annotation.Nullable;
+
+import com.bumptech.glide.Glide;
+import com.google.android.material.snackbar.Snackbar;
+
+public class PhotoPickerToolActivity extends Activity {
+
+ private static final String TAG = "PhotoPickerToolActivity";
+ private static final String EXTRA_PICK_IMAGES_MAX = "android.provider.extra.PICK_IMAGES_MAX";
+ private static final String ACTION_PICK_IMAGES = "android.provider.action.PICK_IMAGES";
+ private static final int PICK_IMAGES_MAX_LIMIT = 100;
+ private static final int REQUEST_CODE = 42;
+
+ private int mMaxCount = 10;
+ private boolean mIsShowImageOnly;
+ private boolean mIsShowVideoOnly;
+ private boolean mSetMimeType;
+ private ScrollView mScrollView;
+
+ private CheckBox mSetImageOnlyCheckBox;
+ private CheckBox mSetVideoOnlyCheckBox;
+ private CheckBox mSetMimeTypeCheckBox;
+ private CheckBox mSetSelectionCountCheckBox;
+ private CheckBox mAllowMultipleCheckBox;
+ private CheckBox mGetContentCheckBox;
+ private EditText mMaxCountText;
+ private EditText mMimeTypeText;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ mAllowMultipleCheckBox = findViewById(R.id.cbx_allow_multiple);
+ mGetContentCheckBox = findViewById(R.id.cbx_get_content);
+ mSetImageOnlyCheckBox = findViewById(R.id.cbx_set_image_only);
+ mSetMimeTypeCheckBox = findViewById(R.id.cbx_set_mime_type);
+ mSetSelectionCountCheckBox = findViewById(R.id.cbx_set_selection_count);
+ mSetVideoOnlyCheckBox = findViewById(R.id.cbx_set_video_only);
+ mMaxCountText = findViewById(R.id.edittext_max_count);
+ mMimeTypeText = findViewById(R.id.edittext_mime_type);
+ mScrollView = findViewById(R.id.scrollview);
+
+ mSetImageOnlyCheckBox.setOnCheckedChangeListener(this::onShowImageOnlyCheckedChanged);
+ mSetVideoOnlyCheckBox.setOnCheckedChangeListener(this::onShowVideoOnlyCheckedChanged);
+ mSetMimeTypeCheckBox.setOnCheckedChangeListener(this::onSetMimeTypeCheckedChanged);
+ mSetSelectionCountCheckBox.setOnCheckedChangeListener(
+ this::onSetSelectionCountCheckedChanged);
+
+ mMaxCountText.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ try {
+ mMaxCount = Integer.parseInt(mMaxCountText.getText().toString().trim());
+ } catch (NumberFormatException ex) {
+ // The input is not an integer type, set the mMaxCount to -1.
+ mMaxCount = -1;
+ final String wrongFormatWarning =
+ "The count format is wrong! Please input correct number!";
+ Snackbar.make(mMaxCountText, wrongFormatWarning, Snackbar.LENGTH_LONG).show();
+ }
+ }
+ });
+
+ final Button launchButton = findViewById(R.id.launch_button);
+ launchButton.setOnClickListener(this::onLaunchButtonClicked);
+ }
+
+ private void onShowImageOnlyCheckedChanged(View view, boolean isChecked) {
+ if (mIsShowImageOnly == isChecked) {
+ return;
+ }
+
+ mIsShowImageOnly = isChecked;
+ if (isChecked) {
+ mSetVideoOnlyCheckBox.setChecked(false);
+ mSetMimeTypeCheckBox.setChecked(false);
+ }
+ }
+
+ private void onShowVideoOnlyCheckedChanged(View view, boolean isChecked) {
+ if (mIsShowVideoOnly == isChecked) {
+ return;
+ }
+
+ mIsShowVideoOnly = isChecked;
+ if (isChecked) {
+ mSetImageOnlyCheckBox.setChecked(false);
+ mSetMimeTypeCheckBox.setChecked(false);
+ }
+ }
+
+ private void onSetMimeTypeCheckedChanged(View view, boolean isChecked) {
+ if (mSetMimeType == isChecked) {
+ return;
+ }
+
+ mSetMimeType = isChecked;
+ if (isChecked) {
+ mSetImageOnlyCheckBox.setChecked(false);
+ mSetVideoOnlyCheckBox.setChecked(false);
+ }
+ mMimeTypeText.setEnabled(isChecked);
+ }
+
+ private void onSetSelectionCountCheckedChanged(View view, boolean isChecked) {
+ mMaxCountText.setEnabled(isChecked);
+ }
+
+ private void onLaunchButtonClicked(View view) {
+ final Intent intent;
+ if (mGetContentCheckBox.isChecked()) {
+ intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.setType("*/*");
+ } else {
+ intent = new Intent(ACTION_PICK_IMAGES);
+ }
+
+ if (mAllowMultipleCheckBox.isChecked()) {
+ if (mGetContentCheckBox.isChecked()) {
+ intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
+ } else {
+ intent.putExtra(EXTRA_PICK_IMAGES_MAX, PICK_IMAGES_MAX_LIMIT);
+ }
+ }
+
+ if (mSetImageOnlyCheckBox.isChecked()) {
+ intent.setType("image/*");
+ } else if (mSetVideoOnlyCheckBox.isChecked()) {
+ intent.setType("video/*");
+ } else if (mSetMimeTypeCheckBox.isChecked()) {
+ final String mimeType = mMimeTypeText.getText().toString().trim();
+ intent.setType(mimeType);
+ }
+
+ if (mSetSelectionCountCheckBox.isChecked()) {
+ intent.putExtra(EXTRA_PICK_IMAGES_MAX, mMaxCount);
+ }
+
+ try {
+ startActivityForResult(intent, REQUEST_CODE);
+ } catch (ActivityNotFoundException ex){
+ final String errorMessage =
+ "No Activity found to handle Intent with type \"" + intent.getType() + "\"";
+ logErrorAndShowToast(errorMessage);
+ }
+ }
+
+ private void logErrorAndShowToast(String errorMessage) {
+ Log.e(TAG, errorMessage);
+ Snackbar.make(mScrollView, errorMessage, Snackbar.LENGTH_LONG).show();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ if (resultCode == RESULT_CANCELED) {
+ Log.e(TAG, "The result code is canceled");
+ return;
+ };
+
+ if (requestCode != REQUEST_CODE) {
+ logErrorAndShowToast("The request code is not as we expected");
+ return;
+ }
+
+ if (data == null) {
+ logErrorAndShowToast("The result intent is null");
+ return;
+ }
+
+ final Uri uri = data.getData();
+ if (uri == null && data.getClipData() == null) {
+ logErrorAndShowToast("The uri and clipData of result intent is null");
+ return;
+ }
+
+ final LinearLayout itemContainer = findViewById(R.id.item_container);
+ final int itemSize = (int) (300 * getResources().getDisplayMetrics().density);
+ final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(itemSize, itemSize);
+ params.gravity = Gravity.CENTER;
+ itemContainer.removeAllViews();
+ if (uri != null) {
+ itemContainer.addView(generateText(uri.toString()));
+ itemContainer.addView(generateItems(uri, params));
+ } else {
+ final ClipData clipData = data.getClipData();
+ final int count = clipData.getItemCount();
+ for (int i = 0; i < count; i++) {
+ Uri item = (Uri) clipData.getItemAt(i).getUri();
+ itemContainer.addView(generateText("" + i + ". " + item.toString()));
+ itemContainer.addView(generateItems(item, params));
+ }
+ // scroll to first item
+ mScrollView.smoothScrollTo(0, 0);
+ }
+ }
+
+ private TextView generateText(String text) {
+ final TextView textView = new TextView(this);
+ textView.setTextAppearance(R.style.HeaderTitle);
+ textView.setText(text);
+ return textView;
+ }
+
+ private ImageView generateImageView(Uri uri, LinearLayout.LayoutParams params) {
+ final ImageView image = new ImageView(this);
+ image.setLayoutParams(params);
+ image.setScaleType(ImageView.ScaleType.FIT_CENTER);
+ Glide.with(this)
+ .load(uri)
+ .thumbnail()
+ .into(image);
+ return image;
+ }
+
+ private VideoView generateVideoView(Uri uri, LinearLayout.LayoutParams params) {
+ final VideoView video = new VideoView(this);
+ video.setLayoutParams(params);
+ video.setVideoURI(uri);
+ video.setOnPreparedListener(mp -> {
+ mp.setLooping(true);
+ mp.seekTo(0);
+ mp.start();
+ });
+ return video;
+ }
+
+ private View generateItems(Uri uri, LinearLayout.LayoutParams params) {
+ String mimeType = null;
+ // TODO: after getType issue is fixed, change to use getType
+ try (Cursor cursor = getContentResolver().query(uri,
+ new String[]{MediaStore.Files.FileColumns.MIME_TYPE}, null, null, null, null)) {
+ cursor.moveToFirst();
+ mimeType = cursor.getString(0);
+ }
+
+ if (isVideoMimeType(mimeType)) {
+ return generateVideoView(uri, params);
+ } else {
+ return generateImageView(uri, params);
+ }
+ }
+
+ private static boolean isVideoMimeType(@Nullable String mimeType) {
+ if (mimeType == null) {
+ return false;
+ }
+ return startsWithIgnoreCase(mimeType, "video/");
+ }
+
+ /**
+ * Variant of {@link String#startsWith(String)} but which tests with case-insensitivity.
+ */
+ private static boolean startsWithIgnoreCase(@Nullable String target, @Nullable String other) {
+ if (target == null || other == null) {
+ return false;
+ }
+ if (other.length() >= target.length()) {
+ return false;
+ }
+ return target.regionMatches(true, 0, other, 0, other.length());
+ }
+}