Snap for 8426163 from 376034879ecde474509d474832abd8ee6483f549 to mainline-tzdata2-release
Change-Id: I3565c0c7af4d72f79bf700e106081cd1c6f622be
diff --git a/Android.bp b/Android.bp
index 022dab4..f1c92d5 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,21 +1,4 @@
-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",
- ],
-}
-
android_app {
name: "MediaProvider",
manifest: "AndroidManifest.xml",
@@ -23,21 +6,14 @@
static_libs: [
"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",
- "glide-prebuilt",
],
libs: [
"unsupportedappusage",
"app-compat-annotations",
- "framework-annotations-lib",
"framework-mediaprovider.impl",
- "framework-media.stubs.module_lib",
+ "framework_mediaprovider_annotation",
"framework-statsd",
],
@@ -64,7 +40,6 @@
sdk_version: "module_current",
min_sdk_version: "30",
- target_sdk_version: "30",
certificate: "media",
privileged: true,
@@ -82,12 +57,6 @@
"-Xep:MediaProviderMimeType:ERROR",
],
},
-
- required: ["preinstalled-packages-com.android.providers.media.module.xml"],
-
- lint: {
- strict_updatability_linting: true,
- },
}
// Used by MediaProvider and MediaProviderTests
@@ -127,15 +96,6 @@
genrule {
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",
+ cmd: "$(location stats-log-api-gen) --java $(out) --module mediaprovider --javaPackage com.android.providers.media --javaClass MediaProviderStatsLog",
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",
-}
-
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9352cfe..4fe4843 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -8,8 +8,6 @@
<uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
- <!-- Permission required to prompt for the work profile to be turned on -->
- <uses-permission android:name="android.permission.MODIFY_QUIET_MODE" />
<uses-permission android:name="android.permission.WATCH_APPOPS" />
<uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
@@ -27,31 +25,16 @@
<uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/>
<uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
- <!-- Permissions required for reading device configs -->
- <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/>
-
- <uses-permission android:name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"/>
-
<!-- Permissions required for statsd pull metrics -->
<uses-permission android:name="android.permission.REGISTER_STATS_PULL_ATOM"/>
- <!-- 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:crossProfile="true">
+ android:usesCleartextTraffic="true">
<provider
android:name="com.android.providers.media.MediaProvider"
android:authorities="media"
@@ -71,14 +54,8 @@
</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">
+ <receiver android:name="com.android.providers.media.MediaUpgradeReceiver">
<!-- This broadcast is sent after the core system has finished
booting, before the home app is launched or BOOT_COMPLETED
is sent. -->
@@ -87,8 +64,7 @@
</intent-filter>
</receiver>
- <receiver android:name="com.android.providers.media.MediaReceiver"
- android:exported="true">
+ <receiver android:name="com.android.providers.media.MediaReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
@@ -121,7 +97,6 @@
android:permission="android.permission.BIND_JOB_SERVICE" />
<service android:name="com.android.providers.media.fuse.ExternalStorageServiceImpl"
- android:exported="true"
android:permission="android.permission.BIND_EXTERNAL_STORAGE_SERVICE">
<intent-filter>
<action android:name="android.service.storage.ExternalStorageService" />
@@ -150,39 +125,5 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
-
- <activity
- android:name="com.android.providers.media.photopicker.PhotoPickerActivity"
- android:theme="@style/PickerDefaultTheme"
- android:exported="true"
- android:excludeFromRecents="true"
- android:priority="100" >
- <intent-filter>
- <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>
- <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/res/raw/transcode_compat_manifest b/MODULE_LICENSE_APACHE2
similarity index 100%
rename from res/raw/transcode_compat_manifest
rename to MODULE_LICENSE_APACHE2
diff --git a/OWNERS b/OWNERS
index 79add9e..4e37503 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,3 +1,7 @@
-# Bug component: 95221
-
-include platform/frameworks/base:/core/java/android/os/storage/OWNERS
+jsharkey@android.com
+maco@google.com
+marcone@google.com
+nandana@google.com
+zezeozue@google.com
+corinac@google.com
+sahanas@google.com
\ No newline at end of file
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 5b80e36..c8dbf77 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -3,6 +3,3 @@
[Builtin Hooks Options]
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
-
-[Hook Scripts]
-hidden_api_txt_checksorted_hook = ${REPO_ROOT}/tools/platform-compat/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 87582bb..3af81a2 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -8,9 +8,6 @@
"options": [
{
"exclude-annotation": "androidx.test.filters.LargeTest"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
},
@@ -38,22 +35,11 @@
},
{
"name": "fuse_node_test"
- },
- {
- "name": "CtsMediaProviderTranscodeTests"
}
],
"postsubmit": [
{
"name": "MediaProviderClientTests"
- },
- {
- "name": "CtsAppSecurityHostTestCases",
- "options": [
- {
- "include-filter": "android.appsecurity.cts.ExternalStorageHostTest"
- }
- ]
}
]
}
diff --git a/apex/Android.bp b/apex/Android.bp
index d53560a..801e979 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -1,31 +1,18 @@
-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"],
-}
-
apex {
name: "com.android.mediaprovider",
defaults: ["com.android.mediaprovider-defaults"],
manifest: "apex_manifest.json",
apps: ["MediaProvider"],
- compat_configs: ["media-provider-platform-compat-config"],
+ prebuilts: ["media-provider-platform-compat-config"],
}
apex_defaults {
name: "com.android.mediaprovider-defaults",
- bootclasspath_fragments: ["com.android.mediaprovider-bootclasspath-fragment"],
- prebuilts: ["current_sdkinfo"],
+ java_libs: ["framework-mediaprovider"],
key: "com.android.mediaprovider.key",
certificate: ":com.android.mediaprovider.certificate",
file_contexts: ":com.android.mediaprovider-file_contexts",
min_sdk_version: "30",
- // 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,
}
@@ -39,29 +26,3 @@
name: "com.android.mediaprovider.certificate",
certificate: "com.android.mediaprovider",
}
-
-sdk {
- name: "mediaprovider-module-sdk",
- bootclasspath_fragments: ["com.android.mediaprovider-bootclasspath-fragment"],
-}
-
-// Encapsulate the contributions made by the com.android.mediaprovider to the bootclasspath.
-bootclasspath_fragment {
- name: "com.android.mediaprovider-bootclasspath-fragment",
- contents: ["framework-mediaprovider"],
- apex_available: ["com.android.mediaprovider"],
-
- // The bootclasspath_fragments that provide APIs on which this depends.
- fragments: [
- {
- apex: "com.android.art",
- module: "art-bootclasspath-fragment",
- },
- ],
-
- // 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"],
- },
-}
diff --git a/apex/apex_manifest.json b/apex/apex_manifest.json
index 59c4963..6d8da53 100644
--- a/apex/apex_manifest.json
+++ b/apex/apex_manifest.json
@@ -1,4 +1,4 @@
{
"name": "com.android.mediaprovider",
- "version": 319999910
+ "version": 309999900
}
diff --git a/apex/framework/Android.bp b/apex/framework/Android.bp
index 716f818..14a6cb3 100644
--- a/apex/framework/Android.bp
+++ b/apex/framework/Android.bp
@@ -12,15 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-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"],
-}
-
java_sdk_library {
name: "framework-mediaprovider",
defaults: ["framework-module-defaults"],
@@ -36,8 +27,7 @@
installable: true,
libs: [
- "androidx.annotation_annotation",
- "framework-media.stubs.module_lib",
+ "framework_mediaprovider_annotation",
"unsupportedappusage",
],
@@ -59,3 +49,10 @@
],
path: "java",
}
+
+java_library {
+ name: "framework_mediaprovider_annotation",
+ srcs: [":framework-mediaprovider-annotation-sources"],
+ installable: false,
+ sdk_version: "core_current",
+}
diff --git a/apex/framework/api/current.txt b/apex/framework/api/current.txt
index 76f3da4..52439fb 100644
--- a/apex/framework/api/current.txt
+++ b/apex/framework/api/current.txt
@@ -1,68 +1,8 @@
// 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 @NonNull public abstract android.os.Bundle onGetMediaInfo(@Nullable android.os.Bundle);
- method @NonNull public abstract android.os.ParcelFileDescriptor onOpenMedia(@NonNull String, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException;
- method @NonNull public abstract android.content.res.AssetFileDescriptor onOpenThumbnail(@NonNull String, @NonNull android.graphics.Point, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException;
- method @NonNull public android.database.Cursor onQueryAlbums(@Nullable android.os.Bundle);
- method @NonNull public abstract android.database.Cursor onQueryDeletedMedia(@Nullable android.os.Bundle);
- method @NonNull public abstract android.database.Cursor onQueryMedia(@NonNull String);
- method @NonNull public abstract android.database.Cursor onQueryMedia(@Nullable 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 final class CloudMediaProviderContract {
- field public static final String EXTRA_FILTER_ALBUM = "android.provider.extra.FILTER_ALBUM";
- field public static final String EXTRA_FILTER_MIMETYPE = "android.provider.extra.FILTER_MIMETYPE";
- field public static final String EXTRA_FILTER_SIZE_BYTES = "android.provider.extra.FILTER_SIZE_BYTES";
- field public static final String EXTRA_GENERATION = "android.provider.extra.GENERATION";
- field public static final String EXTRA_PAGE_TOKEN = "android.provider.extra.PAGE_TOKEN";
- 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_MS = "date_taken_ms";
- 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.MediaColumns {
- field public static final String DATE_TAKEN_MS = "date_taken_ms";
- field public static final String DURATION_MS = "duration_ms";
- 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";
- }
-
- public static final class CloudMediaProviderContract.MediaInfo {
- field public static final String MEDIA_COUNT = "media_count";
- field public static final String MEDIA_GENERATION = "media_generation";
- field public static final String MEDIA_VERSION = "media_version";
- }
-
public final class MediaStore {
ctor public MediaStore();
- method public static boolean canManageMedia(@NonNull android.content.Context);
method @NonNull public static android.app.PendingIntent createDeleteRequest(@NonNull android.content.ContentResolver, @NonNull java.util.Collection<android.net.Uri>);
method @NonNull public static android.app.PendingIntent createFavoriteRequest(@NonNull android.content.ContentResolver, @NonNull java.util.Collection<android.net.Uri>, boolean);
method @NonNull public static android.app.PendingIntent createTrashRequest(@NonNull android.content.ContentResolver, @NonNull java.util.Collection<android.net.Uri>, boolean);
@@ -72,41 +12,32 @@
method public static long getGeneration(@NonNull android.content.Context, @NonNull String);
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 @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>);
method public static boolean getRequireOriginal(@NonNull android.net.Uri);
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 isCurrentSystemGallery(@NonNull android.content.ContentResolver, int, @NonNull String);
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_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";
field public static final String AUTHORITY = "media";
field @NonNull public static final android.net.Uri AUTHORITY_URI;
- field public static final String EXTRA_ACCEPT_ORIGINAL_MEDIA_FORMAT = "android.provider.extra.ACCEPT_ORIGINAL_MEDIA_FORMAT";
field public static final String EXTRA_BRIGHTNESS = "android.provider.extra.BRIGHTNESS";
field public static final String EXTRA_DURATION_LIMIT = "android.intent.extra.durationLimit";
field public static final String EXTRA_FINISH_ON_COMPLETION = "android.intent.extra.finishOnCompletion";
field public static final String EXTRA_FULL_SCREEN = "android.intent.extra.fullScreen";
field public static final String EXTRA_MEDIA_ALBUM = "android.intent.extra.album";
field public static final String EXTRA_MEDIA_ARTIST = "android.intent.extra.artist";
- field public static final String EXTRA_MEDIA_CAPABILITIES = "android.provider.extra.MEDIA_CAPABILITIES";
- field public static final String EXTRA_MEDIA_CAPABILITIES_UID = "android.provider.extra.MEDIA_CAPABILITIES_UID";
field public static final String EXTRA_MEDIA_FOCUS = "android.intent.extra.focus";
field public static final String EXTRA_MEDIA_GENRE = "android.intent.extra.genre";
- field @Deprecated public static final String EXTRA_MEDIA_PLAYLIST = "android.intent.extra.playlist";
+ field public static final String EXTRA_MEDIA_PLAYLIST = "android.intent.extra.playlist";
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";
@@ -127,7 +58,6 @@
field public static final String MEDIA_SCANNER_VOLUME = "volume";
field public static final String META_DATA_REVIEW_GALLERY_PREWARM_SERVICE = "android.media.review_gallery_prewarm_service";
field public static final String META_DATA_STILL_IMAGE_CAMERA_PREWARM_SERVICE = "android.media.still_image_camera_preview_service";
- field public static final String QUERY_ARG_INCLUDE_RECENTLY_UNMOUNTED_VOLUMES = "android:query-arg-recently-unmounted-volumes";
field public static final String QUERY_ARG_MATCH_FAVORITE = "android:query-arg-match-favorite";
field public static final String QUERY_ARG_MATCH_PENDING = "android:query-arg-match-pending";
field public static final String QUERY_ARG_MATCH_TRASHED = "android:query-arg-match-trashed";
@@ -184,7 +114,7 @@
field public static final android.net.Uri INTERNAL_CONTENT_URI;
}
- public static final class MediaStore.Audio.Artists.Albums implements android.provider.BaseColumns android.provider.MediaStore.Audio.AlbumColumns {
+ public static final class MediaStore.Audio.Artists.Albums implements android.provider.MediaStore.Audio.AlbumColumns {
ctor public MediaStore.Audio.Artists.Albums();
method public static android.net.Uri getContentUri(String, long);
}
@@ -203,7 +133,6 @@
field public static final String IS_MUSIC = "is_music";
field public static final String IS_NOTIFICATION = "is_notification";
field public static final String IS_PODCAST = "is_podcast";
- field public static final String IS_RECORDING = "is_recording";
field public static final String IS_RINGTONE = "is_ringtone";
field @Deprecated public static final String TITLE_KEY = "title_key";
field public static final String TITLE_RESOURCE_URI = "title_resource_uri";
@@ -249,33 +178,33 @@
field public static final String RECORD_SOUND_ACTION = "android.provider.MediaStore.RECORD_SOUND";
}
- @Deprecated public static final class MediaStore.Audio.Playlists implements android.provider.BaseColumns android.provider.MediaStore.Audio.PlaylistsColumns {
- ctor @Deprecated public MediaStore.Audio.Playlists();
- method @Deprecated public static android.net.Uri getContentUri(String);
- field @Deprecated public static final String CONTENT_TYPE = "vnd.android.cursor.dir/playlist";
- field @Deprecated public static final String DEFAULT_SORT_ORDER = "name";
- field @Deprecated public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/playlist";
- field @Deprecated public static final android.net.Uri EXTERNAL_CONTENT_URI;
- field @Deprecated public static final android.net.Uri INTERNAL_CONTENT_URI;
+ public static final class MediaStore.Audio.Playlists implements android.provider.BaseColumns android.provider.MediaStore.Audio.PlaylistsColumns {
+ ctor public MediaStore.Audio.Playlists();
+ method public static android.net.Uri getContentUri(String);
+ field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/playlist";
+ field public static final String DEFAULT_SORT_ORDER = "name";
+ field public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/playlist";
+ field public static final android.net.Uri EXTERNAL_CONTENT_URI;
+ field public static final android.net.Uri INTERNAL_CONTENT_URI;
}
- @Deprecated public static final class MediaStore.Audio.Playlists.Members implements android.provider.MediaStore.Audio.AudioColumns {
- ctor @Deprecated public MediaStore.Audio.Playlists.Members();
- method @Deprecated public static android.net.Uri getContentUri(String, long);
- method @Deprecated public static boolean moveItem(android.content.ContentResolver, long, int, int);
- field @Deprecated public static final String AUDIO_ID = "audio_id";
- field @Deprecated public static final String CONTENT_DIRECTORY = "members";
- field @Deprecated public static final String DEFAULT_SORT_ORDER = "play_order";
- field @Deprecated public static final String PLAYLIST_ID = "playlist_id";
- field @Deprecated public static final String PLAY_ORDER = "play_order";
- field @Deprecated public static final String _ID = "_id";
+ public static final class MediaStore.Audio.Playlists.Members implements android.provider.MediaStore.Audio.AudioColumns {
+ ctor public MediaStore.Audio.Playlists.Members();
+ method public static android.net.Uri getContentUri(String, long);
+ method public static boolean moveItem(android.content.ContentResolver, long, int, int);
+ field public static final String AUDIO_ID = "audio_id";
+ field public static final String CONTENT_DIRECTORY = "members";
+ field public static final String DEFAULT_SORT_ORDER = "play_order";
+ field public static final String PLAYLIST_ID = "playlist_id";
+ field public static final String PLAY_ORDER = "play_order";
+ field public static final String _ID = "_id";
}
- @Deprecated public static interface MediaStore.Audio.PlaylistsColumns extends android.provider.MediaStore.MediaColumns {
+ public static interface MediaStore.Audio.PlaylistsColumns extends android.provider.MediaStore.MediaColumns {
field @Deprecated public static final String DATA = "_data";
- field @Deprecated public static final String DATE_ADDED = "date_added";
- field @Deprecated public static final String DATE_MODIFIED = "date_modified";
- field @Deprecated public static final String NAME = "name";
+ field public static final String DATE_ADDED = "date_added";
+ field public static final String DATE_MODIFIED = "date_modified";
+ field public static final String NAME = "name";
}
public static final class MediaStore.Audio.Radio {
@@ -307,7 +236,7 @@
field public static final int MEDIA_TYPE_DOCUMENT = 6; // 0x6
field public static final int MEDIA_TYPE_IMAGE = 1; // 0x1
field public static final int MEDIA_TYPE_NONE = 0; // 0x0
- field @Deprecated public static final int MEDIA_TYPE_PLAYLIST = 4; // 0x4
+ field public static final int MEDIA_TYPE_PLAYLIST = 4; // 0x4
field public static final int MEDIA_TYPE_SUBTITLE = 5; // 0x5
field public static final int MEDIA_TYPE_VIDEO = 3; // 0x3
field public static final String MIME_TYPE = "mime_type";
@@ -384,7 +313,7 @@
field public static final String CD_TRACK_NUMBER = "cd_track_number";
field public static final String COMPILATION = "compilation";
field public static final String COMPOSER = "composer";
- field public static final String DATA = "_data";
+ field @Deprecated public static final String DATA = "_data";
field public static final String DATE_ADDED = "date_added";
field public static final String DATE_EXPIRES = "date_expires";
field public static final String DATE_MODIFIED = "date_modified";
diff --git a/apex/framework/api/module-lib-current.txt b/apex/framework/api/module-lib-current.txt
index a22e516..d802177 100644
--- a/apex/framework/api/module-lib-current.txt
+++ b/apex/framework/api/module-lib-current.txt
@@ -1,9 +1 @@
// Signature format: 2.0
-package android.provider {
-
- public final class CloudMediaProviderContract {
- field public static final String METHOD_GET_MEDIA_INFO = "android:getMediaInfo";
- }
-
-}
-
diff --git a/apex/framework/api/system-current.txt b/apex/framework/api/system-current.txt
index d29a6ed..5ce4218 100644
--- a/apex/framework/api/system-current.txt
+++ b/apex/framework/api/system-current.txt
@@ -8,7 +8,6 @@
method @WorkerThread public static void waitForIdle(@NonNull android.content.ContentResolver);
field public static final String AUTHORITY_LEGACY = "media_legacy";
field @NonNull public static final android.net.Uri AUTHORITY_LEGACY_URI;
- field public static final String QUERY_ARG_DEFER_SCAN = "android:query-arg-defer-scan";
}
}
diff --git a/apex/framework/java/android/provider/CloudMediaProvider.java b/apex/framework/java/android/provider/CloudMediaProvider.java
deleted file mode 100644
index bf5eddb..0000000
--- a/apex/framework/java/android/provider/CloudMediaProvider.java
+++ /dev/null
@@ -1,450 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.provider;
-
-import static android.provider.CloudMediaProviderContract.METHOD_GET_MEDIA_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_EXACT;
-import static android.provider.CloudMediaProviderContract.URI_PATH_MEDIA_INFO;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-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.Point;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.ParcelFileDescriptor;
-
-import java.io.FileNotFoundException;
-
-/**
- * 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 without changing the provider version as returned by {@link #onGetMediaInfo}.
- *
- * @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_MEDIA_ID = 2;
- private static final int MATCH_DELETED_MEDIAS = 3;
- private static final int MATCH_ALBUMS = 4;
- private static final int MATCH_MEDIA_INFO = 5;
-
- private final UriMatcher mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
- private volatile int mMediaStoreAuthorityAppId;
-
- /**
- * 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_MEDIA_EXACT, MATCH_MEDIA_ID);
- mMatcher.addURI(authority, URI_PATH_DELETED_MEDIA, MATCH_DELETED_MEDIAS);
- mMatcher.addURI(authority, URI_PATH_ALBUM, MATCH_ALBUMS);
- mMatcher.addURI(authority, URI_PATH_MEDIA_INFO, MATCH_MEDIA_INFO);
- }
-
- /**
- * 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_FILTER_ALBUM}
- * </ul>
- *
- * @return {@link Bundle} containing {@link CloudMediaProviderContract.MediaInfo}
- */
- @SuppressWarnings("unused")
- @NonNull
- public abstract Bundle onGetMediaInfo(@Nullable Bundle extras);
-
- /**
- * Returns a {@link Cursor} to a single media item containing the columns representing the media
- * item identified by the {@link CloudMediaProviderContract.MediaColumns#ID} with
- * {@code mediaId}.
- *
- * @param mediaId the media item to return
- * @return cursor representing single media item containing all
- * {@link CloudMediaProviderContract.MediaColumns}
- */
- @SuppressWarnings("unused")
- @NonNull
- public abstract Cursor onQueryMedia(@NonNull String mediaId);
-
- /**
- * 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_MS}, i.e. most recent items first.
- * <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_GENERATION}
- * <li> {@link CloudMediaProviderContract#EXTRA_PAGE_TOKEN}
- * <li> {@link CloudMediaProviderContract#EXTRA_FILTER_ALBUM}
- * </ul>
- * @return cursor representing media items containing all
- * {@link CloudMediaProviderContract.MediaColumns} columns
- */
- @SuppressWarnings("unused")
- @NonNull
- public abstract Cursor onQueryMedia(@Nullable 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 #onGetMediaInfo}. These items can
- * be optionally filtered by {@code extras}.
- * <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_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(@Nullable 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_MS}, i.e. most recent items first.
- * <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_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(@Nullable 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 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
- */
- @SuppressWarnings("unused")
- @NonNull
- public abstract AssetFileDescriptor onOpenThumbnail(@NonNull String mediaId,
- @NonNull Point size, @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 signal used by the OS to signal if the request should be cancelled
- * @return read-only file descriptor for accessing the media file
- *
- * @see #onOpenThumbnail
- */
- @SuppressWarnings("unused")
- @NonNull
- public abstract ParcelFileDescriptor onOpenMedia(@NonNull String mediaId,
- @Nullable CancellationSignal signal) throws FileNotFoundException;
-
- /**
- * 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_INFO.equals(method)) {
- return onGetMediaInfo(extras);
- } else {
- throw new UnsupportedOperationException("Method not supported " + method);
- }
- }
-
- /**
- * 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, signal);
- }
-
- /**
- * Implementation is provided by the parent class. Cannot be overridden.
- *
- * @see #onOpenThumbnail
- * @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 #onOpenThumbnail
- * @see #onOpenMedia
- */
- @NonNull
- @Override
- public final AssetFileDescriptor openTypedAssetFile(
- @NonNull Uri uri, @NonNull String mimeTypeFilter, @Nullable Bundle opts,
- @Nullable CancellationSignal signal) throws FileNotFoundException {
- String mediaId = uri.getLastPathSegment();
- final boolean wantsThumb = (opts != null) && opts.containsKey(ContentResolver.EXTRA_SIZE)
- && mimeTypeFilter.startsWith("image/");
- if (wantsThumb) {
- Point point = (Point) opts.getParcelable(ContentResolver.EXTRA_SIZE);
- return onOpenThumbnail(mediaId, point, signal);
- }
- return new AssetFileDescriptor(onOpenMedia(mediaId, signal), 0 /* startOffset */,
- AssetFileDescriptor.UNKNOWN_LENGTH);
- }
-
- /**
- * 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_MEDIA_ID:
- return onQueryMedia(uri.getLastPathSegment());
- 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");
- }
-}
diff --git a/apex/framework/java/android/provider/CloudMediaProviderContract.java b/apex/framework/java/android/provider/CloudMediaProviderContract.java
deleted file mode 100644
index d8d3eaa..0000000
--- a/apex/framework/java/android/provider/CloudMediaProviderContract.java
+++ /dev/null
@@ -1,406 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.provider;
-
-import android.annotation.SystemApi;
-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_MS
- * @see System#currentTimeMillis()
- */
- public static final String DATE_TAKEN_MS = "date_taken_ms";
-
- /**
- * 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";
-
- /**
- * 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_MS = "duration_ms";
-
- /**
- * 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";
- }
-
- /** 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 provider and a given version.
- * <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_MS
- * @see System#currentTimeMillis()
- */
- public static final String DATE_TAKEN_MS = "date_taken_ms";
-
- /**
- * 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>
- * Type: LONG
- */
- public static final String MEDIA_COUNT = "album_media_count";
- }
-
- /** Constants related to the entire media collection */
- public static final class MediaInfo {
- private MediaInfo() {}
-
- /**
- * Media collection version identifier
- * <p>
- * The only requirement on the value of a version is uniqueness on a device, i.e. a
- * a version should never be reused 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, the version should change so that the OS can clear its cache and more
- * importantly, revoke any URI grants to apps.
- * <p>
- * Apps are recommended to generate unique versions 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 version. With random UUIDs, if data gets cleared, a new one can easily be
- * generated safely.
- * <p>
- * Type: STRING
- *
- * @see CloudMediaProvider#onGetMediaInfo
- */
- public static final String MEDIA_VERSION = "media_version";
-
- /**
- * Maximum generation number of media items in the entire media collection.
- * <p>
- * Providers should associate a monotonically increasing generation number 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_GENERATION
- * @see CloudMediaProvider#onGetMediaInfo
- */
- public static final String MEDIA_GENERATION = "media_generation";
-
- /**
- * Total count of the media items in the entire media collection.
- * <p>
- * Along with the {@link #MEDIA_GENERATION} this helps the OS identify if there have been
- * changes to media items in the media collection.
- * <p>
- * Type: LONG
- *
- * @see CloudMediaProvider#onGetMediaInfo
- */
- public static final String MEDIA_COUNT = "media_count";
- }
-
- /**
- * 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";
-
- /**
- * Generation number to fetch the latest media or album metadata changes from the media
- * collection.
- * <p>
- * The provider should associate a monotonically increasing generation number 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 associate a separate monotonically increasing generation number for album
- * item changes (insertion/deletion/update). Unlike the media generation number, the album
- * generation number should also record insertions and deletions to media items within the
- * album. E.g., a direct change to an albums
- * {@link CloudMediaProviderContract.AlbumColumns#DISPLAY_NAME} will increase the
- * album generation number, likewise adding a photo to that album.
- * <p>
- * Note that multiple media (or album) items can share a generation number 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 generation number if they all occurred within the same
- * database transaction and hence guarantee that a db query result either has all they synced
- * items or none.
- * <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 generation number that are strictly greater
- * than the filter.
- * <p>
- * If the provider supports this filter, it must support the respective
- * {@link CloudMediaProvider#onGetMediaInfo} 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_GENERATION} key to the array of {@link ContentResolver#EXTRA_HONORED_ARGS}
- * as part of the returned {@link Cursor#setExtras} {@link Bundle}.
- *
- * @see MediaInfo#MEDIA_GENERATION
- * @see CloudMediaProvider#onQueryMedia
- * @see CloudMediaProvider#onQueryAlbums
- * @see MediaStore.MediaColumns#GENERATION_MODIFIED
- * <p>
- * Type: LONG
- */
- public static final String EXTRA_GENERATION = "android.provider.extra.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_FILTER_ALBUM}
- * 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_FILTER_ALBUM = "android.provider.extra.FILTER_ALBUM";
-
- /**
- * Limits the query results to only media items matching the give mimetype.
- * <p>
- * The provider should handle an asterisk in the subtype, e.g. {@code image/*} should match
- * {@code image/jpeg} and {@code image/png}.
- * <p>
- * This is only intended for the MediaProvider to implement for cross-user communication. Not
- * for third party apps.
- *
- * @see CloudMediaProvider#onQueryMedia
- * <p>
- * Type: STRING
- */
- public static final String EXTRA_FILTER_MIMETYPE = "android.provider.extra.FILTER_MIMETYPE";
-
- /**
- * 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
- */
- public static final String EXTRA_FILTER_SIZE_BYTES = "android.provider.extra.FILTER_SIZE_BYTES";
-
- /**
- * Constant used to execute {@link CloudMediaProvider#onGetMediaInfo} via
- * {@link ContentProvider#call}.
- *
- * {@hide}
- */
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- public static final String METHOD_GET_MEDIA_INFO = "android:getMediaInfo";
-
- /**
- * URI path for {@link CloudMediaProvider#onQueryMedia}
- *
- * {@hide}
- */
- public static final String URI_PATH_MEDIA = "media";
-
- /**
- * URI path for {@link CloudMediaProvider#onQueryMedia}
- *
- * {@hide}
- */
- public static final String URI_PATH_MEDIA_EXACT = URI_PATH_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#onGetMediaInfo}
- *
- * {@hide}
- */
- public static final String URI_PATH_MEDIA_INFO = "media_info";
-}
diff --git a/apex/framework/java/android/provider/MediaStore.java b/apex/framework/java/android/provider/MediaStore.java
index 92d2187..e49f338 100644
--- a/apex/framework/java/android/provider/MediaStore.java
+++ b/apex/framework/java/android/provider/MediaStore.java
@@ -29,11 +29,9 @@
import android.annotation.SystemApi;
import android.annotation.WorkerThread;
import android.app.Activity;
-import android.app.AppOpsManager;
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;
@@ -41,27 +39,20 @@
import android.content.Context;
import android.content.Intent;
import android.content.UriPermission;
-import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageDecoder;
import android.graphics.PostProcessor;
-import android.media.ApplicationMediaCapabilities;
import android.media.ExifInterface;
import android.media.MediaFormat;
import android.media.MediaMetadataRetriever;
-import android.media.MediaPlayer;
import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Environment;
import android.os.OperationCanceledException;
-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;
@@ -70,8 +61,6 @@
import android.util.Log;
import android.util.Size;
-import androidx.annotation.RequiresApi;
-
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -213,13 +202,6 @@
public static final String GET_MEDIA_URI_CALL = "get_media_uri";
/** {@hide} */
- public static final String GET_REDACTED_MEDIA_URI_CALL = "get_redacted_media_uri";
- /** {@hide} */
- 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 EXTRA_URI = "uri";
/** {@hide} */
public static final String EXTRA_URI_PERMISSIONS = "uriPermissions";
@@ -230,29 +212,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";
-
- /** {@hide} */
- public static final String IS_SYSTEM_GALLERY_CALL = "is_system_gallery";
- /** {@hide} */
- public static final String EXTRA_IS_SYSTEM_GALLERY_UID = "is_system_gallery_uid";
- /** {@hide} */
- public static final String EXTRA_IS_SYSTEM_GALLERY_RESPONSE = "is_system_gallery_response";
-
- /** {@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 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";
/**
* This is for internal use by the media scanner only.
@@ -275,8 +234,6 @@
/** {@hide} */
public static final String PARAM_LIMIT = "limit";
- private static final int DEFAULT_USER_ID = UserHandle.myUserId();
-
/**
* Activity Action: Launch a music player.
* The activity should be able to play, browse, or manipulate music files stored on the device.
@@ -374,13 +331,7 @@
public static final String EXTRA_MEDIA_GENRE = "android.intent.extra.genre";
/**
* The name of the Intent-extra used to define the playlist.
- *
- * @deprecated Android playlists are now deprecated. We will keep the current
- * functionality for compatibility resons, but we will no longer take feature
- * request. We do not advise adding new usages of Android Playlists. M3U files can
- * be used as an alternative.
*/
- @Deprecated
public static final String EXTRA_MEDIA_PLAYLIST = "android.intent.extra.playlist";
/**
* The name of the Intent-extra used to define the radio channel.
@@ -495,17 +446,13 @@
* supply the uri through the EXTRA_OUTPUT field for compatibility with old applications.
* If you don't set a ClipData, it will be copied there for you when calling
* {@link Context#startActivity(Intent)}.
- * <p>
- * Regardless of whether or not EXTRA_OUTPUT is present, when an image is captured via this
- * intent, {@link android.hardware.Camera#ACTION_NEW_PICTURE} won't be broadcasted.
- * <p>
- * Note: if you app targets {@link android.os.Build.VERSION_CODES#M M} and above
+ *
+ * <p>Note: if you app targets {@link android.os.Build.VERSION_CODES#M M} and above
* and declares as using the {@link android.Manifest.permission#CAMERA} permission which
* is not granted, then attempting to use this action will result in a {@link
* java.lang.SecurityException}.
*
* @see #EXTRA_OUTPUT
- * @see android.hardware.Camera#ACTION_NEW_PICTURE
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public final static String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
@@ -530,13 +477,9 @@
* supply the uri through the EXTRA_OUTPUT field for compatibility with old applications.
* If you don't set a ClipData, it will be copied there for you when calling
* {@link Context#startActivity(Intent)}.
- * <p>
- * Regardless of whether or not EXTRA_OUTPUT is present, when an image is captured via this
- * intent, {@link android.hardware.Camera#ACTION_NEW_PICTURE} won't be broadcasted.
*
* @see #ACTION_IMAGE_CAPTURE
* @see #EXTRA_OUTPUT
- * @see android.hardware.Camera#ACTION_NEW_PICTURE
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_IMAGE_CAPTURE_SECURE =
@@ -549,20 +492,14 @@
* The caller may pass in an extra EXTRA_VIDEO_QUALITY to control the video quality.
* <p>
* The caller may pass in an extra EXTRA_OUTPUT to control
- * where the video is written.
- * <ul>
- * <li>If EXTRA_OUTPUT is not present, the video will be written to the standard location
- * for videos, and the Uri of that location will be returned in the data field of the Uri.
- * {@link android.hardware.Camera#ACTION_NEW_VIDEO} will also be broadcasted when the video
- * is recorded.
- * <li>If EXTRA_OUTPUT is assigned a Uri value, no
- * {@link android.hardware.Camera#ACTION_NEW_VIDEO} will be broadcasted. As of
- * {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this uri can also be
- * supplied through {@link android.content.Intent#setClipData(ClipData)}. If using this
- * approach, you still must supply the uri through the EXTRA_OUTPUT field for compatibility
- * with old applications. If you don't set a ClipData, it will be copied there for you when
- * calling {@link Context#startActivity(Intent)}.
- * </ul>
+ * where the video is written. If EXTRA_OUTPUT is not present the video will be
+ * written to the standard location for videos, and the Uri of that location will be
+ * returned in the data field of the Uri.
+ * As of {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this uri can also be supplied through
+ * {@link android.content.Intent#setClipData(ClipData)}. If using this approach, you still must
+ * supply the uri through the EXTRA_OUTPUT field for compatibility with old applications.
+ * If you don't set a ClipData, it will be copied there for you when calling
+ * {@link Context#startActivity(Intent)}.
*
* <p>Note: if you app targets {@link android.os.Build.VERSION_CODES#M M} and above
* and declares as using the {@link android.Manifest.permission#CAMERA} permission which
@@ -573,7 +510,6 @@
* @see #EXTRA_VIDEO_QUALITY
* @see #EXTRA_SIZE_LIMIT
* @see #EXTRA_DURATION_LIMIT
- * @see android.hardware.Camera#ACTION_NEW_VIDEO
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public final static String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE";
@@ -651,106 +587,6 @@
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 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 Intent#EXTRA_ALLOW_MULTIPLE} to indicate this. When multiple
- * selection is enabled, callers can also constrain number of selection
- * {@link MediaStore#EXTRA_PICK_IMAGES_MAX}.
- * <p>
- * When the caller requests {@link Intent#EXTRA_ALLOW_MULTIPLE}, and
- * doesn't request {@link MediaStore#EXTRA_PICK_IMAGES_MAX} or value of
- * {@link MediaStore#EXTRA_PICK_IMAGES_MAX} exceeds the default maximum,
- * then number of selection will be restricted to a default maximum of 100
- * items.
- * <p>
- * Output: MediaStore content URI(s) of the item(s) that was picked.
- */
- @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_PICK_IMAGES = "android.provider.action.PICK_IMAGES";
-
- /**
- * The name of an optional intent-extra used to 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.
- * The value of this intext-extra should be a non-negative integer greater
- * than or equal to 1, the value is ignored otherwise.
- */
- public final static String EXTRA_PICK_IMAGES_MAX = "android.provider.extra.PICK_IMAGES_MAX";
-
- /**
- * Specify that the caller wants to receive the original media format without transcoding.
- *
- * <b>Caution: using this flag can cause app
- * compatibility issues whenever Android adds support for new media formats.</b>
- * Clients should instead specify their supported media capabilities explicitly
- * in their manifest or with the {@link #EXTRA_MEDIA_CAPABILITIES} {@code open} flag.
- *
- * This option is useful for apps that don't attempt to parse the actual byte contents of media
- * files, such as playback using {@link MediaPlayer} or for off-device backup. Note that the
- * {@link android.Manifest.permission#ACCESS_MEDIA_LOCATION} permission will still be required
- * to avoid sensitive metadata redaction, similar to {@link #setRequireOriginal(Uri)}.
- * </ul>
- *
- * Note that this flag overrides any explicitly declared {@code media_capabilities.xml} or
- * {@link ApplicationMediaCapabilities} extras specified in the same {@code open} request.
- *
- * <p>This option can be added to the {@code opts} {@link Bundle} in various
- * {@link ContentResolver} {@code open} methods.
- *
- * @see ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)
- * @see ContentResolver#openTypedAssetFile(Uri, String, Bundle, CancellationSignal)
- * @see #setRequireOriginal(Uri)
- * @see MediaStore#getOriginalMediaFormatFileDescriptor(Context, ParcelFileDescriptor)
- */
- public final static String EXTRA_ACCEPT_ORIGINAL_MEDIA_FORMAT =
- "android.provider.extra.ACCEPT_ORIGINAL_MEDIA_FORMAT";
-
- /**
- * Specify the {@link ApplicationMediaCapabilities} that should be used while opening a media.
- *
- * If the capabilities specified matches the format of the original file, the app will receive
- * the original file, otherwise, it will get transcoded to a default supported format.
- *
- * This flag takes higher precedence over the applications declared
- * {@code media_capabilities.xml} and is useful for apps that want to have more granular control
- * over their supported media capabilities.
- *
- * <p>This option can be added to the {@code opts} {@link Bundle} in various
- * {@link ContentResolver} {@code open} methods.
- *
- * @see ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)
- * @see ContentResolver#openTypedAssetFile(Uri, String, Bundle, CancellationSignal)
- */
- public final static String EXTRA_MEDIA_CAPABILITIES =
- "android.provider.extra.MEDIA_CAPABILITIES";
-
- /**
- * Specify the UID of the app that should be used to determine supported media capabilities
- * while opening a media.
- *
- * If this specified UID is found to be capable of handling the original media file format, the
- * app will receive the original file, otherwise, the file will get transcoded to a default
- * format supported by the specified UID.
- */
- public static final String EXTRA_MEDIA_CAPABILITIES_UID =
- "android.provider.extra.MEDIA_CAPABILITIES_UID";
-
- /**
* 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.
@@ -789,35 +625,11 @@
* only be used when {@link ContentResolver#update} operation needs to
* return early without updating metadata for the file. This may make other
* apps see incomplete metadata for the updated file as scan runs
- * asynchronously here.
- * Note that when this flag is set, the published file will not appear in
- * default query until the deferred scan is complete.
- * Most apps shouldn't set this flag.
+ * asynchronously here. Most apps shouldn't set this flag.
*
* @hide
*/
- @SystemApi
- public static final String QUERY_ARG_DEFER_SCAN = "android:query-arg-defer-scan";
-
- /**
- * Flag that requests {@link ContentResolver#query} to include content from
- * recently unmounted volumes.
- * <p>
- * When the flag is set, {@link ContentResolver#query} will return content
- * from all volumes(i.e., both mounted and recently unmounted volume whose
- * content is still held by MediaProvider).
- * <p>
- * Note that the query result doesn't provide any hint for content from
- * unmounted volume. It's strongly recommended to use default query to
- * avoid accessing/operating on the content that are not available on the
- * device.
- * <p>
- * The flag is useful for apps which manage their own database and
- * query MediaStore in order to synchronize between MediaStore database
- * and their own database.
- */
- public static final String QUERY_ARG_INCLUDE_RECENTLY_UNMOUNTED_VOLUMES =
- "android:query-arg-recently-unmounted-volumes";
+ public static final String QUERY_ARG_DO_ASYNC_SCAN = "android:query-arg-do-async-scan";
/**
* Specify how {@link MediaColumns#IS_PENDING} items should be filtered when
@@ -963,34 +775,6 @@
}
/**
- * Returns {@link ParcelFileDescriptor} representing the original media file format for
- * {@code fileDescriptor}.
- *
- * <p>Media files may get transcoded based on an application's media capabilities requirements.
- * However, in various cases, when the application needs access to the original media file, or
- * doesn't attempt to parse the actual byte contents of media files, such as playback using
- * {@link MediaPlayer} or for off-device backup, this method can be useful.
- *
- * <p>This method is applicable only for media files managed by {@link MediaStore}.
- *
- * <p>The method returns the original file descriptor with the same permission that the caller
- * has for the input file descriptor.
- *
- * @throws IOException if the given {@link ParcelFileDescriptor} could not be converted
- *
- * @see MediaStore#EXTRA_ACCEPT_ORIGINAL_MEDIA_FORMAT
- */
- public static @NonNull ParcelFileDescriptor getOriginalMediaFormatFileDescriptor(
- @NonNull Context context,
- @NonNull ParcelFileDescriptor fileDescriptor) throws IOException {
- Bundle input = new Bundle();
- input.putParcelable(EXTRA_FILE_DESCRIPTOR, fileDescriptor);
-
- return context.getContentResolver().openTypedAssetFileDescriptor(Files.EXTERNAL_CONTENT_URI,
- "*/*", input).getParcelFileDescriptor();
- }
-
- /**
* Rewrite the given {@link Uri} to point at
* {@link MediaStore#AUTHORITY_LEGACY}.
*
@@ -1220,21 +1004,28 @@
/**
* Absolute filesystem path to the media item on disk.
* <p>
- * Apps may use this path to do file operations. However, they should not assume that the
- * file is always available. Apps must be prepared to handle any file-based I/O errors that
- * could occur.
+ * On Android 11, you can use this value when you access an existing
+ * file using direct file paths. That's because this value has a valid
+ * file path. However, don't assume that the file is always available.
+ * Be prepared to handle any file-based I/O errors that could occur.
* <p>
- * From Android 11 onwards, this column is read-only for apps that target
- * {@link android.os.Build.VERSION_CODES#R R} and higher. On those devices, when creating or
- * updating a uri, this column's value is not accepted. Instead, to update the
- * filesystem location of a file, use the values of the {@link #DISPLAY_NAME} and
+ * Don't use this value when you create or update a media file, even
+ * if you're on Android 11 and are using direct file paths. Instead,
+ * use the values of the {@link #DISPLAY_NAME} and
* {@link #RELATIVE_PATH} columns.
* <p>
- * Though direct file operations are supported,
- * {@link ContentResolver#openFileDescriptor(Uri, String)} API is recommended for better
- * performance.
+ * Note that apps may not have filesystem permissions to directly access
+ * this path. Instead of trying to open this path directly, apps should
+ * use {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
+ * access.
*
+ * @deprecated Apps may not have filesystem permissions to directly
+ * access this path. Instead of trying to open this path
+ * directly, apps should use
+ * {@link ContentResolver#openFileDescriptor(Uri, String)}
+ * to gain access.
*/
+ @Deprecated
@Column(Cursor.FIELD_TYPE_STRING)
public static final String DATA = "_data";
@@ -1818,7 +1609,7 @@
* The MTP storage ID of the file
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage
@Deprecated
// @Column(Cursor.FIELD_TYPE_INTEGER)
public static final String STORAGE_ID = "storage_id";
@@ -1893,13 +1684,7 @@
/**
* Constant for the {@link #MEDIA_TYPE} column indicating that file
* is a playlist file.
- *
- * @deprecated Android playlists are now deprecated. We will keep the current
- * functionality for compatibility reasons, but we will no longer take
- * feature request. We do not advise adding new usages of Android Playlists.
- * M3U files can be used as an alternative.
*/
- @Deprecated
public static final int MEDIA_TYPE_PLAYLIST = 4;
/**
@@ -1956,68 +1741,6 @@
* @hide
*/
public static final int _MODIFIER_MEDIA_SCAN = 3;
-
- /**
- * Constant for the {@link #_MODIFIER} column indicating
- * that the last modifier of the database row is explicit
- * {@link ContentResolver} operation and is waiting for metadata
- * update.
- * @hide
- */
- public static final int _MODIFIER_CR_PENDING_METADATA = 4;
-
- /**
- * Status of the transcode file
- *
- * For apps that do not support modern media formats for video, we
- * seamlessly transcode the file and return transcoded file for
- * both file path and ContentResolver operations. This column tracks
- * the status of the transcoded file.
- *
- * @hide
- */
- // @Column(value = Cursor.FIELD_TYPE_INTEGER)
- public static final String _TRANSCODE_STATUS = "_transcode_status";
-
- /**
- * Constant for the {@link #_TRANSCODE_STATUS} column indicating
- * that the transcode file if exists is empty or never transcoded.
- * @hide
- */
- public static final int TRANSCODE_EMPTY = 0;
-
- /**
- * Constant for the {@link #_TRANSCODE_STATUS} column indicating
- * that the transcode file if exists contains transcoded video.
- * @hide
- */
- public static final int TRANSCODE_COMPLETE = 1;
-
- /**
- * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_VIDEO_CODEC_TYPE}
- * extracted from the video file. This value be null for non-video files.
- *
- * @hide
- */
- // @Column(value = Cursor.FIELD_TYPE_INTEGER)
- public static final String _VIDEO_CODEC_TYPE = "_video_codec_type";
-
- /**
- * Redacted Uri-ID corresponding to this DB entry. The value will be null if no
- * redacted uri has ever been created for this uri.
- *
- * @hide
- */
- // @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
- public static final String REDACTED_URI_ID = "redacted_uri_id";
-
- /**
- * Indexed value of {@link UserIdInt} to which the file belongs.
- *
- * @hide
- */
- // @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
- public static final String _USER_ID = "_user_id";
}
}
@@ -2667,10 +2390,23 @@
/**
* Path to the thumbnail file on disk.
+ * <p>
+ * Note that apps may not have filesystem permissions to directly
+ * access this path. Instead of trying to open this path directly,
+ * apps should use
+ * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
+ * access.
*
* As of {@link android.os.Build.VERSION_CODES#Q}, this thumbnail
* has correct rotation, don't need to rotate it again.
+ *
+ * @deprecated Apps may not have filesystem permissions to directly
+ * access this path. Instead of trying to open this path
+ * directly, apps should use
+ * {@link ContentResolver#loadThumbnail}
+ * to gain access.
*/
+ @Deprecated
@Column(Cursor.FIELD_TYPE_STRING)
public static final String DATA = "_data";
@@ -2845,83 +2581,41 @@
/**
* Non-zero if the audio file is music
- *
- * This is mutually exclusive with {@link #IS_ALARM},
- * {@link #IS_AUDIOBOOK}, {@link #IS_NOTIFICATION},
- * {@link #IS_PODCAST}, {@link #IS_RECORDING},
- * and {@link #IS_RINGTONE}.
*/
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
public static final String IS_MUSIC = "is_music";
/**
* Non-zero if the audio file is a podcast
- *
- * This is mutually exclusive with {@link #IS_ALARM},
- * {@link #IS_AUDIOBOOK}, {@link #IS_MUSIC},
- * {@link #IS_NOTIFICATION}, {@link #IS_RECORDING},
- * and {@link #IS_RINGTONE}.
*/
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
public static final String IS_PODCAST = "is_podcast";
/**
* Non-zero if the audio file may be a ringtone
- *
- * This is mutually exclusive with {@link #IS_ALARM},
- * {@link #IS_AUDIOBOOK}, {@link #IS_MUSIC},
- * {@link #IS_NOTIFICATION}, {@link #IS_PODCAST},
- * and {@link #IS_RECORDING}.
*/
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
public static final String IS_RINGTONE = "is_ringtone";
/**
* Non-zero if the audio file may be an alarm
- *
- * This is mutually exclusive with {@link #IS_AUDIOBOOK},
- * {@link #IS_MUSIC}, {@link #IS_NOTIFICATION},
- * {@link #IS_PODCAST}, {@link #IS_RECORDING},
- * and {@link #IS_RINGTONE}.
*/
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
public static final String IS_ALARM = "is_alarm";
/**
* Non-zero if the audio file may be a notification sound
- *
- * This is mutually exclusive with {@link #IS_ALARM},
- * {@link #IS_AUDIOBOOK}, {@link #IS_MUSIC},
- * {@link #IS_PODCAST}, {@link #IS_RECORDING},
- * and {@link #IS_RINGTONE}.
*/
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
public static final String IS_NOTIFICATION = "is_notification";
/**
* Non-zero if the audio file is an audiobook
- *
- * This is mutually exclusive with {@link #IS_ALARM},
- * {@link #IS_MUSIC}, {@link #IS_NOTIFICATION},
- * {@link #IS_PODCAST}, {@link #IS_RECORDING}, and
- * {@link #IS_RINGTONE}
*/
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
public static final String IS_AUDIOBOOK = "is_audiobook";
/**
- * Non-zero if the audio file is a voice recording recorded
- * by voice recorder apps
- *
- * This is mutually exclusive with {@link #IS_ALARM},
- * {@link #IS_AUDIOBOOK}, {@link #IS_MUSIC},
- * {@link #IS_NOTIFICATION}, {@link #IS_PODCAST},
- * and {@link #IS_RINGTONE}.
- */
- @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
- public static final String IS_RECORDING = "is_recording";
-
- /**
* The id of the genre the audio file is from, if any
*/
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
@@ -3206,13 +2900,7 @@
/**
* Audio playlist metadata columns.
- *
- * @deprecated Android playlists are now deprecated. We will keep the current
- * functionality for compatibility reasons, but we will no longer take
- * feature request. We do not advise adding new usages of Android Playlists.
- * M3U files can be used as an alternative.
*/
- @Deprecated
public interface PlaylistsColumns extends MediaColumns {
/**
* The name of the playlist
@@ -3222,7 +2910,20 @@
/**
* Path to the playlist file on disk.
+ * <p>
+ * Note that apps may not have filesystem permissions to directly
+ * access this path. Instead of trying to open this path directly,
+ * apps should use
+ * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
+ * access.
+ *
+ * @deprecated Apps may not have filesystem permissions to directly
+ * access this path. Instead of trying to open this path
+ * directly, apps should use
+ * {@link ContentResolver#openFileDescriptor(Uri, String)}
+ * to gain access.
*/
+ @Deprecated
@Column(Cursor.FIELD_TYPE_STRING)
public static final String DATA = "_data";
@@ -3243,13 +2944,7 @@
/**
* Contains playlists for audio files
- *
- * @deprecated Android playlists are now deprecated. We will keep the current
- * functionality for compatibility resons, but we will no longer take
- * feature request. We do not advise adding new usages of Android Playlists.
- * M3U files can be used as an alternative.
*/
- @Deprecated
public static final class Playlists implements BaseColumns,
PlaylistsColumns {
/**
@@ -3448,7 +3143,7 @@
* Sub-directory of each artist containing all albums on which
* a song by the artist appears.
*/
- public static final class Albums implements BaseColumns, AlbumColumns {
+ public static final class Albums implements AlbumColumns {
public static final Uri getContentUri(String volumeName,long artistId) {
return ContentUris
.withAppendedId(Audio.Artists.getContentUri(volumeName), artistId)
@@ -3635,7 +3330,20 @@
public static class Thumbnails implements BaseColumns {
/**
* Path to the thumbnail file on disk.
+ * <p>
+ * Note that apps may not have filesystem permissions to directly
+ * access this path. Instead of trying to open this path directly,
+ * apps should use
+ * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
+ * access.
+ *
+ * @deprecated Apps may not have filesystem permissions to directly
+ * access this path. Instead of trying to open this path
+ * directly, apps should use
+ * {@link ContentResolver#loadThumbnail}
+ * to gain access.
*/
+ @Deprecated
@Column(Cursor.FIELD_TYPE_STRING)
public static final String DATA = "_data";
@@ -3713,7 +3421,7 @@
* @deprecated location details are no longer indexed for privacy
* reasons, and this value is now always {@code null}.
* You can still manually obtain location metadata using
- * {@link MediaMetadataRetriever#METADATA_KEY_LOCATION}.
+ * {@link ExifInterface#getLatLong(float[])}.
*/
@Deprecated
@Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
@@ -3725,7 +3433,7 @@
* @deprecated location details are no longer indexed for privacy
* reasons, and this value is now always {@code null}.
* You can still manually obtain location metadata using
- * {@link MediaMetadataRetriever#METADATA_KEY_LOCATION}.
+ * {@link ExifInterface#getLatLong(float[])}.
*/
@Deprecated
@Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
@@ -3967,7 +3675,14 @@
/**
* Path to the thumbnail file on disk.
+ *
+ * @deprecated Apps may not have filesystem permissions to directly
+ * access this path. Instead of trying to open this path
+ * directly, apps should use
+ * {@link ContentResolver#openFileDescriptor(Uri, String)}
+ * to gain access.
*/
+ @Deprecated
@Column(Cursor.FIELD_TYPE_STRING)
public static final String DATA = "_data";
@@ -4247,16 +3962,13 @@
/**
* Return a {@link MediaStore} Uri that is an equivalent to the given
- * {@link DocumentsProvider} Uri. This only supports {@code ExternalStorageProvider}
- * and {@code MediaDocumentsProvider} Uris.
+ * {@link DocumentsProvider} Uri.
* <p>
* This allows apps with Storage Access Framework permissions to convert
* between {@link MediaStore} and {@link DocumentsProvider} Uris that refer
- * to the same underlying item.
- * Note that this method doesn't grant any new permissions, but it grants the same access to
- * the Media Store Uri as the caller has to the given DocumentsProvider Uri; callers must
- * already hold permissions for documentUri obtained with {@link Intent#ACTION_OPEN_DOCUMENT}
- * or related APIs.
+ * to the same underlying item. Note that this method doesn't grant any new
+ * permissions; callers must already hold permissions obtained with
+ * {@link Intent#ACTION_OPEN_DOCUMENT} or related APIs.
*
* @param documentUri The {@link DocumentsProvider} Uri to convert.
* @return An equivalent {@link MediaStore} Uri. Returns {@code null} if no
@@ -4278,163 +3990,6 @@
}
}
- /**
- * Returns true if the given application is the current system gallery of the device.
- * <p>
- * The system gallery is one app chosen by the OEM that has read & write access to all photos
- * and videos on the device and control over folders in media collections.
- *
- * @param resolver The {@link ContentResolver} used to connect with
- * {@link MediaStore#AUTHORITY}. Typically this value is {@link Context#getContentResolver()}.
- * @param uid The uid to be checked if it is the current system gallery.
- * @param packageName The package name to be checked if it is the current system gallery.
- */
- public static boolean isCurrentSystemGallery(
- @NonNull ContentResolver resolver,
- int uid,
- @NonNull String packageName) {
- Bundle in = new Bundle();
- in.putInt(EXTRA_IS_SYSTEM_GALLERY_UID, uid);
- final Bundle out = resolver.call(AUTHORITY, IS_SYSTEM_GALLERY_CALL, packageName, in);
- 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 ? DEFAULT_USER_ID : Integer.parseInt(userId);
- }
-
- private static Uri maybeAddUserId(@NonNull Uri uri, String userId) {
- if (userId == null) {
- return uri;
- }
-
- return ContentProvider.createContentUriForUser(uri,
- UserHandle.of(Integer.parseInt(userId)));
- }
-
- 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.
- * <p>
- * A redacted Uri can be used to share a file with another application wherein exposing
- * sensitive information in EXIF headers is not desirable.
- * Note:
- * 1. Redacted uris cannot be granted write access and can neither be used to perform any kind
- * of write operations.
- * 2. To get a redacted uri the caller must hold read permission to {@code uri}.
- *
- * @param resolver The {@link ContentResolver} used to connect with
- * {@link MediaStore#AUTHORITY}. Typically this value is gotten from
- * {@link Context#getContentResolver()}
- * @param uri the {@link Uri} Uri to convert
- * @return redacted version of the {@code uri}. Returns {@code null} when the given
- * {@link Uri} could not be found or is unsupported
- * @throws SecurityException if the caller doesn't have the read access to {@code uri}
- * @see #getRedactedUri(ContentResolver, List)
- */
- @Nullable
- public static Uri getRedactedUri(@NonNull ContentResolver resolver, @NonNull Uri uri) {
- final String authority = uri.getAuthority();
- try (ContentProviderClient client = resolver.acquireContentProviderClient(authority)) {
- final Bundle in = new Bundle();
- 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);
- // 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.
- * <p>
- * A redacted Uri can be used to share a file with another application wherein exposing
- * sensitive information in EXIF headers is not desirable.
- * Note:
- * 1. Order of the returned uris follow the order of the {@code uris}.
- * 2. Redacted uris cannot be granted write access and can neither be used to perform any kind
- * of write operations.
- * 3. To get a redacted uri the caller must hold read permission to its corresponding uri.
- *
- * @param resolver The {@link ContentResolver} used to connect with
- * {@link MediaStore#AUTHORITY}. Typically this value is gotten from
- * {@link Context#getContentResolver()}
- * @param uris the list of {@link Uri} Uri to convert
- * @return a list with redacted version of {@code uris}, in the same order. Returns {@code null}
- * 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)
- */
- @NonNull
- public static List<Uri> getRedactedUri(@NonNull ContentResolver resolver,
- @NonNull List<Uri> uris) {
- 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();
- // 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);
- // 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();
- }
- }
-
/** {@hide} */
public static void resolvePlaylistMembers(@NonNull ContentResolver resolver,
@NonNull Uri playlistUri) {
@@ -4484,46 +4039,4 @@
public static void scanVolume(@NonNull ContentResolver resolver, @NonNull String volumeName) {
resolver.call(AUTHORITY, SCAN_VOLUME_CALL, volumeName, null);
}
-
- /**
- * Returns whether the calling app is granted {@link android.Manifest.permission#MANAGE_MEDIA}
- * or not.
- * <p>Declaring the permission {@link android.Manifest.permission#MANAGE_MEDIA} isn't
- * enough to gain the access.
- * <p>To request access, use {@link android.provider.Settings#ACTION_REQUEST_MANAGE_MEDIA}.
- *
- * @param context the request context
- * @return true, the calling app is granted the permission. Otherwise, false
- *
- * @see android.Manifest.permission#MANAGE_MEDIA
- * @see android.provider.Settings#ACTION_REQUEST_MANAGE_MEDIA
- * @see #createDeleteRequest(ContentResolver, Collection)
- * @see #createTrashRequest(ContentResolver, Collection, boolean)
- * @see #createWriteRequest(ContentResolver, Collection)
- */
- @RequiresApi(Build.VERSION_CODES.S)
- public static boolean canManageMedia(@NonNull Context context) {
- Objects.requireNonNull(context);
- final String packageName = context.getOpPackageName();
- final int uid = context.getApplicationInfo().uid;
- final String permission = android.Manifest.permission.MANAGE_MEDIA;
-
- final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
- final int opMode = appOps.unsafeCheckOpNoThrow(AppOpsManager.permissionToOp(permission),
- uid, packageName);
-
- switch (opMode) {
- case AppOpsManager.MODE_DEFAULT:
- return PackageManager.PERMISSION_GRANTED == context.checkPermission(
- permission, android.os.Process.myPid(), uid);
- case AppOpsManager.MODE_ALLOWED:
- return true;
- case AppOpsManager.MODE_ERRORED:
- case AppOpsManager.MODE_IGNORED:
- return false;
- default:
- Log.w(TAG, "Unknown AppOpsManager mode " + opMode);
- return false;
- }
- }
}
diff --git a/apex/hiddenapi/OWNERS b/apex/hiddenapi/OWNERS
deleted file mode 100644
index ac8a2b6..0000000
--- a/apex/hiddenapi/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-# soong-team@ as the hiddenapi files are tightly coupled with Soong
-file:platform/build/soong:/OWNERS
-
-# compat-team@ for changes to hiddenapi files
-file:tools/platform-compat:/OWNERS
diff --git a/apex/hiddenapi/hiddenapi-max-target-o-low-priority.txt b/apex/hiddenapi/hiddenapi-max-target-o-low-priority.txt
deleted file mode 100644
index 7c59c96..0000000
--- a/apex/hiddenapi/hiddenapi-max-target-o-low-priority.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-Landroid/provider/MediaStore$Audio$AudioColumns;->ALBUM_ARTIST:Ljava/lang/String;
-Landroid/provider/MediaStore$Audio$AudioColumns;->COMPILATION:Ljava/lang/String;
-Landroid/provider/MediaStore$Audio$AudioColumns;->GENRE:Ljava/lang/String;
-Landroid/provider/MediaStore$Audio$AudioColumns;->TITLE_RESOURCE_URI:Ljava/lang/String;
-Landroid/provider/MediaStore$Audio$Media;->EXTERNAL_PATHS:[Ljava/lang/String;
-Landroid/provider/MediaStore$Audio$Radio;-><init>()V
-Landroid/provider/MediaStore$Files;->getDirectoryUri(Ljava/lang/String;)Landroid/net/Uri;
-Landroid/provider/MediaStore$Images$Media;->StoreThumbnail(Landroid/content/ContentResolver;Landroid/graphics/Bitmap;JFFI)Landroid/graphics/Bitmap;
-Landroid/provider/MediaStore$InternalThumbnails;-><init>()V
-Landroid/provider/MediaStore$InternalThumbnails;->cancelThumbnailRequest(Landroid/content/ContentResolver;JLandroid/net/Uri;J)V
-Landroid/provider/MediaStore$InternalThumbnails;->DEFAULT_GROUP_ID:I
-Landroid/provider/MediaStore$InternalThumbnails;->FULL_SCREEN_KIND:I
-Landroid/provider/MediaStore$InternalThumbnails;->getMiniThumbFromFile(Landroid/database/Cursor;Landroid/net/Uri;Landroid/content/ContentResolver;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;
-Landroid/provider/MediaStore$InternalThumbnails;->getThumbnail(Landroid/content/ContentResolver;JJILandroid/graphics/BitmapFactory$Options;Landroid/net/Uri;Z)Landroid/graphics/Bitmap;
-Landroid/provider/MediaStore$InternalThumbnails;->MICRO_KIND:I
-Landroid/provider/MediaStore$InternalThumbnails;->MINI_KIND:I
-Landroid/provider/MediaStore$InternalThumbnails;->PROJECTION:[Ljava/lang/String;
-Landroid/provider/MediaStore$InternalThumbnails;->sThumbBuf:[B
-Landroid/provider/MediaStore$InternalThumbnails;->sThumbBufLock:Ljava/lang/Object;
-Landroid/provider/MediaStore$MediaColumns;->MEDIA_SCANNER_NEW_OBJECT_ID:Ljava/lang/String;
-Landroid/provider/MediaStore;->CONTENT_AUTHORITY_SLASH:Ljava/lang/String;
-Landroid/provider/MediaStore;->getDocumentUri(Landroid/content/ContentResolver;Ljava/lang/String;Ljava/util/List;)Landroid/net/Uri;
-Landroid/provider/MediaStore;->getFilePath(Landroid/content/ContentResolver;Landroid/net/Uri;)Ljava/lang/String;
-Landroid/provider/MediaStore;->PARAM_DELETE_DATA:Ljava/lang/String;
-Landroid/provider/MediaStore;->RETRANSLATE_CALL:Ljava/lang/String;
-Landroid/provider/MediaStore;->TAG:Ljava/lang/String;
-Landroid/provider/MediaStore;->UNHIDE_CALL:Ljava/lang/String;
diff --git a/apex/testing/Android.bp b/apex/testing/Android.bp
index 41612ba..2f6cc4f 100644
--- a/apex/testing/Android.bp
+++ b/apex/testing/Android.bp
@@ -1,12 +1,3 @@
-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"],
-}
-
apex_test {
name: "test_com.android.mediaprovider",
visibility: [
diff --git a/deploy.sh b/deploy.sh
index cc1be8e..e6edcce 100755
--- a/deploy.sh
+++ b/deploy.sh
@@ -8,8 +8,6 @@
adb remount
adb sync
adb shell umount /apex/com.android.mediaprovider*
-adb shell rm -rf /data/apex/active/com.android.mediaprovider*
-adb shell rm -rf /data/apex/decompressed/com.android.mediaprovider*
adb shell setprop apexd.status '""'
adb shell setprop ctl.restart apexd
adb shell rm -rf /system/priv-app/MediaProvider
diff --git a/errorprone/Android.bp b/errorprone/Android.bp
index 3f11f91..317c081 100644
--- a/errorprone/Android.bp
+++ b/errorprone/Android.bp
@@ -1,13 +1,4 @@
-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"],
-}
-
java_plugin {
name: "error_prone_mediaprovider",
@@ -23,14 +14,11 @@
static_libs: [
"//external/error_prone:error_prone_core",
- ],
-
- libs: [
- "//external/auto:auto_service_annotations",
+ "//external/dagger2:dagger2-auto-service",
],
plugins: [
- "//external/auto:auto_service_plugin",
+ "//external/dagger2:dagger2-auto-service",
],
javacflags: ["-verbose"],
diff --git a/gen_strings.py b/gen_strings.py
index a3d98be..99bba8a 100755
--- a/gen_strings.py
+++ b/gen_strings.py
@@ -31,6 +31,12 @@
if verb == "write":
verblabel = "modify"
+ verblabelcaps = verblabel[0].upper() + verblabel[1:]
+ if verb == "trash":
+ verblabelcaps = "Move to trash"
+ if verb == "untrash":
+ verblabelcaps = "Move out of trash"
+
print '''
<!-- ========================= %s STRINGS ========================= -->
''' % (verb.upper())
@@ -43,13 +49,6 @@
<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>
''').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>
-''').substitute(vars()).strip("\n")
elif verb == "untrash":
print Template('''
@@ -59,13 +58,6 @@
<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>
''').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>
-''').substitute(vars()).strip("\n")
else:
print Template('''
@@ -75,17 +67,6 @@
<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>
''').substitute(vars()).strip("\n")
- if verb == "write":
- actionLabel = "Modifying"
- else:
- 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>
-''').substitute(vars()).strip("\n")
print '''
<!-- ========================= END AUTO-GENERATED BY gen_strings.py ========================= -->
diff --git a/jni/Android.bp b/jni/Android.bp
index 758bee1..2f7cdb4 100644
--- a/jni/Android.bp
+++ b/jni/Android.bp
@@ -14,15 +14,6 @@
// limitations under the License.
//
-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"],
-}
-
cc_library_shared {
name: "libfuse_jni",
diff --git a/jni/FuseDaemon.cpp b/jni/FuseDaemon.cpp
index 53f86cd..5ce73b1 100755
--- a/jni/FuseDaemon.cpp
+++ b/jni/FuseDaemon.cpp
@@ -79,7 +79,7 @@
// logging macros to avoid duplication.
#define TRACE_NODE(__node, __req) \
LOG(VERBOSE) << __FUNCTION__ << " : " << #__node << " = [" << get_name(__node) \
- << "] (uid=" << (__req)->ctx.uid << ") "
+ << "] (uid=" << __req->ctx.uid << ") "
#define ATRACE_NAME(name) ScopedTrace ___tracer(name)
#define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
@@ -109,17 +109,10 @@
// Stolen from: UserManagerService
constexpr int MAX_USER_ID = UINT32_MAX / PER_USER_RANGE;
-const int MY_UID = getuid();
-const int MY_USER_ID = MY_UID / PER_USER_RANGE;
-const std::string MY_USER_ID_STRING(std::to_string(MY_UID / PER_USER_RANGE));
-
// Regex copied from FileUtils.java in MediaProvider, but without media directory.
const std::regex PATTERN_OWNED_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";
+ "^/storage/[^/]+/(?:[0-9]+/)?Android/(?:data|obb|sandbox)/([^/]+)(/?.*)?",
+ std::regex_constants::icase);
/*
* In order to avoid double caching with fuse, call fadvise on the file handles
@@ -247,28 +240,23 @@
/* Single FUSE mount */
struct fuse {
- explicit fuse(const std::string& _path, const ino_t _ino,
- const std::vector<string>& _supported_transcoding_relative_paths)
+ explicit fuse(const std::string& _path)
: path(_path),
tracker(mediaprovider::fuse::NodeTracker(&lock)),
- root(node::CreateRoot(_path, &lock, _ino, &tracker)),
+ root(node::CreateRoot(_path, &lock, &tracker)),
mp(0),
zero_addr(0),
- disable_dentry_cache(false),
- passthrough(false),
- supported_transcoding_relative_paths(_supported_transcoding_relative_paths) {}
+ disable_dentry_cache(false) {}
inline bool IsRoot(const node* node) const { return node == root; }
inline string GetEffectiveRootPath() {
- if (android::base::StartsWith(path, "/storage/emulated")) {
- return path + "/" + MY_USER_ID_STRING;
+ if (path.find("/storage/emulated", 0) == 0) {
+ return path + "/" + std::to_string(getuid() / PER_USER_RANGE);
}
return path;
}
- inline string GetTransformsDir() { return GetEffectiveRootPath() + "/.transforms"; }
-
// Note that these two (FromInode / ToInode) conversion wrappers are required
// because fuse_lowlevel_ops documents that the root inode is always one
// (see FUSE_ROOT_ID in fuse_lowlevel.h). There are no particular requirements
@@ -289,22 +277,6 @@
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;
- }
-
std::recursive_mutex lock;
const string path;
// The Inode tracker associated with this FUSE instance.
@@ -329,14 +301,8 @@
std::atomic_bool* active;
std::atomic_bool disable_dentry_cache;
- std::atomic_bool passthrough;
- // FUSE device id.
- std::atomic_uint dev;
- const std::vector<string> supported_transcoding_relative_paths;
};
-enum class FuseOp { lookup, readdir, mknod, mkdir, create };
-
static inline string get_name(node* n) {
if (n) {
std::string name = IS_OS_DEBUGABLE ? "real_path: " + n->BuildPath() + " " : "";
@@ -411,7 +377,7 @@
// 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)) {
+ if (mediaprovider::fuse::containsMount(path, std::to_string(getuid() / PER_USER_RANGE))) {
LOG(WARNING) << "Ignoring attempt to invalidate dentry for FUSE mounts";
return;
}
@@ -423,145 +389,54 @@
}
}
-static double get_entry_timeout(const string& path, node* node, struct fuse* fuse) {
+static double get_timeout(struct fuse* fuse, const string& path, bool should_inval) {
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 || path.find(media_path, 0) == 0 || is_package_owned_path(path, fuse->path)) {
// We set dentry timeout to 0 for the following reasons:
- // 1. The dentry cache was completely disabled
- // 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.
+ // 1. Case-insensitive lookups need to invalidate other case-insensitive dentry matches
+ // 2. 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.
// 3. With app data isolation enabled, app A should not guess existence of app B from the
// Android/{data,obb}/<package> paths, hence we prevent the kernel from caching that
// information.
- // 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.
return 0;
}
return std::numeric_limits<double>::max();
}
-static std::string get_path(node* node) {
- const string& io_path = node->GetIoPath();
- return io_path.empty() ? node->BuildPath() : io_path;
-}
-
-// Returns true if the path resides under .transforms/synthetic.
-// NOTE: currently only file paths corresponding to redacted URIs reside under this folder. The path
-// itself never exists and just a link for transformation.
-static inline bool is_synthetic_path(const string& path, struct fuse* fuse) {
- return android::base::StartsWithIgnoreCase(
- path, fuse->GetTransformsDir() + "/" + TRANSFORM_SYNTHETIC_DIR);
-}
-
-static inline bool is_transforms_dir_path(const string& path, struct fuse* fuse) {
- return android::base::StartsWithIgnoreCase(path, fuse->GetTransformsDir());
-}
-
-static std::unique_ptr<mediaprovider::fuse::FileLookupResult> validate_node_path(
- const std::string& path, const std::string& name, fuse_req_t req, int* error_code,
- struct fuse_entry_param* e, const FuseOp op) {
- struct fuse* fuse = get_fuse(req);
- const struct fuse_ctx* ctx = fuse_req_ctx(req);
- memset(e, 0, sizeof(*e));
-
- const bool synthetic_path = is_synthetic_path(path, fuse);
- if (lstat(path.c_str(), &e->attr) < 0 && !(op == FuseOp::lookup && synthetic_path)) {
- *error_code = errno;
- return nullptr;
- }
-
- if (is_transforms_dir_path(path, fuse)) {
- if (op == FuseOp::lookup) {
- // Lookups are only allowed under .transforms/synthetic dir
- if (!(android::base::EqualsIgnoreCase(path, fuse->GetTransformsDir()) ||
- android::base::StartsWithIgnoreCase(
- path, fuse->GetTransformsDir() + "/" + TRANSFORM_SYNTHETIC_DIR))) {
- *error_code = ENONET;
- return nullptr;
- }
- } else {
- // user-code is only allowed to make lookups under .transforms dir, and that too only
- // under .transforms/synthetic dir
- *error_code = ENOENT;
- return nullptr;
- }
- }
-
- if (S_ISDIR(e->attr.st_mode)) {
- // now that we have reached this point, ops on directories are safe and require no
- // transformation.
- return std::make_unique<mediaprovider::fuse::FileLookupResult>(0, 0, 0, true, false, "");
- }
-
- 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, "");
- }
-
- // Handle potential file transforms
- std::unique_ptr<mediaprovider::fuse::FileLookupResult> file_lookup_result =
- fuse->mp->FileLookup(path, req->ctx.uid, req->ctx.pid);
-
- 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;
- 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)) {
- *error_code = errno;
- return nullptr;
- }
-
- return file_lookup_result;
-}
-
static node* make_node_entry(fuse_req_t req, node* parent, const string& name, const string& path,
- struct fuse_entry_param* e, int* error_code, const FuseOp op) {
+ struct fuse_entry_param* e, int* error_code) {
struct fuse* fuse = get_fuse(req);
const struct fuse_ctx* ctx = fuse_req_ctx(req);
node* node;
memset(e, 0, sizeof(*e));
-
- std::unique_ptr<mediaprovider::fuse::FileLookupResult> file_lookup_result =
- validate_node_path(path, name, req, error_code, e, op);
- if (!file_lookup_result) {
- // Fail lookup if we can't validate |path, |errno| would have already been set
- return nullptr;
+ if (lstat(path.c_str(), &e->attr) < 0) {
+ *error_code = errno;
+ return NULL;
}
- const 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;
-
- node = parent->LookupChildByName(name, true /* acquire */, transforms);
+ bool should_inval = false;
+ node = parent->LookupChildByName(name, true /* acquire */);
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)) {
+ node = ::node::Create(parent, name, &fuse->lock, &fuse->tracker);
+ } else if (!mediaprovider::fuse::containsMount(path, std::to_string(getuid() / PER_USER_RANGE))) {
+ should_inval = node->HasCaseInsensitiveMatch();
// Only invalidate a path if it does not contain mount.
// 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
+ // |should_inval| 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
if (name != node->GetName()) {
+ should_inval = true;
// 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();
+ node->SetCaseInsensitiveMatch();
// 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
@@ -572,12 +447,6 @@
std::thread t([=]() { fuse_inval(fuse->se, parent_ino, child_ino, node_name, path); });
t.detach();
}
-
- // This updated value allows us correctly decide if to keep_cache and use direct_io during
- // FUSE_OPEN. Between the last lookup and this lookup, we might have deleted a cached
- // transcoded file on the lower fs. A subsequent transcode at FUSE_READ should ensure we
- // don't reuse any stale transcode page cache content.
- node->SetTransformsComplete(transforms_complete);
}
TRACE_NODE(node, req);
@@ -587,8 +456,11 @@
// 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();
+ e->entry_timeout = get_timeout(fuse, path, should_inval);
+ e->attr_timeout = is_package_owned_path(path, fuse->path) || should_inval
+ ? 0
+ : std::numeric_limits<double>::max();
+
return node;
}
@@ -607,49 +479,15 @@
*/
static void pf_init(void* userdata, struct fuse_conn_info* conn) {
- struct fuse* fuse = reinterpret_cast<struct fuse*>(userdata);
-
// We don't want a getattr request with every read request
conn->want &= ~FUSE_CAP_AUTO_INVAL_DATA & ~FUSE_CAP_READDIRPLUS_AUTO;
unsigned mask = (FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE | FUSE_CAP_SPLICE_READ |
FUSE_CAP_ASYNC_READ | FUSE_CAP_ATOMIC_O_TRUNC | FUSE_CAP_WRITEBACK_CACHE |
FUSE_CAP_EXPORT_SUPPORT | FUSE_CAP_FLOCK_LOCKS);
-
- bool disable_splice_write = false;
- if (fuse->passthrough) {
- if (conn->capable & FUSE_CAP_PASSTHROUGH) {
- mask |= FUSE_CAP_PASSTHROUGH;
-
- // SPLICE_WRITE seems to cause linux kernel cache corruption with passthrough enabled.
- // It is still under investigation but while running
- // ScopedStorageDeviceTest#testAccessMediaLocationInvalidation, we notice test flakes
- // of about 1/20 for the following reason:
- // 1. App without ACCESS_MEDIA_LOCATION permission reads redacted bytes via FUSE cache
- // 2. App with ACCESS_MEDIA_LOCATION permission reads non-redacted bytes via passthrough
- // cache
- // (2) fails because bytes from (1) sneak into the passthrough cache??
- // To workaround, we disable splice for write when passthrough is enabled.
- // This shouldn't have any performance regression if comparing passthrough devices to
- // no-passthrough devices for the following reasons:
- // 1. No-op for no-passthrough devices
- // 2. Passthrough devices
- // a. Files not requiring redaction use passthrough which bypasses FUSE_READ entirely
- // b. Files requiring redaction are still faster than no-passthrough devices that use
- // direct_io
- disable_splice_write = true;
- } else {
- LOG(WARNING) << "Passthrough feature not supported by the kernel";
- fuse->passthrough = false;
- }
- }
-
conn->want |= conn->capable & mask;
- if (disable_splice_write) {
- conn->want &= ~FUSE_CAP_SPLICE_WRITE;
- }
-
conn->max_read = MAX_READ_SIZE;
+ struct fuse* fuse = reinterpret_cast<struct fuse*>(userdata);
fuse->active->store(true, std::memory_order_release);
}
@@ -662,7 +500,7 @@
// Return true if the path is accessible for that uid.
static bool is_app_accessible_path(MediaProviderWrapper* mp, const string& path, uid_t uid) {
- if (uid < AID_APP_START || uid == MY_UID) {
+ if (uid < AID_APP_START) {
return true;
}
@@ -697,7 +535,7 @@
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) {
struct fuse* fuse = get_fuse(req);
node* parent_node = fuse->FromInode(parent);
if (!parent_node) {
@@ -712,15 +550,16 @@
return nullptr;
}
+ string child_path = parent_path + "/" + name;
+
TRACE_NODE(parent_node, req);
- const string child_path = parent_path + "/" + name;
std::smatch match;
std::regex_search(child_path, match, storage_emulated_regex);
// Ensure the FuseDaemon user id matches the user id or cross-user lookups are allowed in
// requested path
- if (match.size() == 2 && MY_USER_ID_STRING != match[1].str()) {
+ if (match.size() == 2 && std::to_string(getuid() / PER_USER_RANGE) != match[1].str()) {
// If user id mismatch, check cross-user lookups
long userId = strtol(match[1].str().c_str(), nullptr, 10);
if (userId < 0 || userId > MAX_USER_ID ||
@@ -729,8 +568,7 @@
return nullptr;
}
}
-
- return make_node_entry(req, parent_node, name, child_path, e, error_code, op);
+ return make_node_entry(req, parent_node, name, child_path, e, error_code);
}
static void pf_lookup(fuse_req_t req, fuse_ino_t parent, const char* name) {
@@ -738,7 +576,7 @@
struct fuse_entry_param e;
int error_code = 0;
- if (do_lookup(req, parent, name, &e, &error_code, FuseOp::lookup)) {
+ if (do_lookup(req, parent, name, &e, &error_code)) {
fuse_reply_entry(req, &e);
} else {
CHECK(error_code != 0);
@@ -779,16 +617,6 @@
fuse_reply_none(req);
}
-static void pf_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, off_t length,
- fuse_file_info* fi) {
- ATRACE_CALL();
- struct fuse* fuse = get_fuse(req);
-
- handle* h = reinterpret_cast<handle*>(fi->fh);
- auto err = fallocate(h->fd, mode, offset, length);
- fuse_reply_err(req, err ? errno : 0);
-}
-
static void pf_getattr(fuse_req_t req,
fuse_ino_t ino,
struct fuse_file_info* fi) {
@@ -799,7 +627,7 @@
fuse_reply_err(req, ENOENT);
return;
}
- const string& path = get_path(node);
+ string path = node->BuildPath();
if (!is_app_accessible_path(fuse->mp, path, req->ctx.uid)) {
fuse_reply_err(req, ENOENT);
return;
@@ -811,7 +639,8 @@
if (lstat(path.c_str(), &s) < 0) {
fuse_reply_err(req, errno);
} else {
- fuse_reply_attr(req, &s, std::numeric_limits<double>::max());
+ fuse_reply_attr(req, &s, is_package_owned_path(path, fuse->path) ?
+ 0 : std::numeric_limits<double>::max());
}
}
@@ -827,7 +656,7 @@
fuse_reply_err(req, ENOENT);
return;
}
- const string& path = get_path(node);
+ string path = node->BuildPath();
if (!is_app_accessible_path(fuse->mp, path, req->ctx.uid)) {
fuse_reply_err(req, ENOENT);
return;
@@ -840,16 +669,8 @@
fd = h->fd;
} else {
const struct fuse_ctx* ctx = fuse_req_ctx(req);
- std::unique_ptr<FileOpenResult> result = fuse->mp->OnFileOpen(
- path, path, ctx->uid, ctx->pid, node->GetTransformsReason(), true /* for_write */,
- false /* redact */, false /* log_transforms_metrics */);
-
- if (!result) {
- fuse_reply_err(req, EFAULT);
- return;
- }
-
- if (result->status) {
+ int status = fuse->mp->IsOpenAllowed(path, ctx->uid, true);
+ if (status) {
fuse_reply_err(req, EACCES);
return;
}
@@ -914,14 +735,15 @@
}
lstat(path.c_str(), attr);
- fuse_reply_attr(req, attr, std::numeric_limits<double>::max());
+ fuse_reply_attr(req, attr, is_package_owned_path(path, fuse->path) ?
+ 0 : std::numeric_limits<double>::max());
}
static void pf_canonical_path(fuse_req_t req, fuse_ino_t ino)
{
struct fuse* fuse = get_fuse(req);
node* node = fuse->FromInode(ino);
- const string& path = node ? get_path(node) : "";
+ string path = node ? node->BuildPath() : "";
if (node && is_app_accessible_path(fuse->mp, path, req->ctx.uid)) {
// TODO(b/147482155): Check that uid has access to |path| and its contents
@@ -961,7 +783,7 @@
int error_code = 0;
struct fuse_entry_param e;
- if (make_node_entry(req, parent_node, name, child_path, &e, &error_code, FuseOp::mknod)) {
+ if (make_node_entry(req, parent_node, name, child_path, &e, &error_code)) {
fuse_reply_entry(req, &e);
} else {
CHECK(error_code != 0);
@@ -1005,7 +827,7 @@
int error_code = 0;
struct fuse_entry_param e;
- if (make_node_entry(req, parent_node, name, child_path, &e, &error_code, FuseOp::mkdir)) {
+ if (make_node_entry(req, parent_node, name, child_path, &e, &error_code)) {
fuse_reply_entry(req, &e);
} else {
CHECK(error_code != 0);
@@ -1038,8 +860,12 @@
return;
}
- // TODO(b/169306422): Log each deleted node
- parent_node->SetDeletedForChild(name);
+ node* child_node = parent_node->LookupChildByName(name, false /* acquire */);
+ TRACE_NODE(child_node, req);
+ if (child_node) {
+ child_node->SetDeleted();
+ }
+
fuse_reply_err(req, 0);
}
@@ -1056,14 +882,6 @@
fuse_reply_err(req, ENOENT);
return;
}
-
- if (is_transforms_dir_path(parent_path, fuse)) {
- // .transforms is a special daemon controlled dir so apps shouldn't be able to see it via
- // readdir, and any dir operations attempted on it should fail
- fuse_reply_err(req, ENOENT);
- return;
- }
-
TRACE_NODE(parent_node, req);
const string child_path = parent_path + "/" + name;
@@ -1111,12 +929,6 @@
return ENOENT;
}
- if (is_transforms_dir_path(old_parent_path, fuse)) {
- // .transforms is a special daemon controlled dir so apps shouldn't be able to see it via
- // readdir, and any dir operations attempted on it should fail
- return ENOENT;
- }
-
node* new_parent_node = fuse->FromInode(new_parent);
if (!new_parent_node) return ENOENT;
const string new_parent_path = new_parent_node->BuildPath();
@@ -1134,7 +946,10 @@
TRACE_NODE(old_parent_node, req);
TRACE_NODE(new_parent_node, req);
- const string old_child_path = old_parent_path + "/" + name;
+ node* child_node = old_parent_node->LookupChildByName(name, true /* acquire */);
+ TRACE_NODE(child_node, req) << "old_child";
+
+ const string old_child_path = child_node->BuildPath();
const string new_child_path = new_parent_path + "/" + new_name;
if (android::base::EqualsIgnoreCase(fuse->GetEffectiveRootPath() + "/android", old_child_path)) {
@@ -1147,14 +962,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);
+ child_node->Rename(new_name, new_parent_node);
}
+ TRACE_NODE(child_node, req) << "new_child";
+
+ child_node->Release(1);
return res;
}
@@ -1172,76 +984,40 @@
}
*/
-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,
- int* keep_cache) {
+static handle* create_handle_for_node(struct fuse* fuse, const string& path, int fd, node* node,
+ const RedactionInfo* ri, int* keep_cache) {
std::lock_guard<std::recursive_mutex> guard(fuse->lock);
-
+ // We don't want to use the FUSE VFS cache in two cases:
+ // 1. When redaction is needed because app A with EXIF access might access
+ // a region that should have been redacted for app B without EXIF access, but app B on
+ // a subsequent read, will be able to see the EXIF data because the read request for
+ // that region will be served from cache and not get to the FUSE daemon
+ // 2. When the file has a read or write lock on it. This means that the MediaProvider
+ // has given an fd to the lower file system to an app. There are two cases where using
+ // the cache in this case can be a problem:
+ // a. Writing to a FUSE fd with caching enabled will use the write-back cache and a
+ // subsequent read from the lower fs fd will not see the write.
+ // b. Reading from a FUSE fd with caching enabled may not see the latest writes using
+ // the lower fs fd because those writes did not go through the FUSE layer and reads from
+ // FUSE after that write may be served from cache
+ bool has_redacted = node->HasRedactedCache();
bool redaction_needed = ri->isRedactionNeeded();
- handle* handle = nullptr;
- int transforms = node->GetTransforms();
- bool transforms_complete = node->IsTransformsComplete();
- if (transforms_uid > 0) {
- CHECK(transforms);
- }
+ bool is_redaction_change =
+ (redaction_needed && !has_redacted) || (!redaction_needed && has_redacted);
+ bool is_cached_file_open = node->HasCachedHandle();
- if (fuse->passthrough) {
- *keep_cache = transforms_complete;
- // We only enabled passthrough iff these 2 conditions hold
- // 1. Redaction is not needed
- // 2. Node transforms are completed, e.g transcoding.
- // (2) is important because we transcode lazily (on the first read) and with passthrough,
- // we will never get a read into the FUSE daemon, so passthrough would have returned
- // 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 */,
- !redaction_needed && transforms_complete /* passthrough */, uid,
- transforms_uid);
+ if (!is_cached_file_open && is_redaction_change) {
+ node->SetRedactedCache(redaction_needed);
+ // Purges stale page cache before open
+ *keep_cache = 0;
} else {
- // Without fuse->passthrough, we don't want to use the FUSE VFS cache in two cases:
- // 1. When redaction is needed because app A with EXIF access might access
- // a region that should have been redacted for app B without EXIF access, but app B on
- // a subsequent read, will be able to see the EXIF data because the read request for
- // that region will be served from cache and not get to the FUSE daemon
- // 2. When the file has a read or write lock on it. This means that the MediaProvider
- // has given an fd to the lower file system to an app. There are two cases where using
- // the cache in this case can be a problem:
- // a. Writing to a FUSE fd with caching enabled will use the write-back cache and a
- // subsequent read from the lower fs fd will not see the write.
- // b. Reading from a FUSE fd with caching enabled may not see the latest writes using
- // the lower fs fd because those writes did not go through the FUSE layer and reads from
- // FUSE after that write may be served from cache
- bool has_redacted = node->HasRedactedCache();
- 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);
-
- if (!is_cached_file_open && is_redaction_change) {
- node->SetRedactedCache(redaction_needed);
- // Purges stale page cache before open
- *keep_cache = 0;
- } else {
- *keep_cache = transforms_complete;
- }
- handle = new struct handle(fd, ri, !direct_io /* cached */, false /* passthrough */, uid,
- transforms_uid);
+ *keep_cache = 1;
}
- node->AddHandle(handle);
- return handle;
-}
-
-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) {
- return false;
- }
-
- fi->passthrough_fh = passthrough_fh;
- return true;
+ bool direct_io = (is_cached_file_open && is_redaction_change) || is_file_locked(fd, path);
+ handle* h = new handle(fd, ri, !direct_io);
+ node->AddHandle(h);
+ return h;
}
static void pf_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) {
@@ -1253,39 +1029,22 @@
return;
}
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)) {
+ const string path = node->BuildPath();
+ if (!is_app_accessible_path(fuse->mp, path, ctx->uid)) {
fuse_reply_err(req, ENOENT);
return;
}
- bool for_write = is_requesting_write(fi->flags);
-
- if (for_write && node->GetTransforms()) {
- TRACE_NODE(node, req) << "write with transforms";
- } else {
- TRACE_NODE(node, req) << (for_write ? "write" : "read");
- }
+ TRACE_NODE(node, req) << (is_requesting_write(fi->flags) ? "write" : "read");
if (fi->flags & O_DIRECT) {
fi->flags &= ~O_DIRECT;
fi->direct_io = true;
}
- // 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 */);
- if (!result) {
- fuse_reply_err(req, EFAULT);
- return;
- }
-
- if (result->status) {
- fuse_reply_err(req, result->status);
+ int status = fuse->mp->IsOpenAllowed(path, ctx->uid, is_requesting_write(fi->flags));
+ if (status) {
+ fuse_reply_err(req, status);
return;
}
@@ -1301,31 +1060,31 @@
open_flags &= ~O_APPEND;
}
- const int fd = open(io_path.c_str(), open_flags);
+ const int fd = open(path.c_str(), open_flags);
if (fd < 0) {
fuse_reply_err(req, errno);
return;
}
+ // We don't redact if the caller was granted write permission for this file
+ std::unique_ptr<RedactionInfo> ri;
+ if (is_requesting_write(fi->flags)) {
+ ri = std::make_unique<RedactionInfo>();
+ } else {
+ ri = fuse->mp->GetRedactionInfo(path, req->ctx.uid, req->ctx.pid);
+ }
+
+ if (!ri) {
+ close(fd);
+ fuse_reply_err(req, EFAULT);
+ 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);
+ handle* h = create_handle_for_node(fuse, path, fd, node, ri.release(), &keep_cache);
fi->fh = ptr_to_id(h);
fi->keep_cache = keep_cache;
fi->direct_io = !h->cached;
-
- // 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;
- }
- }
-
fuse_reply_open(req, fi);
}
@@ -1407,18 +1166,6 @@
handle* h = reinterpret_cast<handle*>(fi->fh);
struct fuse* fuse = get_fuse(req);
- node* node = fuse->FromInode(ino);
-
- if (!node->IsTransformsComplete()) {
- if (!fuse->mp->Transform(node->BuildPath(), node->GetIoPath(), node->GetTransforms(),
- node->GetTransformsReason(), req->ctx.uid, h->uid,
- h->transforms_uid)) {
- fuse_reply_err(req, EFAULT);
- return;
- }
- node->SetTransformsComplete(true);
- }
-
fuse->fadviser.Record(h->fd, size);
if (h->ri->isRedactionNeeded()) {
@@ -1492,6 +1239,14 @@
fuse_reply_write(req, size);
}
#endif
+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,
@@ -1636,7 +1391,7 @@
h->next_off++;
if (plus) {
int error_code = 0;
- if (do_lookup(req, ino, de->d_name.c_str(), &e, &error_code, FuseOp::readdir)) {
+ if (do_lookup(req, ino, de->d_name.c_str(), &e, &error_code)) {
entry_size = fuse_add_direntry_plus(req, buf + used, len - used, de->d_name.c_str(),
&e, h->next_off);
} else {
@@ -1788,14 +1543,7 @@
return;
}
- std::unique_ptr<FileOpenResult> result = fuse->mp->OnFileOpen(
- path, path, req->ctx.uid, req->ctx.pid, node->GetTransformsReason(), for_write,
- false /* redact */, false /* log_transforms_metrics */);
- if (!result) {
- status = EFAULT;
- } else if (result->status) {
- status = EACCES;
- }
+ status = fuse->mp->IsOpenAllowed(path, req->ctx.uid, for_write);
}
fuse_reply_err(req, status);
@@ -1854,8 +1602,7 @@
int error_code = 0;
struct fuse_entry_param e;
- node* node =
- make_node_entry(req, parent_node, name, child_path, &e, &error_code, FuseOp::create);
+ node* node = make_node_entry(req, parent_node, name, child_path, &e, &error_code);
TRACE_NODE(node, req);
if (!node) {
CHECK(error_code != 0);
@@ -1871,23 +1618,10 @@
// 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);
+ handle* h = create_handle_for_node(fuse, child_path, fd, node, new RedactionInfo(), &keep_cache);
fi->fh = ptr_to_id(h);
fi->keep_cache = keep_cache;
fi->direct_io = !h->cached;
-
- // 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;
- }
- }
-
fuse_reply_create(req, &e, fi);
}
/*
@@ -1951,7 +1685,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,
@@ -1967,8 +1701,8 @@
.write_buf = pf_write_buf,
/*.retrieve_reply = pf_retrieve_reply,*/
.forget_multi = pf_forget_multi,
- /*.flock = pf_flock,*/
- .fallocate = pf_fallocate,
+ /*.flock = pf_flock,
+ .fallocate = pf_fallocate,*/
.readdirplus = pf_readdirplus,
/*.copy_file_range = pf_copy_file_range,*/
};
@@ -1994,13 +1728,6 @@
}
bool FuseDaemon::ShouldOpenWithFuse(int fd, bool for_read, const std::string& path) {
- if (fuse->passthrough) {
- // Always open with FUSE if passthrough is enabled. This avoids the delicate file lock
- // acquisition below to ensure VFS cache consistency and doesn't impact filesystem
- // performance since read(2)/write(2) happen in the kernel
- return true;
- }
-
bool use_fuse = false;
if (active.load(std::memory_order_acquire)) {
@@ -2052,8 +1779,7 @@
return active.load(std::memory_order_acquire);
}
-void FuseDaemon::Start(android::base::unique_fd fd, const std::string& path,
- const std::vector<std::string>& supported_transcoding_relative_paths) {
+void FuseDaemon::Start(android::base::unique_fd fd, const std::string& path) {
android::base::SetDefaultTag(LOG_TAG);
struct fuse_args args;
@@ -2078,7 +1804,7 @@
return;
}
- struct fuse fuse_default(path, stat.st_ino, supported_transcoding_relative_paths);
+ struct fuse fuse_default(path);
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
@@ -2098,16 +1824,12 @@
fuse_set_log_func(fuse_logger);
}
- if (MY_USER_ID != 0 && mp.IsAppCloneUser(MY_USER_ID)) {
+ uid_t userId = getuid() / PER_USER_RANGE;
+ if (userId != 0 && mp.IsAppCloneUser(userId)) {
// Disable dentry caching for the app clone user
fuse->disable_dentry_cache = true;
}
- fuse->passthrough = android::base::GetBoolProperty("persist.sys.fuse.passthrough.enable", false);
- if (fuse->passthrough) {
- LOG(INFO) << "Using FUSE passthrough";
- }
-
struct fuse_session
* se = fuse_session_new(&args, &ops, sizeof(ops), &fuse_default);
if (!se) {
@@ -2136,42 +1858,5 @@
LOG(INFO) << "Ended fuse";
return;
}
-
-const string FuseDaemon::GetOriginalMediaFormatFilePath(int fd) const {
- struct stat s;
- memset(&s, 0, sizeof(s));
- if (fstat(fd, &s) < 0) {
- PLOG(DEBUG) << "GetOriginalMediaFormatFilePath fstat failed.";
- return string();
- }
-
- ino_t ino = s.st_ino;
- dev_t dev = s.st_dev;
-
- 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();
- }
-
- const node* node = node::LookupInode(fuse->root, ino);
- if (!node) {
- PLOG(DEBUG) << "GetOriginalMediaFormatFilePath no node found with given ino";
- return string();
- }
-
- return node->BuildPath();
-}
-
-void FuseDaemon::InitializeDeviceId(const std::string& path) {
- struct stat stat;
-
- if (lstat(path.c_str(), &stat)) {
- PLOG(ERROR) << "InitializeDeviceId failed to stat given path " << path;
- return;
- }
-
- fuse->dev.store(stat.st_dev, std::memory_order_release);
-}
} //namespace fuse
} // namespace mediaprovider
diff --git a/jni/FuseDaemon.h b/jni/FuseDaemon.h
index 3164830..3c4b947 100644
--- a/jni/FuseDaemon.h
+++ b/jni/FuseDaemon.h
@@ -37,8 +37,7 @@
/**
* Start the FUSE daemon loop that will handle filesystem calls.
*/
- void Start(android::base::unique_fd fd, const std::string& path,
- const std::vector<std::string>& supported_transcoding_relative_paths);
+ void Start(android::base::unique_fd fd, const std::string& path);
/**
* Checks if the FUSE daemon is started.
@@ -55,16 +54,6 @@
*/
void InvalidateFuseDentryCache(const std::string& path);
- /**
- * Return path of the original media format file for the given file descriptor.
- */
- const std::string GetOriginalMediaFormatFilePath(int fd) const;
-
- /**
- * Initialize device id for the FUSE daemon with the FUSE device id of the given path.
- */
- void InitializeDeviceId(const std::string& path);
-
private:
FuseDaemon(const FuseDaemon&) = delete;
void operator=(const FuseDaemon&) = delete;
diff --git a/jni/FuseUtils.cpp b/jni/FuseUtils.cpp
index 9f30440..7829888 100644
--- a/jni/FuseUtils.cpp
+++ b/jni/FuseUtils.cpp
@@ -26,7 +26,7 @@
namespace mediaprovider {
namespace fuse {
-bool containsMount(const string& path) {
+bool containsMount(const string& path, const string& userid) {
// 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,20 +35,16 @@
return false;
}
- // Skip over the user-id by finding the next '/'
- size_t pos = path.find_first_of("/", prefix.length());
- // If we can't find another '/', or the '/' immediately follows the previous,
- // ('/storage/emulated//'), not a valid mount.
- if (pos == std::string::npos || pos == prefix.length()) {
+ const string& rest_of_path = path.substr(prefix.length());
+ if (!android::base::StartsWithIgnoreCase(rest_of_path, userid)) {
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 dede692..d9d28e6 100644
--- a/jni/FuseUtilsTest.cpp
+++ b/jni/FuseUtilsTest.cpp
@@ -23,41 +23,44 @@
using namespace mediaprovider::fuse;
TEST(FuseUtilsTest, testContainsMount_isTrueForAndroidDataObb) {
- 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"));
+ 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"));
}
TEST(FuseUtilsTest, testContainsMount) {
- 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/"));
- EXPECT_FALSE(containsMount("/storage/emulated/1234/"));
+ EXPECT_FALSE(containsMount("/random/path", "1234"));
+ EXPECT_FALSE(containsMount("/storage/abc-123", "1234"));
+ EXPECT_FALSE(containsMount("/storage/emulated/1234/Android/data/and/more", "1234"));
}
TEST(FuseUtilsTest, testContainsMount_isCaseInsensitive) {
- 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"));
+ 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"));
}
TEST(FuseUtilsTest, testContainsMount_isFalseForPathWithAdditionalSlash) {
- EXPECT_FALSE(containsMount("/storage/emulated//Android/data"));
+ 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"));
}
diff --git a/jni/MediaProviderWrapper.cpp b/jni/MediaProviderWrapper.cpp
index 9f8a759..734ae97 100644
--- a/jni/MediaProviderWrapper.cpp
+++ b/jni/MediaProviderWrapper.cpp
@@ -56,17 +56,30 @@
return false;
}
-/**
- * Auxiliary for caching class fields
- */
-static jfieldID CacheField(JNIEnv* env, jclass clazz, const char field_name[], const char type[]) {
- jfieldID fid;
- string actual_field_name(field_name);
- fid = env->GetFieldID(clazz, actual_field_name.c_str(), type);
- if (!fid) {
- LOG(FATAL) << "Error caching field: " << field_name << type;
+std::unique_ptr<RedactionInfo> getRedactionInfoInternal(JNIEnv* env, jobject media_provider_object,
+ jmethodID mid_get_redaction_ranges,
+ uid_t uid, pid_t tid, const string& path) {
+ ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
+ ScopedLocalRef<jlongArray> redaction_ranges_local_ref(
+ env, static_cast<jlongArray>(env->CallObjectMethod(
+ media_provider_object, mid_get_redaction_ranges, j_path.get(), uid, tid)));
+ ScopedLongArrayRO redaction_ranges(env, redaction_ranges_local_ref.get());
+
+ if (CheckForJniException(env)) {
+ return nullptr;
}
- return fid;
+
+ std::unique_ptr<RedactionInfo> ri;
+ if (redaction_ranges.size() % 2) {
+ LOG(ERROR) << "Error while calculating redaction ranges: array length is uneven";
+ } else if (redaction_ranges.size() > 0) {
+ ri = std::make_unique<RedactionInfo>(redaction_ranges.size() / 2, redaction_ranges.get());
+ } else {
+ // No ranges to redact
+ ri = std::make_unique<RedactionInfo>();
+ }
+
+ return ri;
}
int insertFileInternal(JNIEnv* env, jobject media_provider_object, jmethodID mid_insert_file,
@@ -91,6 +104,18 @@
return res;
}
+int isOpenAllowedInternal(JNIEnv* env, jobject media_provider_object, jmethodID mid_is_open_allowed,
+ const string& path, uid_t uid, bool for_write) {
+ ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
+ int res = env->CallIntMethod(media_provider_object, mid_is_open_allowed, j_path.get(), uid,
+ for_write);
+
+ if (CheckForJniException(env)) {
+ return EFAULT;
+ }
+ 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) {
@@ -228,13 +253,13 @@
media_provider_class_ = reinterpret_cast<jclass>(env->NewGlobalRef(media_provider_class_));
// Cache methods - Before calling a method, make sure you cache it here
+ mid_get_redaction_ranges_ = CacheMethod(env, "getRedactionRanges", "(Ljava/lang/String;II)[J",
+ /*is_static*/ false);
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_on_file_open_ = CacheMethod(env, "onFileOpen",
- "(Ljava/lang/String;Ljava/lang/String;IIIZZZ)Lcom/android/"
- "providers/media/FileOpenResult;",
- /*is_static*/ false);
+ mid_is_open_allowed_ = CacheMethod(env, "isOpenAllowed", "(Ljava/lang/String;IZ)I",
+ /*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",
@@ -253,42 +278,6 @@
/*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);
- mid_file_lookup_ =
- CacheMethod(env, "onFileLookup",
- "(Ljava/lang/String;II)Lcom/android/providers/media/FileLookupResult;",
- /*is_static*/ false);
-
- // FileLookupResult
- file_lookup_result_class_ = env->FindClass("com/android/providers/media/FileLookupResult");
- if (!file_lookup_result_class_) {
- LOG(FATAL) << "Could not find class FileLookupResult";
- }
- file_lookup_result_class_ =
- reinterpret_cast<jclass>(env->NewGlobalRef(file_lookup_result_class_));
- fid_file_lookup_transforms_ = CacheField(env, file_lookup_result_class_, "transforms", "I");
- fid_file_lookup_transforms_reason_ =
- CacheField(env, file_lookup_result_class_, "transformsReason", "I");
- fid_file_lookup_uid_ = CacheField(env, file_lookup_result_class_, "uid", "I");
- fid_file_lookup_transforms_complete_ =
- CacheField(env, file_lookup_result_class_, "transformsComplete", "Z");
- fid_file_lookup_transforms_supported_ =
- CacheField(env, file_lookup_result_class_, "transformsSupported", "Z");
- fid_file_lookup_io_path_ =
- CacheField(env, file_lookup_result_class_, "ioPath", "Ljava/lang/String;");
-
- // FileOpenResult
- file_open_result_class_ = env->FindClass("com/android/providers/media/FileOpenResult");
- if (!file_open_result_class_) {
- LOG(FATAL) << "Could not find class FileOpenResult";
- }
- file_open_result_class_ = reinterpret_cast<jclass>(env->NewGlobalRef(file_open_result_class_));
- fid_file_open_status_ = CacheField(env, file_open_result_class_, "status", "I");
- fid_file_open_uid_ = CacheField(env, file_open_result_class_, "uid", "I");
- 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");
}
MediaProviderWrapper::~MediaProviderWrapper() {
@@ -297,6 +286,23 @@
env->DeleteGlobalRef(media_provider_class_);
}
+std::unique_ptr<RedactionInfo> MediaProviderWrapper::GetRedactionInfo(const string& path, uid_t uid,
+ pid_t tid) {
+ if (shouldBypassMediaProvider(uid) || !GetBoolProperty(kPropRedactionEnabled, true)) {
+ return std::make_unique<RedactionInfo>();
+ }
+
+ // Default value in case JNI thread was being terminated, causes the read to fail.
+ std::unique_ptr<RedactionInfo> res = nullptr;
+
+ JNIEnv* env = MaybeAttachCurrentThread();
+ auto ri = getRedactionInfoInternal(env, media_provider_object_, mid_get_redaction_ranges_, uid,
+ tid, path);
+ res = std::move(ri);
+
+ return res;
+}
+
int MediaProviderWrapper::InsertFile(const string& path, uid_t uid) {
if (uid == ROOT_UID) {
return 0;
@@ -316,53 +322,14 @@
return deleteFileInternal(env, media_provider_object_, mid_delete_file_, path, uid);
}
-std::unique_ptr<FileOpenResult> MediaProviderWrapper::OnFileOpen(const string& path,
- const string& io_path, uid_t uid,
- pid_t tid, int transforms_reason,
- bool for_write, bool redact,
- bool log_transforms_metrics) {
- JNIEnv* env = MaybeAttachCurrentThread();
+int MediaProviderWrapper::IsOpenAllowed(const string& path, uid_t uid, bool for_write) {
if (shouldBypassMediaProvider(uid)) {
- return std::make_unique<FileOpenResult>(0, uid, 0 /* transforms_uid */, new RedactionInfo());
+ return 0;
}
- ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
- ScopedLocalRef<jstring> j_io_path(env, env->NewStringUTF(io_path.c_str()));
- ScopedLocalRef<jobject> j_res_file_open_object(
- env, env->CallObjectMethod(media_provider_object_, mid_on_file_open_, j_path.get(),
- j_io_path.get(), uid, tid, transforms_reason, for_write,
- redact, log_transforms_metrics));
-
- if (CheckForJniException(env)) {
- 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 =
- env->GetIntField(j_res_file_open_object.get(), fid_file_open_transforms_uid_);
-
- if (redact) {
- ScopedLocalRef<jlongArray> redaction_ranges_local_ref(
- env, static_cast<jlongArray>(env->GetObjectField(j_res_file_open_object.get(),
- fid_file_open_redaction_ranges_)));
- ScopedLongArrayRO redaction_ranges(env, redaction_ranges_local_ref.get());
-
- std::unique_ptr<RedactionInfo> ri;
- if (redaction_ranges.size() % 2) {
- LOG(ERROR) << "Error while calculating redaction ranges: array length is uneven";
- } else if (redaction_ranges.size() > 0) {
- ri = std::make_unique<RedactionInfo>(redaction_ranges.size() / 2,
- redaction_ranges.get());
- } else {
- // No ranges to redact
- ri = std::make_unique<RedactionInfo>();
- }
- return std::make_unique<FileOpenResult>(status, original_uid, transforms_uid, ri.release());
- } else {
- return std::make_unique<FileOpenResult>(status, original_uid, transforms_uid,
- new RedactionInfo());
- }
+ JNIEnv* env = MaybeAttachCurrentThread();
+ return isOpenAllowedInternal(env, media_provider_object_, mid_is_open_allowed_, path, uid,
+ for_write);
}
int MediaProviderWrapper::IsCreatingDirAllowed(const string& path, uid_t uid) {
@@ -473,57 +440,6 @@
return res;
}
-std::unique_ptr<FileLookupResult> MediaProviderWrapper::FileLookup(const std::string& path,
- uid_t uid, pid_t tid) {
- JNIEnv* env = MaybeAttachCurrentThread();
-
- ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
-
- ScopedLocalRef<jobject> j_res_file_lookup_object(
- env, env->CallObjectMethod(media_provider_object_, mid_file_lookup_, j_path.get(), uid,
- tid));
-
- if (CheckForJniException(env)) {
- return nullptr;
- }
-
- int transforms = env->GetIntField(j_res_file_lookup_object.get(), fid_file_lookup_transforms_);
- int transforms_reason =
- env->GetIntField(j_res_file_lookup_object.get(), fid_file_lookup_transforms_reason_);
- int original_uid = env->GetIntField(j_res_file_lookup_object.get(), fid_file_lookup_uid_);
- bool transforms_complete = env->GetBooleanField(j_res_file_lookup_object.get(),
- fid_file_lookup_transforms_complete_);
- bool transforms_supported = env->GetBooleanField(j_res_file_lookup_object.get(),
- fid_file_lookup_transforms_supported_);
- ScopedLocalRef<jstring> j_io_path(
- env,
- (jstring)env->GetObjectField(j_res_file_lookup_object.get(), fid_file_lookup_io_path_));
- ScopedUtfChars j_io_path_utf(env, j_io_path.get());
-
- std::unique_ptr<FileLookupResult> file_lookup_result = std::make_unique<FileLookupResult>(
- transforms, transforms_reason, original_uid, transforms_complete, transforms_supported,
- string(j_io_path_utf.c_str()));
- return file_lookup_result;
-}
-
-bool MediaProviderWrapper::Transform(const std::string& src, const std::string& dst, int transforms,
- int transforms_reason, uid_t read_uid, uid_t open_uid,
- uid_t transforms_uid) {
- JNIEnv* env = MaybeAttachCurrentThread();
-
- ScopedLocalRef<jstring> j_src(env, env->NewStringUTF(src.c_str()));
- ScopedLocalRef<jstring> j_dst(env, env->NewStringUTF(dst.c_str()));
- bool res = env->CallBooleanMethod(media_provider_object_, mid_transform_, j_src.get(),
- j_dst.get(), transforms, transforms_reason, read_uid,
- open_uid, transforms_uid);
-
- if (CheckForJniException(env)) {
- return false;
- }
-
- return res;
-}
-
/*****************************************************************************************/
/******************************** Private member functions *******************************/
/*****************************************************************************************/
@@ -542,6 +458,7 @@
mid = env->GetMethodID(media_provider_class_, actual_method_name.c_str(), signature);
}
if (!mid) {
+ // SHOULD NOT HAPPEN!
LOG(FATAL) << "Error caching method: " << method_name << signature;
}
return mid;
diff --git a/jni/MediaProviderWrapper.h b/jni/MediaProviderWrapper.h
index 2ad1769..23c611a 100644
--- a/jni/MediaProviderWrapper.h
+++ b/jni/MediaProviderWrapper.h
@@ -17,7 +17,6 @@
#ifndef MEDIAPROVIDER_FUSE_MEDIAPROVIDERWRAPPER_H_
#define MEDIAPROVIDER_FUSE_MEDIAPROVIDERWRAPPER_H_
-#include <android-base/logging.h>
#include <jni.h>
#include <sys/types.h>
@@ -36,48 +35,6 @@
namespace mediaprovider {
namespace fuse {
-/** Represents file open result from MediaProvider */
-struct FileOpenResult {
- FileOpenResult(const int status, const int uid, uid_t transforms_uid,
- const RedactionInfo* redaction_info)
- : status(status), uid(uid), transforms_uid(transforms_uid), redaction_info(redaction_info) {}
-
- const int status;
- const int uid;
- const uid_t transforms_uid;
- std::unique_ptr<const RedactionInfo> redaction_info;
-};
-
-/**
- * Represents transform info for a file, containing the transforms, the transforms completion
- * status and the ioPath. Provided by MediaProvider.java via a JNI call.
- */
-struct FileLookupResult {
- FileLookupResult(int transforms, int transforms_reason, uid_t uid, bool transforms_complete,
- bool transforms_supported, const std::string& io_path)
- : transforms(transforms),
- transforms_reason(transforms_reason),
- uid(uid),
- transforms_complete(transforms_complete),
- transforms_supported(transforms_supported),
- io_path(io_path) {
- if (transforms != 0) {
- CHECK(transforms_supported);
- }
- }
-
- /**
- * These fields are not to be interpreted, they are determined and populated from MediaProvider
- * via a JNI call.
- */
- const int transforms;
- const int transforms_reason;
- const uid_t uid;
- const bool transforms_complete;
- const bool transforms_supported;
- const std::string io_path;
-};
-
/**
* Class that wraps MediaProvider.java and all of the needed JNI calls to make
* interaction with MediaProvider easier.
@@ -91,14 +48,11 @@
* Computes and returns the RedactionInfo for a given file and UID.
*
* @param uid UID of the app requesting the read
- * @param path path of the requested file that will be used for database operations
- * @param io_path path of the requested file that will be used for IO
+ * @param path path of the requested file
* @return RedactionInfo on success, nullptr on failure to calculate
* redaction ranges (e.g. exception was thrown in Java world)
*/
- std::unique_ptr<RedactionInfo> GetRedactionInfo(const std::string& path,
- const std::string& io_path, uid_t uid,
- pid_t tid);
+ std::unique_ptr<RedactionInfo> GetRedactionInfo(const std::string& path, uid_t uid, pid_t tid);
/**
* Inserts a new entry for the given path and UID.
@@ -139,26 +93,14 @@
/**
* Determines if the given UID is allowed to open the file denoted by the given path.
*
- * Also computes and returns the RedactionInfo for a given file and |uid|
- *
- * @param path path of the requested file that will be used for database operations
- * @param io_path path of the requested file that will be used for IO
+ * @param path the path of the file to be opened
* @param uid UID of the calling app
- * @param tid UID of the calling app
* @param for_write specifies if the file is to be opened for write
- * @param redact specifies whether to attempt redaction
- * @return FileOpenResult containing status, uid and redaction_info
+ * @return 0 upon success or errno value upon failure.
*/
- std::unique_ptr<FileOpenResult> OnFileOpen(const std::string& path, const std::string& io_path,
- uid_t uid, pid_t tid, int transforms_reason,
- bool for_write, bool redact,
- bool log_transforms_metrics);
+ int IsOpenAllowed(const std::string& path, uid_t uid, bool for_write);
/**
- * Determines if the given UID is allowed to create a directory with the given path.
- *
- * @param path the path of the directory to be created
- * @param uid UID of the calling app
* @return 0 if it's allowed, or errno error code if operation isn't allowed.
*/
int IsCreatingDirAllowed(const std::string& path, uid_t uid);
@@ -217,15 +159,6 @@
void OnFileCreated(const std::string& path);
/**
- * Returns FileLookupResult to determine transform info for a path and uid.
- */
- std::unique_ptr<FileLookupResult> FileLookup(const std::string& path, uid_t uid, pid_t tid);
-
- /** Transforms from src to dst file */
- bool Transform(const std::string& src, const std::string& dst, int transforms,
- int transforms_reason, uid_t read_uid, uid_t open_uid, uid_t transforms_uid);
-
- /**
* Determines if to allow FUSE_LOOKUP for uid. Might allow uids that don't belong to the
* MediaProvider user, depending on OEM configuration.
*
@@ -250,15 +183,13 @@
static pthread_key_t gJniEnvKey;
private:
- jclass file_lookup_result_class_;
- jclass file_open_result_class_;
jclass media_provider_class_;
jobject media_provider_object_;
/** Cached MediaProvider method IDs **/
+ jmethodID mid_get_redaction_ranges_;
jmethodID mid_insert_file_;
jmethodID mid_delete_file_;
- jmethodID mid_on_file_open_;
- jmethodID mid_scan_file_;
+ jmethodID mid_is_open_allowed_;
jmethodID mid_is_mkdir_or_rmdir_allowed_;
jmethodID mid_is_opendir_allowed_;
jmethodID mid_get_files_in_dir_;
@@ -267,20 +198,6 @@
jmethodID mid_on_file_created_;
jmethodID mid_should_allow_lookup_;
jmethodID mid_is_app_clone_user_;
- jmethodID mid_transform_;
- jmethodID mid_file_lookup_;
- /** Cached FileLookupResult field IDs **/
- jfieldID fid_file_lookup_transforms_;
- jfieldID fid_file_lookup_transforms_reason_;
- jfieldID fid_file_lookup_uid_;
- jfieldID fid_file_lookup_transforms_complete_;
- jfieldID fid_file_lookup_transforms_supported_;
- jfieldID fid_file_lookup_io_path_;
- /** Cached FileOpenResult field IDs **/
- jfieldID fid_file_open_status_;
- jfieldID fid_file_open_uid_;
- jfieldID fid_file_open_transforms_uid_;
- jfieldID fid_file_open_redaction_ranges_;
/**
* Auxiliary for caching MediaProvider methods.
diff --git a/jni/com_android_providers_media_FuseDaemon.cpp b/jni/com_android_providers_media_FuseDaemon.cpp
index 46ed041..3a65696 100644
--- a/jni/com_android_providers_media_FuseDaemon.cpp
+++ b/jni/com_android_providers_media_FuseDaemon.cpp
@@ -17,7 +17,6 @@
// Need to use LOGE_EX.
#define LOG_TAG "FuseDaemonJNI"
-#include <nativehelper/scoped_local_ref.h>
#include <nativehelper/scoped_utf_chars.h>
#include <string>
@@ -33,39 +32,14 @@
constexpr const char* CLASS_NAME = "com/android/providers/media/fuse/FuseDaemon";
static jclass gFuseDaemonClass;
-static std::vector<std::string> get_supported_transcoding_relative_paths(
- JNIEnv* env, jobjectArray java_supported_transcoding_relative_paths) {
- ScopedLocalRef<jobjectArray> j_transcoding_relative_paths(
- env, java_supported_transcoding_relative_paths);
- std::vector<std::string> transcoding_relative_paths;
-
- const int transcoding_relative_paths_count =
- env->GetArrayLength(j_transcoding_relative_paths.get());
- for (int i = 0; i < transcoding_relative_paths_count; i++) {
- ScopedLocalRef<jstring> j_ref_relative_path(
- env, (jstring)env->GetObjectArrayElement(j_transcoding_relative_paths.get(), i));
- ScopedUtfChars j_utf_relative_path(env, j_ref_relative_path.get());
- const char* relative_path = j_utf_relative_path.c_str();
-
- if (relative_path) {
- transcoding_relative_paths.push_back(relative_path);
- } else {
- LOG(ERROR) << "Error reading supported transcoding relative path at index: " << i;
- }
- }
-
- return transcoding_relative_paths;
-}
-
jlong com_android_providers_media_FuseDaemon_new(JNIEnv* env, jobject self,
jobject media_provider) {
LOG(DEBUG) << "Creating the FUSE daemon...";
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,
- jobjectArray java_supported_transcoding_relative_paths) {
+void com_android_providers_media_FuseDaemon_start(JNIEnv* env, jobject self, jlong java_daemon,
+ jint fd, jstring java_path) {
LOG(DEBUG) << "Starting the FUSE daemon...";
fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
@@ -76,11 +50,7 @@
return;
}
- const std::vector<std::string>& transcoding_relative_paths =
- get_supported_transcoding_relative_paths(env,
- java_supported_transcoding_relative_paths);
-
- daemon->Start(std::move(ufd), utf_chars_path.c_str(), transcoding_relative_paths);
+ daemon->Start(std::move(ufd), utf_chars_path.c_str());
}
bool com_android_providers_media_FuseDaemon_is_started(JNIEnv* env, jobject self,
@@ -131,25 +101,6 @@
// 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) {
- fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
- const std::string path = daemon->GetOriginalMediaFormatFilePath(fd);
- return env->NewStringUTF(path.c_str());
-}
-
-void com_android_providers_media_FuseDaemon_initialize_device_id(JNIEnv* env, jobject self,
- jlong java_daemon,
- jstring java_path) {
- fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
- ScopedUtfChars utf_chars_path(env, java_path);
- if (!utf_chars_path.c_str()) {
- LOG(WARNING) << "Couldn't initialise FUSE device id";
- return;
- }
- daemon->InitializeDeviceId(utf_chars_path.c_str());
-}
-
bool com_android_providers_media_FuseDaemon_is_fuse_thread(JNIEnv* env, jclass clazz) {
return pthread_getspecific(fuse::MediaProviderWrapper::gJniEnvKey) != nullptr;
}
@@ -157,7 +108,7 @@
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;[Ljava/lang/String;)V",
+ {"native_start", "(JILjava/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)},
@@ -169,12 +120,7 @@
reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_is_started)},
{"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_initialize_device_id", "(JLjava/lang/String;)V",
- reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_initialize_device_id)}};
+ com_android_providers_media_FuseDaemon_invalidate_fuse_dentry_cache)}};
} // namespace
void register_android_providers_media_FuseDaemon(JavaVM* vm, JNIEnv* env) {
diff --git a/jni/include/libfuse_jni/FuseUtils.h b/jni/include/libfuse_jni/FuseUtils.h
index 0e35099..88044db 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 any
+ * Returns true if the given path (ignoring case) is mounted for the given
* userid. Mounted paths are:
* "/storage/emulated/<userid>/Android"
* "/storage/emulated/<userid>/Android/data"
* "/storage/emulated/<userid>/Android/obb" *
*/
-bool containsMount(const std::string& path);
+bool containsMount(const std::string& path, const std::string& userid);
} // namespace fuse
} // namespace mediaprovider
diff --git a/jni/node-inl.h b/jni/node-inl.h
index e531a0a..dfd8141 100644
--- a/jni/node-inl.h
+++ b/jni/node-inl.h
@@ -19,8 +19,6 @@
#include <android-base/logging.h>
-#include <sys/types.h>
-#include <atomic>
#include <cstdint>
#include <limits>
#include <list>
@@ -42,23 +40,13 @@
namespace fuse {
struct handle {
- explicit handle(int fd, const RedactionInfo* ri, bool cached, bool passthrough, uid_t uid,
- uid_t transforms_uid)
- : fd(fd),
- ri(ri),
- cached(cached),
- passthrough(passthrough),
- uid(uid),
- transforms_uid(transforms_uid) {
+ explicit handle(int fd, const RedactionInfo* ri, bool cached) : fd(fd), ri(ri), cached(cached) {
CHECK(ri != nullptr);
}
const int fd;
const std::unique_ptr<const RedactionInfo> ri;
const bool cached;
- const bool passthrough;
- const uid_t uid;
- const uid_t transforms_uid;
~handle() { close(fd); }
};
@@ -126,26 +114,21 @@
class node {
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 int transforms_reason, std::recursive_mutex* lock, ino_t ino,
+ static node* Create(node* parent, const std::string& name, std::recursive_mutex* lock,
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, lock, tracker);
}
// Creates a new root node. Root nodes have no parents by definition
// and their "name" must signify an absolute path.
- static node* CreateRoot(const std::string& path, std::recursive_mutex* lock, ino_t ino,
+ static node* CreateRoot(const std::string& path, std::recursive_mutex* lock,
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, lock, tracker);
// The root always has one extra reference to avoid it being
// accidentally collected.
@@ -191,42 +174,36 @@
// associated with its descendants.
std::string BuildSafePath() const;
- // Looks up a direct descendant of this node by case-insensitive |name|. If |acquire| is true,
+ // Looks up a direct descendant of this node by name. If |acquire| is true,
// also Acquire the node before returning a reference to it.
- // |transforms| is an opaque flag that is used to distinguish multiple nodes sharing the same
- // |name| but requiring different IO transformations as determined by the MediaProvider.
- node* LookupChildByName(const std::string& name, bool acquire, const int transforms = 0) const {
- return ForChild(name, [acquire, transforms](node* child) {
- if (child->transforms_ == transforms) {
+ node* LookupChildByName(const std::string& name, bool acquire) const {
+ std::lock_guard<std::recursive_mutex> guard(*lock_);
+
+ // lower_bound will give us the first child with strcasecmp(child->name, name) >=0.
+ // For more context see comment on the NodeCompare struct.
+ auto start = children_.lower_bound(std::make_pair(name, 0));
+ // upper_bound will give us the first child with strcasecmp(child->name, name) > 0
+ auto end =
+ children_.upper_bound(std::make_pair(name, std::numeric_limits<uintptr_t>::max()));
+ for (auto it = start; it != end; it++) {
+ node* child = *it;
+ if (!child->deleted_) {
if (acquire) {
child->Acquire();
}
- return true;
+ return child;
}
- return false;
- });
+ }
+ return nullptr;
}
- // Marks this node children as deleted. They are still associated with their parent, and
- // all open handles etc. to the deleted nodes are preserved until their refcount goes
+ // Marks this node as deleted. It is still associated with its parent, and
+ // all open handles etc. to this node are preserved until its refcount goes
// to zero.
- void SetDeletedForChild(const std::string& name) {
- ForChild(name, [](node* child) {
- child->SetDeleted();
- return false;
- });
- }
-
void SetDeleted() {
std::lock_guard<std::recursive_mutex> guard(*lock_);
- deleted_ = true;
- }
- void RenameChild(const std::string& old_name, const std::string& new_name, node* new_parent) {
- ForChild(old_name, [=](node* child) {
- child->Rename(new_name, new_parent);
- return false;
- });
+ deleted_ = true;
}
void Rename(const std::string& name, node* new_parent) {
@@ -275,20 +252,6 @@
return name_;
}
- const std::string& GetIoPath() const { return io_path_; }
-
- int GetTransforms() const { return transforms_; }
-
- int GetTransformsReason() const { return transforms_reason_; }
-
- bool IsTransformsComplete() const {
- return transforms_complete_.load(std::memory_order_acquire);
- }
-
- void SetTransformsComplete(bool complete) {
- transforms_complete_.store(complete, std::memory_order_release);
- }
-
node* GetParent() const {
std::lock_guard<std::recursive_mutex> guard(*lock_);
return parent_;
@@ -319,14 +282,14 @@
return false;
}
- bool ShouldInvalidate() const {
+ bool HasCaseInsensitiveMatch() const {
std::lock_guard<std::recursive_mutex> guard(*lock_);
- return should_invalidate_;
+ return has_case_insensitive_match_;
}
- void SetShouldInvalidate() {
+ void SetCaseInsensitiveMatch() {
std::lock_guard<std::recursive_mutex> guard(*lock_);
- should_invalidate_ = true;
+ has_case_insensitive_match_ = true;
}
bool HasRedactedCache() const {
@@ -361,25 +324,15 @@
// through the hierarchy exists.
static const node* LookupAbsolutePath(const node* root, const std::string& absolute_path);
- // Looks up for the node with the given ino rooted at |root|, or nullptr if no such node exists.
- static const node* LookupInode(const node* root, ino_t ino);
-
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)
+ node(node* parent, const std::string& name, std::recursive_mutex* lock, NodeTracker* tracker)
: name_(name),
- io_path_(io_path),
- transforms_complete_(transforms_complete),
- transforms_(transforms),
- transforms_reason_(transforms_reason),
refcount_(0),
parent_(nullptr),
has_redacted_cache_(false),
- should_invalidate_(should_invalidate),
+ has_case_insensitive_match_(false),
deleted_(false),
lock_(lock),
- ino_(ino),
tracker_(tracker) {
tracker_->NodeCreated(this);
Acquire();
@@ -388,10 +341,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
@@ -432,32 +381,6 @@
}
}
- // Finds *all* non-deleted nodes matching |name| and runs the function |callback| on each
- // node until |callback| returns true.
- // When |callback| returns true, the matched node is returned
- node* ForChild(const std::string& name, const std::function<bool(node*)>& callback) const {
- std::lock_guard<std::recursive_mutex> guard(*lock_);
-
- // lower_bound will give us the first child with strcasecmp(child->name, name) >=0.
- // For more context see comment on the NodeCompare struct.
- auto start = children_.lower_bound(std::make_pair(name, 0));
- // upper_bound will give us the first child with strcasecmp(child->name, name) > 0
- auto end =
- children_.upper_bound(std::make_pair(name, std::numeric_limits<uintptr_t>::max()));
-
- // Make a copy of the matches because calling callback might modify the list which will
- // cause issues while iterating over them.
- std::vector<node*> children(start, end);
-
- for (node* child : children) {
- if (!child->deleted_ && callback(child)) {
- return child;
- }
- }
-
- return nullptr;
- }
-
// A custom heterogeneous comparator used for set of this node's children_ to speed up child
// node by name lookups.
//
@@ -504,20 +427,6 @@
// The name of this node. Non-const because it can change during renames.
std::string name_;
- // Filesystem path that will be used for IO (if it is non-empty) instead of node->BuildPath
- const std::string io_path_;
- // Whether any transforms required on |io_path_| are complete.
- // If false, might need to call a node transform function with |transforms| below
- std::atomic_bool transforms_complete_;
- // Opaque flags that determines the 'required' transforms to perform on node
- // before IO. These flags should not be interpreted in native but should be passed to the
- // MediaProvider as part of a transform function and if successful, |transforms_complete_|
- // should be set to true
- const int transforms_;
- // Opaque value indicating the reason why transforms are required.
- // This value should not be interpreted in native but should be passed to the MediaProvider
- // as part of a transform function
- const int transforms_reason_;
// The reference count for this node. Guarded by |lock_|.
uint32_t refcount_;
// Set of children of this node. All of them contain a back reference
@@ -530,11 +439,9 @@
// 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 has_case_insensitive_match_;
bool deleted_;
std::recursive_mutex* lock_;
- // Inode number of the file represented by this node.
- const ino_t ino_;
NodeTracker* const tracker_;
diff --git a/jni/node.cpp b/jni/node.cpp
index 31e4970..e17a9e8 100644
--- a/jni/node.cpp
+++ b/jni/node.cpp
@@ -93,24 +93,6 @@
return node;
}
-const node* node::LookupInode(const node* root, ino_t ino) {
- CHECK(root);
-
- std::lock_guard<std::recursive_mutex> guard(*root->lock_);
-
- if ((root->ino_ == ino) && !root->deleted_ && !(root->handles_.empty())) {
- return root;
- }
-
- for (node* child : root->children_) {
- const node* node = LookupInode(child, ino);
- if (node) {
- return node;
- }
- }
- return nullptr;
-}
-
void node::DeleteTree(node* tree) {
std::lock_guard<std::recursive_mutex> guard(*tree->lock_);
diff --git a/jni/node_test.cpp b/jni/node_test.cpp
index e6870f8..357cea8 100644
--- a/jni/node_test.cpp
+++ b/jni/node_test.cpp
@@ -31,15 +31,8 @@
typedef std::unique_ptr<node, decltype(&NodeTest::destroy)> unique_node_ptr;
- 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_),
- &NodeTest::destroy);
- }
-
- static class node* ForChild(class node* node, const std::string& name,
- const std::function<bool(class node*)>& callback) {
- return node->ForChild(name, callback);
+ unique_node_ptr CreateNode(node* parent, const std::string& path) {
+ return unique_node_ptr(node::Create(parent, path, &lock_, &tracker_), &NodeTest::destroy);
}
// Expose NodeCompare for testing.
@@ -68,7 +61,7 @@
}
TEST_F(NodeTest, TestRelease) {
- node* node = node::Create(nullptr, "/path", "", false, true, 0, 0, &lock_, 0, &tracker_);
+ node* node = node::Create(nullptr, "/path", &lock_, &tracker_);
acquire(node);
acquire(node);
ASSERT_EQ(3, GetRefCount(node));
@@ -84,7 +77,7 @@
ASSERT_TRUE(node->Release(2));
}
-TEST_F(NodeTest, TestRenameName) {
+TEST_F(NodeTest, TestRenameWithName) {
unique_node_ptr parent = CreateNode(nullptr, "/path");
unique_node_ptr child = CreateNode(parent.get(), "subdir");
@@ -101,7 +94,7 @@
ASSERT_EQ(1, GetRefCount(child.get()));
}
-TEST_F(NodeTest, TestRenameParent) {
+TEST_F(NodeTest, TestRenameWithParent) {
unique_node_ptr parent1 = CreateNode(nullptr, "/path1");
unique_node_ptr parent2 = CreateNode(nullptr, "/path2");
@@ -120,7 +113,7 @@
ASSERT_EQ(1, GetRefCount(child.get()));
}
-TEST_F(NodeTest, TestRenameNameAndParent) {
+TEST_F(NodeTest, TestRenameWithNameAndParent) {
unique_node_ptr parent1 = CreateNode(nullptr, "/path1");
unique_node_ptr parent2 = CreateNode(nullptr, "/path2");
@@ -140,101 +133,6 @@
ASSERT_EQ(1, GetRefCount(child.get()));
}
-TEST_F(NodeTest, TestRenameNameForChild) {
- unique_node_ptr parent = CreateNode(nullptr, "/path");
-
- unique_node_ptr child0 = CreateNode(parent.get(), "subdir", 0 /* transforms */);
- unique_node_ptr child1 = CreateNode(parent.get(), "subdir", 1 /* transforms */);
- ASSERT_EQ(3, GetRefCount(parent.get()));
- ASSERT_EQ(child0.get(),
- parent->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
- ASSERT_EQ(child1.get(),
- parent->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
-
- parent->RenameChild("subdir", "subdir_new", parent.get());
-
- ASSERT_EQ(3, GetRefCount(parent.get()));
- ASSERT_EQ(nullptr,
- parent->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
- ASSERT_EQ(nullptr,
- parent->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
- ASSERT_EQ(child0.get(),
- parent->LookupChildByName("subdir_new", false /* acquire */, 0 /* transforms */));
- ASSERT_EQ(child1.get(),
- parent->LookupChildByName("subdir_new", false /* acquire */, 1 /* transforms */));
-
- ASSERT_EQ("/path/subdir_new", child0->BuildPath());
- ASSERT_EQ("/path/subdir_new", child1->BuildPath());
- ASSERT_EQ(1, GetRefCount(child0.get()));
- ASSERT_EQ(1, GetRefCount(child1.get()));
-}
-
-TEST_F(NodeTest, TestRenameParentForChild) {
- unique_node_ptr parent1 = CreateNode(nullptr, "/path1");
- unique_node_ptr parent2 = CreateNode(nullptr, "/path2");
-
- unique_node_ptr child0 = CreateNode(parent1.get(), "subdir", 0 /* transforms */);
- unique_node_ptr child1 = CreateNode(parent1.get(), "subdir", 1 /* transforms */);
- ASSERT_EQ(3, GetRefCount(parent1.get()));
- ASSERT_EQ(child0.get(),
- parent1->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
- ASSERT_EQ(child1.get(),
- parent1->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
-
- parent1->RenameChild("subdir", "subdir", parent2.get());
- ASSERT_EQ(1, GetRefCount(parent1.get()));
- ASSERT_EQ(nullptr,
- parent1->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
- ASSERT_EQ(nullptr,
- parent1->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
-
- ASSERT_EQ(3, GetRefCount(parent2.get()));
- ASSERT_EQ(child0.get(),
- parent2->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
- ASSERT_EQ(child1.get(),
- parent2->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
-
- ASSERT_EQ("/path2/subdir", child0->BuildPath());
- ASSERT_EQ("/path2/subdir", child1->BuildPath());
- ASSERT_EQ(1, GetRefCount(child0.get()));
- ASSERT_EQ(1, GetRefCount(child1.get()));
-}
-
-TEST_F(NodeTest, TestRenameNameAndParentForChild) {
- unique_node_ptr parent1 = CreateNode(nullptr, "/path1");
- unique_node_ptr parent2 = CreateNode(nullptr, "/path2");
-
- unique_node_ptr child0 = CreateNode(parent1.get(), "subdir", 0 /* transforms */);
- unique_node_ptr child1 = CreateNode(parent1.get(), "subdir", 1 /* transforms */);
- ASSERT_EQ(3, GetRefCount(parent1.get()));
- ASSERT_EQ(child0.get(),
- parent1->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
- ASSERT_EQ(child1.get(),
- parent1->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
-
- parent1->RenameChild("subdir", "subdir_new", parent2.get());
- ASSERT_EQ(1, GetRefCount(parent1.get()));
- ASSERT_EQ(nullptr,
- parent1->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
- ASSERT_EQ(nullptr,
- parent1->LookupChildByName("subdir_new", false /* acquire */, 0 /* transforms */));
- ASSERT_EQ(nullptr,
- parent1->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
- ASSERT_EQ(nullptr,
- parent1->LookupChildByName("subdir_new", false /* acquire */, 1 /* transforms */));
-
- ASSERT_EQ(3, GetRefCount(parent2.get()));
- ASSERT_EQ(nullptr,
- parent1->LookupChildByName("subdir_new", false /* acquire */, 0 /* transforms */));
- ASSERT_EQ(nullptr,
- parent1->LookupChildByName("subdir_new", false /* acquire */, 1 /* transforms */));
-
- ASSERT_EQ("/path2/subdir_new", child0->BuildPath());
- ASSERT_EQ("/path2/subdir_new", child1->BuildPath());
- ASSERT_EQ(1, GetRefCount(child0.get()));
- ASSERT_EQ(1, GetRefCount(child1.get()));
-}
-
TEST_F(NodeTest, TestBuildPath) {
unique_node_ptr parent = CreateNode(nullptr, "/path");
ASSERT_EQ("/path", parent->BuildPath());
@@ -258,30 +156,14 @@
ASSERT_EQ(nullptr, parent->LookupChildByName("subdir", false /* acquire */));
}
-TEST_F(NodeTest, TestSetDeletedForChild) {
- unique_node_ptr parent = CreateNode(nullptr, "/path");
- unique_node_ptr child0 = CreateNode(parent.get(), "subdir", 0 /* transforms */);
- unique_node_ptr child1 = CreateNode(parent.get(), "subdir", 1 /* transforms */);
-
- ASSERT_EQ(child0.get(),
- parent->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
- ASSERT_EQ(child1.get(),
- parent->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
- parent->SetDeletedForChild("subdir");
- ASSERT_EQ(nullptr,
- parent->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
- ASSERT_EQ(nullptr,
- parent->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
-}
-
TEST_F(NodeTest, DeleteTree) {
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", &lock_, &tracker_);
+ node::Create(child, "s1", &lock_, &tracker_);
+ node* subchild2 = node::Create(child, "s2", &lock_, &tracker_);
+ node::Create(subchild2, "sc2", &lock_, &tracker_);
ASSERT_EQ(child, parent->LookupChildByName("subdir", false /* acquire */));
node::DeleteTree(child);
@@ -296,20 +178,6 @@
ASSERT_EQ(nullptr, parent->LookupChildByName("", false /* acquire */));
}
-TEST_F(NodeTest, LookupChildByName_transforms) {
- unique_node_ptr parent = CreateNode(nullptr, "/path");
- unique_node_ptr child0 = CreateNode(parent.get(), "subdir", 0 /* transforms */);
- unique_node_ptr child1 = CreateNode(parent.get(), "subdir", 1 /* transforms */);
-
- ASSERT_EQ(child0.get(), parent->LookupChildByName("subdir", false /* acquire */));
- ASSERT_EQ(child0.get(),
- parent->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
- ASSERT_EQ(child1.get(),
- parent->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
- ASSERT_EQ(nullptr,
- parent->LookupChildByName("subdir", false /* acquire */, 2 /* transforms */));
-}
-
TEST_F(NodeTest, LookupChildByName_refcounts) {
unique_node_ptr parent = CreateNode(nullptr, "/path");
unique_node_ptr child = CreateNode(parent.get(), "subdir");
@@ -349,8 +217,7 @@
TEST_F(NodeTest, AddDestroyHandle) {
unique_node_ptr node = CreateNode(nullptr, "/path");
- handle* h = new handle(-1, new mediaprovider::fuse::RedactionInfo, true /* cached */,
- false /* passthrough */, 0 /* uid */, 0 /* transforms_uid */);
+ handle* h = new handle(-1, new mediaprovider::fuse::RedactionInfo, true /* cached */);
node->AddHandle(h);
ASSERT_TRUE(node->HasCachedHandle());
@@ -361,9 +228,8 @@
// the node in question.
EXPECT_DEATH(node->DestroyHandle(h), "");
EXPECT_DEATH(node->DestroyHandle(nullptr), "");
- std::unique_ptr<handle> h2(new handle(-1, new mediaprovider::fuse::RedactionInfo,
- true /* cached */, false /* passthrough */, 0 /* uid */,
- 0 /* transforms_uid */));
+ std::unique_ptr<handle> h2(
+ new handle(-1, new mediaprovider::fuse::RedactionInfo, true /* cached */));
EXPECT_DEATH(node->DestroyHandle(h2.get()), "");
}
@@ -452,44 +318,3 @@
test_fn("bAr", bar1.get(), bar2.get());
test_fn("BaZ", baz1.get(), baz2.get());
}
-
-TEST_F(NodeTest, ForChild) {
- unique_node_ptr parent = CreateNode(nullptr, "/path");
- unique_node_ptr foo1 = CreateNode(parent.get(), "FoO");
- unique_node_ptr foo2 = CreateNode(parent.get(), "fOo");
- unique_node_ptr foo3 = CreateNode(parent.get(), "foo");
- foo3->SetDeleted();
-
- std::vector<node*> match_all;
- auto test_fn_match_all = [&](node* child) {
- match_all.push_back(child);
- return false;
- };
-
- std::vector<node*> match_first;
- auto test_fn_match_first = [&](node* child) {
- match_first.push_back(child);
- return true;
- };
-
- std::vector<node*> match_none;
- auto test_fn_match_none = [&](node* child) {
- match_none.push_back(child);
- return false;
- };
-
- node* node_all = ForChild(parent.get(), "foo", test_fn_match_all);
- ASSERT_EQ(nullptr, node_all);
- ASSERT_EQ(2, match_all.size());
- ASSERT_EQ(std::min(foo1.get(), foo2.get()), match_all[0]);
- ASSERT_EQ(std::max(foo1.get(), foo2.get()), match_all[1]);
-
- node* node_first = ForChild(parent.get(), "foo", test_fn_match_first);
- ASSERT_EQ(std::min(foo1.get(), foo2.get()), node_first);
- ASSERT_EQ(1, match_first.size());
- ASSERT_EQ(std::min(foo1.get(), foo2.get()), match_first[0]);
-
- node* node_none = ForChild(parent.get(), "bar", test_fn_match_none);
- ASSERT_EQ(nullptr, node_none);
- ASSERT_TRUE(match_none.empty());
-}
diff --git a/legacy/Android.bp b/legacy/Android.bp
index dd9e5f8..203ee5a 100644
--- a/legacy/Android.bp
+++ b/legacy/Android.bp
@@ -1,13 +1,4 @@
-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_app {
name: "MediaProviderLegacy",
manifest: "AndroidManifest.xml",
@@ -16,7 +7,6 @@
"androidx.appcompat_appcompat",
"androidx.core_core",
"guava",
- "modules-utils-build",
],
libs: ["app-compat-annotations"],
@@ -27,7 +17,7 @@
":mediaprovider-database-sources",
],
- platform_apis: true,
certificate: "media",
privileged: true,
+ sdk_version: "system_current",
}
diff --git a/legacy/AndroidManifest.xml b/legacy/AndroidManifest.xml
index 2f7ea97..2396f58 100644
--- a/legacy/AndroidManifest.xml
+++ b/legacy/AndroidManifest.xml
@@ -1,5 +1,4 @@
<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">
@@ -20,11 +19,5 @@
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 4c7e741..e921155 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, false, true, null,
- null, null, null, null);
- mExternalDatabase = new DatabaseHelper(context, EXTERNAL_DATABASE_NAME, false, true, null,
- null, null, null, null);
+ 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);
return true;
}
diff --git a/logging.sh b/logging.sh
index c1143ce..fd2a3a5 100755
--- a/logging.sh
+++ b/logging.sh
@@ -6,7 +6,6 @@
then
adb shell setprop log.tag.MediaProvider VERBOSE
adb shell setprop log.tag.ModernMediaScanner VERBOSE
- adb shell setprop log.tag.TranscodeHelper VERBOSE
adb shell setprop log.tag.FuseDaemon DEBUG
adb shell setprop log.tag.libfuse DEBUG
else
@@ -21,7 +20,6 @@
adb shell setprop log.tag.SQLiteQueryBuilder VERBOSE
adb shell setprop log.tag.FuseDaemon VERBOSE
adb shell setprop log.tag.libfuse VERBOSE
- adb shell setprop log.tag.TranscodeHelper VERBOSE
adb shell setprop persist.sys.fuse.log true
else
adb shell setprop log.tag.SQLiteQueryBuilder INFO
diff --git a/preinstalled-packages-com.android.providers.media.module.xml b/preinstalled-packages-com.android.providers.media.module.xml
deleted file mode 100644
index fd31162..0000000
--- a/preinstalled-packages-com.android.providers.media.module.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<config>
- <install-in-user-type package="com.android.providers.media.module">
- <install-in user-type="SYSTEM"/>
- <install-in user-type="FULL"/>
- <install-in user-type="PROFILE"/>
- <do-not-install-in user-type="android.os.usertype.profile.CLONE"/>
- </install-in-user-type>
-</config>
\ No newline at end of file
diff --git a/res/color/picker_chip_background_color.xml b/res/color/picker_chip_background_color.xml
deleted file mode 100644
index ef6b28c..0000000
--- a/res/color/picker_chip_background_color.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?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.
- -->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_selected="true" android:color="@color/picker_highlight_color"/>
- <item android:state_enabled="true" android:color="?android:attr/colorBackground"/>
-</selector>
\ No newline at end of file
diff --git a/res/color/picker_chip_ripple_color.xml b/res/color/picker_chip_ripple_color.xml
deleted file mode 100644
index cc540bd..0000000
--- a/res/color/picker_chip_ripple_color.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?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.
- -->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <!-- Selected. -->
- <item android:state_pressed="true" android:state_selected="true"
- android:alpha="0.16" android:color="?android:colorSecondary"/>
- <item android:state_focused="true" android:state_hovered="true" android:state_selected="true"
- android:alpha="0.16" android:color="?android:colorSecondary"/>
- <item android:state_focused="true" android:state_selected="true"
- android:alpha="0.12" android:color="?android:colorSecondary"/>
- <item android:state_hovered="true" android:state_selected="true"
- android:alpha="0.04" android:color="?android:colorSecondary"/>
- <item android:state_selected="true"
- android:alpha="0.00" android:color="?android:colorSecondary"/>
-
- <!-- Unselected. -->
- <item android:state_pressed="true"
- android:alpha="0.16" android:color="?android:textColorSecondary"/>
- <item android:state_focused="true" android:state_hovered="true"
- android:alpha="0.16" android:color="?android:textColorSecondary"/>
- <item android:state_focused="true"
- android:alpha="0.12" android:color="?android:textColorSecondary"/>
- <item android:state_hovered="true"
- android:alpha="0.04" android:color="?android:textColorSecondary"/>
- <item android:alpha="0.00" android:color="?android:textColorSecondary"/>
-</selector>
\ No newline at end of file
diff --git a/res/color/picker_chip_text_color.xml b/res/color/picker_chip_text_color.xml
deleted file mode 100644
index f75c7c8..0000000
--- a/res/color/picker_chip_text_color.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?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.
- -->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_selected="true" android:color="?android:colorAccent"/>
- <item android:state_enabled="true" android:color="@color/picker_toolbar_chip_text_color"/>
-</selector>
\ No newline at end of file
diff --git a/res/drawable/ic_arrow_back.xml b/res/drawable/ic_arrow_back.xml
deleted file mode 100644
index 1370c3b..0000000
--- a/res/drawable/ic_arrow_back.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?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="@color/picker_toolbar_icon_color"
- 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_check_circle_filled.xml b/res/drawable/ic_check_circle_filled.xml
deleted file mode 100644
index 5e35ba9..0000000
--- a/res/drawable/ic_check_circle_filled.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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:attr/colorAccent"
- 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
deleted file mode 100644
index e82bf74..0000000
--- a/res/drawable/ic_close.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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="@color/picker_toolbar_icon_color">
- <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
deleted file mode 100644
index ad76643..0000000
--- a/res/drawable/ic_collections.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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="?android:attr/colorAccent">
- <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_gif.xml b/res/drawable/ic_gif.xml
deleted file mode 100644
index f0d1c98..0000000
--- a/res/drawable/ic_gif.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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
deleted file mode 100644
index 7be17dd..0000000
--- a/res/drawable/ic_lock.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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_personal_mode.xml b/res/drawable/ic_personal_mode.xml
deleted file mode 100644
index cf4562c..0000000
--- a/res/drawable/ic_personal_mode.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24"
- android:tint="@color/picker_profile_button_content_color">
- <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
deleted file mode 100644
index e7509a2..0000000
--- a/res/drawable/ic_play_circle_filled.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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_radio_button_unchecked.xml b/res/drawable/ic_radio_button_unchecked.xml
deleted file mode 100644
index 622010e..0000000
--- a/res/drawable/ic_radio_button_unchecked.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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="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_work_outline.xml b/res/drawable/ic_work_outline.xml
deleted file mode 100644
index 71d0e45..0000000
--- a/res/drawable/ic_work_outline.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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="@color/picker_profile_button_content_color">
- <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
deleted file mode 100644
index fb0ef88..0000000
--- a/res/drawable/picker_item_check.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?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
deleted file mode 100644
index 4793d35..0000000
--- a/res/drawable/picker_item_gradient.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?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/preview_check.xml b/res/drawable/preview_check.xml
deleted file mode 100644
index c523a9e..0000000
--- a/res/drawable/preview_check.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?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
deleted file mode 100644
index 3f03abd..0000000
--- a/res/drawable/preview_gradient_asc.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?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
deleted file mode 100644
index c68c70e..0000000
--- a/res/drawable/preview_gradient_desc.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?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
deleted file mode 100644
index 688b4ec..0000000
--- a/res/layout/activity_photo_picker.xml
+++ /dev/null
@@ -1,69 +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.
- -->
-
-<!-- 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">
-
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:id="@+id/bottom_sheet"
- android:background="?android:attr/colorBackground"
- android:clipToOutline="true"
- app:behavior_hideable="true"
- app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
-
- <androidx.fragment.app.FragmentContainerView
- android:id="@+id/fragment_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
-
- <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="?android:attr/colorBackground"
- app:titleTextColor="@color/picker_toolbar_title_color">
-
- <LinearLayout
- android:id="@+id/chip_container"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:orientation="horizontal"/>
-
- </androidx.appcompat.widget.Toolbar>
- </com.google.android.material.appbar.AppBarLayout>
-
- </FrameLayout>
-
-</androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/res/layout/fragment_picker_tab.xml b/res/layout/fragment_picker_tab.xml
deleted file mode 100644
index 5fb1311..0000000
--- a/res/layout/fragment_picker_tab.xml
+++ /dev/null
@@ -1,83 +0,0 @@
-<?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.android.providers.media.photopicker.ui.AutoFitRecyclerView
- android:id="@+id/photo_list"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipToPadding="false"
- android:drawSelectorOnTop="true"
- android:overScrollMode="never"/>
-
- <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/PickerProfileButton"
- android:textColor="@color/picker_profile_button_content_color"
- android:text="@string/picker_work_profile"
- android:visibility="gone"
- app:backgroundTint="@color/picker_profile_button_background_color"
- app:borderWidth="0dp"
- app:elevation="3dp"
- app:icon="@drawable/ic_work_outline"
- />
-
- <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="match_parent"
- android:layout_marginHorizontal="@dimen/picker_bottom_bar_horizontal_gap"
- android:layout_gravity="start"
- android:paddingVertical="@dimen/picker_bottom_bar_vertical_gap"
- android:drawableLeft="@drawable/ic_collections"
- android:text="@string/picker_view_selected"
- android:textAllCaps="false"
- android:textColor="?android:attr/colorAccent"
- app:iconPadding="@dimen/picker_bottom_bar_vertical_gap"
- style="?attr/borderlessButtonStyle"/>
-
- <Button
- android:id="@+id/button_add"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginHorizontal="@dimen/picker_bottom_bar_horizontal_gap"
- android:layout_gravity="end"
- android:paddingVertical="@dimen/picker_bottom_bar_vertical_gap"
- android:text="@string/add"
- android:textAllCaps="false"
- android:backgroundTint="?android:attr/colorAccent"
- style="?attr/materialButtonStyle"/>
-
- </FrameLayout>
-</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/fragment_preview.xml b/res/layout/fragment_preview.xml
deleted file mode 100644
index 1cace16..0000000
--- a/res/layout/fragment_preview.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="@color/preview_default_black"
- 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"/>
-
- <!-- Adds scrim for Toolbar -->
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="@dimen/preview_toolbar_scrim_height"
- android:background="@drawable/preview_gradient_desc"
- android:layout_gravity="top"/>
-
- <!-- Adds scrim for deselect and Add button -->
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="@dimen/preview_deselect_scrim_height"
- android:background="@drawable/preview_gradient_asc"
- android:layout_gravity="bottom"/>
-
- <FrameLayout
- android:layout_marginHorizontal="@dimen/preview_buttons_margin_horizontal"
- android:layout_gravity="bottom"
- android:layout_marginBottom="@dimen/preview_buttons_margin_bottom"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <Button
- android:id="@+id/preview_select_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:background="@android:color/transparent"
- android:drawableLeft="@drawable/preview_check"
- android:drawableTint="@color/preview_default_blue"
- android:textAllCaps="false"
- android:text="@string/deselect"
- android:textColor="@color/picker_default_white"
- android:textSize="@dimen/preview_deselect_text_size" />
-
- <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_default_blue"
- android:text="@string/add"
- android:textAllCaps="false"
- android:textColor="@color/preview_default_grey"
- android:textSize="@dimen/preview_add_text_size"/>
- </FrameLayout>
-</FrameLayout>
diff --git a/res/layout/item_album_grid.xml b/res/layout/item_album_grid.xml
deleted file mode 100644
index 8c148c1..0000000
--- a/res/layout/item_album_grid.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?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">
-
- <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="@dimen/picker_album_name_min_height"
- android:layout_marginTop="@dimen/picker_album_name_margin"
- android:textAppearance="?attr/textAppearanceSubtitle2"/>
-
- <TextView
- android:id="@+id/item_count"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/picker_album_item_count_height"
- android:layout_marginTop="@dimen/picker_album_item_count_margin"
- android:textAppearance="?attr/textAppearanceCaption"/>
-
-</LinearLayout>
diff --git a/res/layout/item_date_header.xml b/res/layout/item_date_header.xml
deleted file mode 100644
index fd6f826..0000000
--- a/res/layout/item_date_header.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?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="@dimen/picker_date_header_height"
- android:padding="@dimen/picker_date_header_padding"
- android:textAppearance="@style/PickerDateHeader"/>
\ No newline at end of file
diff --git a/res/layout/item_image_preview.xml b/res/layout/item_image_preview.xml
deleted file mode 100644
index 932ce58..0000000
--- a/res/layout/item_image_preview.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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
deleted file mode 100644
index cecf10d..0000000
--- a/res/layout/item_photo_grid.xml
+++ /dev/null
@@ -1,102 +0,0 @@
-<?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:background="@color/picker_highlight_color"
- 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">
-
- <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:layout_marginEnd="@dimen/picker_item_gif_badge_margin"
- android:layout_marginTop="@dimen/picker_item_gif_badge_margin"
- android:scaleType="fitCenter"
- android:src="@drawable/ic_gif"
- 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
deleted file mode 100644
index f1b5274..0000000
--- a/res/layout/item_video_preview.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <VideoView
- android:id="@+id/preview_videoView"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:contentDescription="@null"/>
-</FrameLayout>
diff --git a/res/layout/permission_body.xml b/res/layout/permission_body.xml
index 7c74065..0fb4ece 100644
--- a/res/layout/permission_body.xml
+++ b/res/layout/permission_body.xml
@@ -97,5 +97,11 @@
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:visibility="gone" />
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="12dp"
+ android:visibility="gone" />
</LinearLayout>
</LinearLayout>
diff --git a/res/layout/picker_chip_tab_header.xml b/res/layout/picker_chip_tab_header.xml
deleted file mode 100644
index f081b98..0000000
--- a/res/layout/picker_chip_tab_header.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
--->
-
-<com.google.android.material.chip.Chip
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:textAppearance="@style/PickerChipText"
- android:textColor="@color/picker_chip_text_color"
- app:chipBackgroundColor="@color/picker_chip_background_color"
- app:chipCornerRadius="@dimen/picker_chip_radius"
- app:chipStrokeWidth="0dp"
- app:rippleColor="@color/picker_chip_ripple_color"
- app:chipMinTouchTargetSize="@dimen/picker_chip_touch_size"/>
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index c1b94ac..09a1296 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Vee uit"</string>
<string name="allow" msgid="8885707816848569619">"Laat toe"</string>
<string name="deny" msgid="6040983710442068936">"Weier"</string>
- <string name="add" msgid="2894574044585549298">"Voeg by"</string>
- <string name="deselect" msgid="4297825044827769490">"Ontkies"</string>
- <string name="select" msgid="2704765470563027689">"Kies"</string>
- <string name="recent" msgid="6694613584743207874">"Onlangs"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Bekyk geselekteerde"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Mediaverwerkingsukses"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Mediaverwerking het begin"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Verwerk media …"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Kanselleer"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Wag"</string>
</resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index cc4bd5d..1a62bf5 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"አጽዳ"</string>
<string name="allow" msgid="8885707816848569619">"ፍቀድ"</string>
<string name="deny" msgid="6040983710442068936">"ከልክል"</string>
- <string name="add" msgid="2894574044585549298">"አክል"</string>
- <string name="deselect" msgid="4297825044827769490">"አትምረጥ"</string>
- <string name="select" msgid="2704765470563027689">"ምረጥ"</string>
- <string name="recent" msgid="6694613584743207874">"የቅርብ ጊዜ"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"የተመረጡትን አሳይ"</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="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_wait" msgid="8909773149560697501">"ጠብቅ"</string>
</resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index f7764e4..c5d7cea 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -51,11 +51,6 @@
<string name="clear" msgid="5524638938415865915">"محو"</string>
<string name="allow" msgid="8885707816848569619">"سماح"</string>
<string name="deny" msgid="6040983710442068936">"رفض"</string>
- <string name="add" msgid="2894574044585549298">"إضافة"</string>
- <string name="deselect" msgid="4297825044827769490">"إلغاء الاختيار"</string>
- <string name="select" msgid="2704765470563027689">"اختيار"</string>
- <string name="recent" msgid="6694613584743207874">"الأحدث"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"عرض المحتوى المحدّد"</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>
@@ -64,14 +59,6 @@
<item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g 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>
@@ -80,14 +67,6 @@
<item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g 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>
@@ -96,14 +75,6 @@
<item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g 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>
@@ -112,14 +83,6 @@
<item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g 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>
@@ -128,14 +91,6 @@
<item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g 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>
@@ -144,14 +99,6 @@
<item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g 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>
@@ -160,14 +107,6 @@
<item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g 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>
@@ -176,14 +115,6 @@
<item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g 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>
@@ -192,14 +123,6 @@
<item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g 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>
@@ -208,14 +131,6 @@
<item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g 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>
@@ -224,14 +139,6 @@
<item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g 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>
@@ -240,14 +147,6 @@
<item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g 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>
@@ -256,14 +155,6 @@
<item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g 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>
@@ -272,14 +163,6 @@
<item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g 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>
@@ -288,14 +171,6 @@
<item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g 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>
@@ -304,20 +179,4 @@
<item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g 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="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_wait" msgid="8909773149560697501">"الانتظار"</string>
</resources>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index ee1054a..6737fca 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"মচক"</string>
<string name="allow" msgid="8885707816848569619">"অনুমতি দিয়ক"</string>
<string name="deny" msgid="6040983710442068936">"অস্বীকাৰ কৰক"</string>
- <string name="add" msgid="2894574044585549298">"যোগ দিয়ক"</string>
- <string name="deselect" msgid="4297825044827769490">"বাছনিৰ পৰা আঁতৰাওক"</string>
- <string name="select" msgid="2704765470563027689">"বাছনি কৰক"</string>
- <string name="recent" msgid="6694613584743207874">"শেহতীয়া"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"ভিউ বাছনি কৰা হৈছে"</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="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_wait" msgid="8909773149560697501">"অপেক্ষা কৰক"</string>
</resources>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 1e2c552..a8d2ab3 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Silin"</string>
<string name="allow" msgid="8885707816848569619">"İcazə verin"</string>
<string name="deny" msgid="6040983710442068936">"Rədd edin"</string>
- <string name="add" msgid="2894574044585549298">"Əlavə edin"</string>
- <string name="deselect" msgid="4297825044827769490">"Seçimi ləğv edin"</string>
- <string name="select" msgid="2704765470563027689">"Seçin"</string>
- <string name="recent" msgid="6694613584743207874">"Son"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Seçilənə baxın"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Media emalı uğurlu oldu"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Media emalı başladılıb"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Media emal edilir…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Ləğv edin"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Gözləyin"</string>
</resources>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index fa5ea8e..9756151 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -45,177 +45,84 @@
<string name="clear" msgid="5524638938415865915">"Obriši"</string>
<string name="allow" msgid="8885707816848569619">"Dozvoli"</string>
<string name="deny" msgid="6040983710442068936">"Odbij"</string>
- <string name="add" msgid="2894574044585549298">"Dodaj"</string>
- <string name="deselect" msgid="4297825044827769490">"Opozovi izbor"</string>
- <string name="select" msgid="2704765470563027689">"Izaberi"</string>
- <string name="recent" msgid="6694613584743207874">"Nedavno"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Prikaži izabrano"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Obrada medija je uspela"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Obrada medija je započela"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Obrađuju se mediji…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Otkaži"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Sačekaj"</string>
</resources>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 51cb9fb..58bffee 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -47,209 +47,100 @@
<string name="clear" msgid="5524638938415865915">"Ачысціць"</string>
<string name="allow" msgid="8885707816848569619">"Дазволіць"</string>
<string name="deny" msgid="6040983710442068936">"Адмовіць"</string>
- <string name="add" msgid="2894574044585549298">"Дадаць"</string>
- <string name="deselect" msgid="4297825044827769490">"Адмяніць выбар"</string>
- <string name="select" msgid="2704765470563027689">"Выбраць"</string>
- <string name="recent" msgid="6694613584743207874">"Нядаўнія"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Праглядзець выбранае"</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="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_wait" msgid="8909773149560697501">"Пачакаць"</string>
</resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index fdddeb5..b576228 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Изчистване"</string>
<string name="allow" msgid="8885707816848569619">"Разрешаване"</string>
<string name="deny" msgid="6040983710442068936">"Отказ"</string>
- <string name="add" msgid="2894574044585549298">"Добавяне"</string>
- <string name="deselect" msgid="4297825044827769490">"Премахване на избора"</string>
- <string name="select" msgid="2704765470563027689">"Избиране"</string>
- <string name="recent" msgid="6694613584743207874">"Скорошно"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Преглед на избраното"</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="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_wait" msgid="8909773149560697501">"Изчакване"</string>
</resources>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index c8ed506..552556e 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"সরান"</string>
<string name="allow" msgid="8885707816848569619">"অনুমতি দিন"</string>
<string name="deny" msgid="6040983710442068936">"বাতিল করুন"</string>
- <string name="add" msgid="2894574044585549298">"যোগ করুন"</string>
- <string name="deselect" msgid="4297825044827769490">"টিক চিহ্নটি সরিয়ে দিন"</string>
- <string name="select" msgid="2704765470563027689">"বেছে নিন"</string>
- <string name="recent" msgid="6694613584743207874">"সাম্প্রতিক"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"ভিউ বেছে নেওয়া হয়েছে"</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="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_wait" msgid="8909773149560697501">"অপেক্ষা করুন"</string>
</resources>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index c6bf66b..9f89481 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -45,177 +45,84 @@
<string name="clear" msgid="5524638938415865915">"Obriši"</string>
<string name="allow" msgid="8885707816848569619">"Dozvoli"</string>
<string name="deny" msgid="6040983710442068936">"Odbij"</string>
- <string name="add" msgid="2894574044585549298">"Dodaj"</string>
- <string name="deselect" msgid="4297825044827769490">"Poništi odabir"</string>
- <string name="select" msgid="2704765470563027689">"Odaberi"</string>
- <string name="recent" msgid="6694613584743207874">"Nedavno"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Prikaži odabrano"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Obrada medijskih fajlova je uspjela"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Obrada medijskih fajlova je započeta"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Obrada medijskih fajlova…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Otkaži"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Sačekaj"</string>
</resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 7fc92b7..184a9b9 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Esborra"</string>
<string name="allow" msgid="8885707816848569619">"Permet"</string>
<string name="deny" msgid="6040983710442068936">"Denega"</string>
- <string name="add" msgid="2894574044585549298">"Afegeix"</string>
- <string name="deselect" msgid="4297825044827769490">"Desselecciona"</string>
- <string name="select" msgid="2704765470563027689">"Selecciona"</string>
- <string name="recent" msgid="6694613584743207874">"Recent"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Mostra els elements seleccionats"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"El contingut multimèdia s\'ha processat correctament"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"El processament del contingut multimèdia s\'ha iniciat"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"S\'està processant el contingut multimèdia…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Cancel·la"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Espera"</string>
</resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index a5cf06b..3fa3e40 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -47,209 +47,100 @@
<string name="clear" msgid="5524638938415865915">"Vymazat"</string>
<string name="allow" msgid="8885707816848569619">"Povolit"</string>
<string name="deny" msgid="6040983710442068936">"Zakázat"</string>
- <string name="add" msgid="2894574044585549298">"Přidat"</string>
- <string name="deselect" msgid="4297825044827769490">"Zrušit výběr"</string>
- <string name="select" msgid="2704765470563027689">"Vybrat"</string>
- <string name="recent" msgid="6694613584743207874">"Aktuální"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Zobrazit vybrané"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Zpracování mediálního obsahu bylo úspěšně dokončeno"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Zpracování mediálního obsahu bylo zahájeno"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Mediální obsah se zpracovává…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Zrušit"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Počkat"</string>
</resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index a1e77cf..96ee522 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Ryd"</string>
<string name="allow" msgid="8885707816848569619">"Tillad"</string>
<string name="deny" msgid="6040983710442068936">"Afvis"</string>
- <string name="add" msgid="2894574044585549298">"Tilføj"</string>
- <string name="deselect" msgid="4297825044827769490">"Fravælg"</string>
- <string name="select" msgid="2704765470563027689">"Vælg"</string>
- <string name="recent" msgid="6694613584743207874">"Seneste"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Visning valgt"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Mediebehandlingen er fuldført"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Mediebehandlingen er startet"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Behandler medier…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Annuller"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Vent"</string>
</resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 1ae4de1..8b19ff5 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Löschen"</string>
<string name="allow" msgid="8885707816848569619">"Zulassen"</string>
<string name="deny" msgid="6040983710442068936">"Ablehnen"</string>
- <string name="add" msgid="2894574044585549298">"Hinzufügen"</string>
- <string name="deselect" msgid="4297825044827769490">"Auswahl aufheben"</string>
- <string name="select" msgid="2704765470563027689">"Auswählen"</string>
- <string name="recent" msgid="6694613584743207874">"Neueste"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Auswahl ansehen"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Medienverarbeitung erfolgreich"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Medienverarbeitung gestartet"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Medien werden verarbeitet…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Abbrechen"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Warten"</string>
</resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 8edd41e..7045ca0 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Διαγραφή"</string>
<string name="allow" msgid="8885707816848569619">"Να επιτρέπεται"</string>
<string name="deny" msgid="6040983710442068936">"Απόρριψη"</string>
- <string name="add" msgid="2894574044585549298">"Προσθήκη"</string>
- <string name="deselect" msgid="4297825044827769490">"Αποεπιλογή"</string>
- <string name="select" msgid="2704765470563027689">"Επιλογή"</string>
- <string name="recent" msgid="6694613584743207874">"Πρόσφατα"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Προβολή επιλεγμένων"</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="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_wait" msgid="8909773149560697501">"Αναμονή"</string>
</resources>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 102ba27..1205a7e 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Clear"</string>
<string name="allow" msgid="8885707816848569619">"Allow"</string>
<string name="deny" msgid="6040983710442068936">"Deny"</string>
- <string name="add" msgid="2894574044585549298">"Add"</string>
- <string name="deselect" msgid="4297825044827769490">"Deselect"</string>
- <string name="select" msgid="2704765470563027689">"Select"</string>
- <string name="recent" msgid="6694613584743207874">"Recent"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"View selected"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Media processing success"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Media processing started"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Processing media…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Cancel"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Wait"</string>
</resources>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index 102ba27..1205a7e 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Clear"</string>
<string name="allow" msgid="8885707816848569619">"Allow"</string>
<string name="deny" msgid="6040983710442068936">"Deny"</string>
- <string name="add" msgid="2894574044585549298">"Add"</string>
- <string name="deselect" msgid="4297825044827769490">"Deselect"</string>
- <string name="select" msgid="2704765470563027689">"Select"</string>
- <string name="recent" msgid="6694613584743207874">"Recent"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"View selected"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Media processing success"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Media processing started"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Processing media…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Cancel"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Wait"</string>
</resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 102ba27..1205a7e 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Clear"</string>
<string name="allow" msgid="8885707816848569619">"Allow"</string>
<string name="deny" msgid="6040983710442068936">"Deny"</string>
- <string name="add" msgid="2894574044585549298">"Add"</string>
- <string name="deselect" msgid="4297825044827769490">"Deselect"</string>
- <string name="select" msgid="2704765470563027689">"Select"</string>
- <string name="recent" msgid="6694613584743207874">"Recent"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"View selected"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Media processing success"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Media processing started"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Processing media…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Cancel"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Wait"</string>
</resources>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 102ba27..1205a7e 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Clear"</string>
<string name="allow" msgid="8885707816848569619">"Allow"</string>
<string name="deny" msgid="6040983710442068936">"Deny"</string>
- <string name="add" msgid="2894574044585549298">"Add"</string>
- <string name="deselect" msgid="4297825044827769490">"Deselect"</string>
- <string name="select" msgid="2704765470563027689">"Select"</string>
- <string name="recent" msgid="6694613584743207874">"Recent"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"View selected"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Media processing success"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Media processing started"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Processing media…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Cancel"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Wait"</string>
</resources>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index a8216af..18ba860 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Clear"</string>
<string name="allow" msgid="8885707816848569619">"Allow"</string>
<string name="deny" msgid="6040983710442068936">"Deny"</string>
- <string name="add" msgid="2894574044585549298">"Add"</string>
- <string name="deselect" msgid="4297825044827769490">"Deselect"</string>
- <string name="select" msgid="2704765470563027689">"Select"</string>
- <string name="recent" msgid="6694613584743207874">"Recent"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"View selected"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Media processing success"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Media processing started"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Processing media…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Cancel"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Wait"</string>
</resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 54cea55..e2ffe99 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Borrar"</string>
<string name="allow" msgid="8885707816848569619">"Permitir"</string>
<string name="deny" msgid="6040983710442068936">"Rechazar"</string>
- <string name="add" msgid="2894574044585549298">"Agregar"</string>
- <string name="deselect" msgid="4297825044827769490">"Anular la selección"</string>
- <string name="select" msgid="2704765470563027689">"Seleccionar"</string>
- <string name="recent" msgid="6694613584743207874">"Recientes"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Ver contenido seleccionado"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Se procesó correctamente el contenido multimedia"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Comenzó el procesamiento de contenido multimedia"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Procesando contenido multimedia…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Cancelar"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Esperar"</string>
</resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 5da93d3..7d0ee18 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Borrar"</string>
<string name="allow" msgid="8885707816848569619">"Permitir"</string>
<string name="deny" msgid="6040983710442068936">"Denegar"</string>
- <string name="add" msgid="2894574044585549298">"Añadir"</string>
- <string name="deselect" msgid="4297825044827769490">"Deseleccionar"</string>
- <string name="select" msgid="2704765470563027689">"Seleccionar"</string>
- <string name="recent" msgid="6694613584743207874">"Reciente"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Ver elementos seleccionados"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Elementos multimedia procesados"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Procesamiento de elementos multimedia iniciado"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Procesando elementos multimedia…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Cancelar"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Espera"</string>
</resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index ab6bd35..1dbfc5c 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Kustuta"</string>
<string name="allow" msgid="8885707816848569619">"Luba"</string>
<string name="deny" msgid="6040983710442068936">"Keela"</string>
- <string name="add" msgid="2894574044585549298">"Lisa"</string>
- <string name="deselect" msgid="4297825044827769490">"Tühista valik"</string>
- <string name="select" msgid="2704765470563027689">"Vali"</string>
- <string name="recent" msgid="6694613584743207874">"Hiljutised"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Kuva valitud"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Meedia töötlemine õnnestus"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Alustati meedia töötlemist"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Meedia töötlemine …"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Tühista"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Oota"</string>
</resources>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index d312938..a4b655a 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Garbitu"</string>
<string name="allow" msgid="8885707816848569619">"Eman baimena"</string>
<string name="deny" msgid="6040983710442068936">"Ukatu"</string>
- <string name="add" msgid="2894574044585549298">"Gehitu"</string>
- <string name="deselect" msgid="4297825044827769490">"Desautatu"</string>
- <string name="select" msgid="2704765470563027689">"Hautatu"</string>
- <string name="recent" msgid="6694613584743207874">"Azkena"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Ikusi hautatutakoak"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Prozesatu da multimedia-edukia"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Hasi da multimedia-edukiaren prozesamendua"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Multimedia-edukia prozesatzen…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Utzi"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Itxaron"</string>
</resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index da05d51..63ef764 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"پاک کردن"</string>
<string name="allow" msgid="8885707816848569619">"اجازه دادن"</string>
<string name="deny" msgid="6040983710442068936">"مجاز نبودن"</string>
- <string name="add" msgid="2894574044585549298">"افزودن"</string>
- <string name="deselect" msgid="4297825044827769490">"لغو انتخاب"</string>
- <string name="select" msgid="2704765470563027689">"انتخاب"</string>
- <string name="recent" msgid="6694613584743207874">"اخیر"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"مشاهده موارد انتخابشده"</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="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_wait" msgid="8909773149560697501">"انتظار"</string>
</resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index dfdd7bf..39517ba 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Poista"</string>
<string name="allow" msgid="8885707816848569619">"Salli"</string>
<string name="deny" msgid="6040983710442068936">"Estä"</string>
- <string name="add" msgid="2894574044585549298">"Lisää"</string>
- <string name="deselect" msgid="4297825044827769490">"Poista valinta"</string>
- <string name="select" msgid="2704765470563027689">"Valitse"</string>
- <string name="recent" msgid="6694613584743207874">"Viimeisimmät"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Katso valitut"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Mediasisällön käsittely onnistui"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Mediasisällön käsittely alkoi"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Käsitellään mediasisältöä…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Peru"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Odota"</string>
</resources>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 0dc87ac..021ba70 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Effacer"</string>
<string name="allow" msgid="8885707816848569619">"Autoriser"</string>
<string name="deny" msgid="6040983710442068936">"Refuser"</string>
- <string name="add" msgid="2894574044585549298">"Ajouter"</string>
- <string name="deselect" msgid="4297825044827769490">"Désélectionner"</string>
- <string name="select" msgid="2704765470563027689">"Sélectionner"</string>
- <string name="recent" msgid="6694613584743207874">"Récent"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Afficher le contenu sélectionné"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Le traitement du contenu multimédia a réussi"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Le traitement du contenu multimédia a démarré"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Traitement du contenu multimédia en cours…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Annuler"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Patienter"</string>
</resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index b7e5df7..7b0d694 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Effacer"</string>
<string name="allow" msgid="8885707816848569619">"Autoriser"</string>
<string name="deny" msgid="6040983710442068936">"Refuser"</string>
- <string name="add" msgid="2894574044585549298">"Ajouter"</string>
- <string name="deselect" msgid="4297825044827769490">"Désélectionner"</string>
- <string name="select" msgid="2704765470563027689">"Sélectionner"</string>
- <string name="recent" msgid="6694613584743207874">"Récente(s)"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Afficher la sélection"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Le traitement des contenus multimédias a réussi"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Le traitement des contenus multimédias a commencé"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Traitement des contenus multimédias…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Annuler"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Attendre"</string>
</resources>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 87106b0..93e281d 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Borrar"</string>
<string name="allow" msgid="8885707816848569619">"Permitir"</string>
<string name="deny" msgid="6040983710442068936">"Denegar"</string>
- <string name="add" msgid="2894574044585549298">"Engadir"</string>
- <string name="deselect" msgid="4297825044827769490">"Anular selección"</string>
- <string name="select" msgid="2704765470563027689">"Seleccionar"</string>
- <string name="recent" msgid="6694613584743207874">"Recentes"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Ver elemento seleccionado"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Realizouse correctamente o procesamento do contido multimedia"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Iniciouse o procesamento do contido multimedia"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Procesando contido multimedia…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Cancelar"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Esperar"</string>
</resources>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 84a9457..e7c5619 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"સાફ કરો"</string>
<string name="allow" msgid="8885707816848569619">"મંજૂરી આપો"</string>
<string name="deny" msgid="6040983710442068936">"નકારો"</string>
- <string name="add" msgid="2894574044585549298">"ઉમેરો"</string>
- <string name="deselect" msgid="4297825044827769490">"નાપસંદ કરો"</string>
- <string name="select" msgid="2704765470563027689">"પસંદ કરો"</string>
- <string name="recent" msgid="6694613584743207874">"તાજેતરના"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"પસંદ કરેલી ટેક્સ્ટ જુઓ"</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="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_wait" msgid="8909773149560697501">"રાહ જુઓ"</string>
</resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index cc7be73..3937bd2 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"मिटाएं"</string>
<string name="allow" msgid="8885707816848569619">"अनुमति दें"</string>
<string name="deny" msgid="6040983710442068936">"अनुमति न दें"</string>
- <string name="add" msgid="2894574044585549298">"जोड़ें"</string>
- <string name="deselect" msgid="4297825044827769490">"चुना हुआ हटाएं"</string>
- <string name="select" msgid="2704765470563027689">"चुनें"</string>
- <string name="recent" msgid="6694613584743207874">"हाल ही की फ़ोटो और वीडियो"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"चुनी गई फ़ोटो या वीडियो देखें"</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="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_wait" msgid="8909773149560697501">"इंतज़ार करें"</string>
</resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index b7104bb..6b4abd9 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -45,177 +45,84 @@
<string name="clear" msgid="5524638938415865915">"Izbriši"</string>
<string name="allow" msgid="8885707816848569619">"Dopusti"</string>
<string name="deny" msgid="6040983710442068936">"Odbij"</string>
- <string name="add" msgid="2894574044585549298">"Dodaj"</string>
- <string name="deselect" msgid="4297825044827769490">"Poništi odabir"</string>
- <string name="select" msgid="2704765470563027689">"Odaberi"</string>
- <string name="recent" msgid="6694613584743207874">"Nedavno"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Prikaži odabrano"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Obrada medijskih sadržaja uspješno je dovršena"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Započela je obrada medijskih sadržaja"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Obrada medijskih sadržaja…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Odustani"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Pričekaj"</string>
</resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 0ac8127..1e39499 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -43,145 +43,68 @@
<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>
- <string name="add" msgid="2894574044585549298">"Hozzáadás"</string>
- <string name="deselect" msgid="4297825044827769490">"Jelölés törlése"</string>
- <string name="select" msgid="2704765470563027689">"Kiválasztás"</string>
- <string name="recent" msgid="6694613584743207874">"Legújabbak"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Kijelöltek megtekintése"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Médiatartalom feldolgozása sikeres"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Médiatartalom feldolgozása megkezdődött"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Médiatartalom feldolgozása…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Mégse"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Várakozás"</string>
</resources>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 3b18b51..862e27a 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Ջնջել"</string>
<string name="allow" msgid="8885707816848569619">"Թույլատրել"</string>
<string name="deny" msgid="6040983710442068936">"Մերժել"</string>
- <string name="add" msgid="2894574044585549298">"Ավելացնել"</string>
- <string name="deselect" msgid="4297825044827769490">"Ապընտրել"</string>
- <string name="select" msgid="2704765470563027689">"Ընտրել"</string>
- <string name="recent" msgid="6694613584743207874">"Վերջինները"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Դիտել ընտրությունը"</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="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_wait" msgid="8909773149560697501">"Սպասել"</string>
</resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 68f7213..42406d7 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Hapus"</string>
<string name="allow" msgid="8885707816848569619">"Izinkan"</string>
<string name="deny" msgid="6040983710442068936">"Tolak"</string>
- <string name="add" msgid="2894574044585549298">"Tambahkan"</string>
- <string name="deselect" msgid="4297825044827769490">"Batalkan pilihan"</string>
- <string name="select" msgid="2704765470563027689">"Pilih"</string>
- <string name="recent" msgid="6694613584743207874">"Terbaru"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Lihat yang dipilih"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Pemrosesan media berhasil"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Pemrosesan media dimulai"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Memproses media…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Batal"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Tunggu"</string>
</resources>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 57ab4ab..fb7daa3 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Hreinsa"</string>
<string name="allow" msgid="8885707816848569619">"Leyfa"</string>
<string name="deny" msgid="6040983710442068936">"Hafna"</string>
- <string name="add" msgid="2894574044585549298">"Bæta við"</string>
- <string name="deselect" msgid="4297825044827769490">"Afvelja"</string>
- <string name="select" msgid="2704765470563027689">"Velja"</string>
- <string name="recent" msgid="6694613584743207874">"Nýlegt"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Skoða valið"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Úrvinnsla efnis tókst"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Úrvinnsla efnis hafin"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Vinnur úr efni…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Hætta við"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Bíða"</string>
</resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index fc0f260..9b5d598 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Cancella"</string>
<string name="allow" msgid="8885707816848569619">"Consenti"</string>
<string name="deny" msgid="6040983710442068936">"Rifiuta"</string>
- <string name="add" msgid="2894574044585549298">"Aggiungi"</string>
- <string name="deselect" msgid="4297825044827769490">"Deseleziona"</string>
- <string name="select" msgid="2704765470563027689">"Seleziona"</string>
- <string name="recent" msgid="6694613584743207874">"Recenti"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Visualizza selezione"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Elaborazione dei contenuti multimediali riuscita"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Elaborazione dei contenuti multimediali avviata"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Elaborazione dei contenuti multimediali in corso…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Annulla"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Attendi"</string>
</resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 44230b3..e7011df 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -47,209 +47,100 @@
<string name="clear" msgid="5524638938415865915">"ניקוי"</string>
<string name="allow" msgid="8885707816848569619">"אישור"</string>
<string name="deny" msgid="6040983710442068936">"דחייה"</string>
- <string name="add" msgid="2894574044585549298">"הוספה"</string>
- <string name="deselect" msgid="4297825044827769490">"ביטול הבחירה"</string>
- <string name="select" msgid="2704765470563027689">"בחירה"</string>
- <string name="recent" msgid="6694613584743207874">"אחרונות"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"הצגת הפריטים שנבחרו"</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="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_wait" msgid="8909773149560697501">"המתנה"</string>
</resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 000e448..481c730 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"削除"</string>
<string name="allow" msgid="8885707816848569619">"許可"</string>
<string name="deny" msgid="6040983710442068936">"許可しない"</string>
- <string name="add" msgid="2894574044585549298">"追加"</string>
- <string name="deselect" msgid="4297825044827769490">"選択を解除"</string>
- <string name="select" msgid="2704765470563027689">"選択"</string>
- <string name="recent" msgid="6694613584743207874">"最近"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"選択した写真を見る"</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="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_wait" msgid="8909773149560697501">"待機"</string>
</resources>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 1b671d2..420e846 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"გასუფთავება"</string>
<string name="allow" msgid="8885707816848569619">"დაშვება"</string>
<string name="deny" msgid="6040983710442068936">"უარყოფა"</string>
- <string name="add" msgid="2894574044585549298">"დამატება"</string>
- <string name="deselect" msgid="4297825044827769490">"არჩევის გაუქმება"</string>
- <string name="select" msgid="2704765470563027689">"არჩევა"</string>
- <string name="recent" msgid="6694613584743207874">"ბოლოდროინდელი"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"ხედი არჩეულია"</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="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_wait" msgid="8909773149560697501">"მოცდა"</string>
</resources>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 576611f..1b899b2 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Өшіру"</string>
<string name="allow" msgid="8885707816848569619">"Рұқсат ету"</string>
<string name="deny" msgid="6040983710442068936">"Тыйым салу"</string>
- <string name="add" msgid="2894574044585549298">"Қосу"</string>
- <string name="deselect" msgid="4297825044827769490">"Белгісін алу"</string>
- <string name="select" msgid="2704765470563027689">"Таңдау"</string>
- <string name="recent" msgid="6694613584743207874">"Соңғы"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Таңдалғанды көру"</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="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_wait" msgid="8909773149560697501">"Күту"</string>
</resources>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index c5cfdc3..b4a930f 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"សម្អាត"</string>
<string name="allow" msgid="8885707816848569619">"អនុញ្ញាត"</string>
<string name="deny" msgid="6040983710442068936">"បដិសេធ"</string>
- <string name="add" msgid="2894574044585549298">"បញ្ចូល"</string>
- <string name="deselect" msgid="4297825044827769490">"ដកការជ្រើសរើស"</string>
- <string name="select" msgid="2704765470563027689">"ជ្រើសរើស"</string>
- <string name="recent" msgid="6694613584743207874">"ថ្មីៗ"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"បានជ្រើសរើសទិដ្ឋភាព"</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="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_wait" msgid="8909773149560697501">"រង់ចាំ"</string>
</resources>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 431e6d8..a39e574 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"ತೆರವುಗೊಳಿಸಿ"</string>
<string name="allow" msgid="8885707816848569619">"ಅನುಮತಿಸಿ"</string>
<string name="deny" msgid="6040983710442068936">"ನಿರಾಕರಿಸಿ"</string>
- <string name="add" msgid="2894574044585549298">"ಸೇರಿಸಿ"</string>
- <string name="deselect" msgid="4297825044827769490">"ಆಯ್ಕೆ ರದ್ದುಮಾಡಿ"</string>
- <string name="select" msgid="2704765470563027689">"ಆಯ್ಕೆಮಾಡಿ"</string>
- <string name="recent" msgid="6694613584743207874">"ಇತ್ತೀಚಿನದು"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"ಆಯ್ಕೆಮಾಡಿರುವುದನ್ನು ವೀಕ್ಷಿಸಿ"</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="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_wait" msgid="8909773149560697501">"ನಿರೀಕ್ಷಿಸಿ"</string>
</resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 1554cac..97fa4ec 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"삭제"</string>
<string name="allow" msgid="8885707816848569619">"허용"</string>
<string name="deny" msgid="6040983710442068936">"거부"</string>
- <string name="add" msgid="2894574044585549298">"추가"</string>
- <string name="deselect" msgid="4297825044827769490">"선택 해제"</string>
- <string name="select" msgid="2704765470563027689">"선택"</string>
- <string name="recent" msgid="6694613584743207874">"최근"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"선택 항목 보기"</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="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_wait" msgid="8909773149560697501">"대기"</string>
</resources>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 87f46b2..74ad592 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Тазалоо"</string>
<string name="allow" msgid="8885707816848569619">"Ооба"</string>
<string name="deny" msgid="6040983710442068936">"Жок"</string>
- <string name="add" msgid="2894574044585549298">"Кошуу"</string>
- <string name="deselect" msgid="4297825044827769490">"Тандоодон чыгаруу"</string>
- <string name="select" msgid="2704765470563027689">"Тандоо"</string>
- <string name="recent" msgid="6694613584743207874">"Акыркы"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Көрүнүш тандалды"</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="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_wait" msgid="8909773149560697501">"Күтүү"</string>
</resources>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index d54011b..568d20a 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"ລຶບລ້າງ"</string>
<string name="allow" msgid="8885707816848569619">"ອະນຸຍາດ"</string>
<string name="deny" msgid="6040983710442068936">"ປະຕິເສດ"</string>
- <string name="add" msgid="2894574044585549298">"ເພີ່ມ"</string>
- <string name="deselect" msgid="4297825044827769490">"ເຊົາເລືອກ"</string>
- <string name="select" msgid="2704765470563027689">"ເລືອກ"</string>
- <string name="recent" msgid="6694613584743207874">"ຫຼ້າສຸດ"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"ເບິ່ງອັນທີ່ເລືອກໄວ້"</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="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_wait" msgid="8909773149560697501">"ລໍຖ້າ"</string>
</resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 7b4228d..4190c0f 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -47,209 +47,100 @@
<string name="clear" msgid="5524638938415865915">"Išvalyti"</string>
<string name="allow" msgid="8885707816848569619">"Leisti"</string>
<string name="deny" msgid="6040983710442068936">"Atmesti"</string>
- <string name="add" msgid="2894574044585549298">"Pridėti"</string>
- <string name="deselect" msgid="4297825044827769490">"Panaikinti pasirinkimą"</string>
- <string name="select" msgid="2704765470563027689">"Pasirinkti"</string>
- <string name="recent" msgid="6694613584743207874">"Naujausios"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Žiūrėti pasirinktus"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Medija apdorojimas sėkmingas"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Pradedama apdoroti mediją"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Medija apdorojama…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Atšaukti"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Palaukti"</string>
</resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index c5d3bcf..7bc296f 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -45,177 +45,84 @@
<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>
- <string name="add" msgid="2894574044585549298">"Pievienot"</string>
- <string name="deselect" msgid="4297825044827769490">"Noņemt atlasi"</string>
- <string name="select" msgid="2704765470563027689">"Atlasīt"</string>
- <string name="recent" msgid="6694613584743207874">"Jaunākie"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Skatīt atlasīto"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Multivides apstrāde bija sekmīga."</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Tika sākta multivides apstrāde."</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Notiek multivides apstrāde…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Atcelt"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Gaidīt"</string>
</resources>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 4e798e8..2fe7609 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Избриши"</string>
<string name="allow" msgid="8885707816848569619">"Дозволи"</string>
<string name="deny" msgid="6040983710442068936">"Одбиј"</string>
- <string name="add" msgid="2894574044585549298">"Додај"</string>
- <string name="deselect" msgid="4297825044827769490">"Поништи го изборот"</string>
- <string name="select" msgid="2704765470563027689">"Избери"</string>
- <string name="recent" msgid="6694613584743207874">"Неодамнешни"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Прикажи ги избраните"</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="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_wait" msgid="8909773149560697501">"Почекајте"</string>
</resources>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index dfccb94..05d8624 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"മായ്ക്കുക"</string>
<string name="allow" msgid="8885707816848569619">"അനുവദിക്കൂ"</string>
<string name="deny" msgid="6040983710442068936">"നിരസിക്കുക"</string>
- <string name="add" msgid="2894574044585549298">"ചേർക്കുക"</string>
- <string name="deselect" msgid="4297825044827769490">"തിരഞ്ഞെടുത്തത് മാറ്റുക"</string>
- <string name="select" msgid="2704765470563027689">"തിരഞ്ഞെടുക്കുക"</string>
- <string name="recent" msgid="6694613584743207874">"അടുത്തിടെയുള്ളത്"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"തിരഞ്ഞെടുത്തത് കാണുക"</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="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_wait" msgid="8909773149560697501">"കാത്തിരിക്കുക"</string>
</resources>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index c4de531..8c7a39b 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Арилгах"</string>
<string name="allow" msgid="8885707816848569619">"Зөвшөөрөх"</string>
<string name="deny" msgid="6040983710442068936">"Татгалзах"</string>
- <string name="add" msgid="2894574044585549298">"Нэмэх"</string>
- <string name="deselect" msgid="4297825044827769490">"Сонголтыг цуцлах"</string>
- <string name="select" msgid="2704765470563027689">"Сонгох"</string>
- <string name="recent" msgid="6694613584743207874">"Саяхны"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Сонгосныг харах"</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="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_wait" msgid="8909773149560697501">"Хүлээх"</string>
</resources>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index c261f8a..9a6b24e 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"साफ करा"</string>
<string name="allow" msgid="8885707816848569619">"अनुमती द्या"</string>
<string name="deny" msgid="6040983710442068936">"नकार द्या"</string>
- <string name="add" msgid="2894574044585549298">"जोडा"</string>
- <string name="deselect" msgid="4297825044827769490">"निवड रद्द करा"</string>
- <string name="select" msgid="2704765470563027689">"निवडा"</string>
- <string name="recent" msgid="6694613584743207874">"अलीकडील"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"दृश्य निवडले"</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="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_wait" msgid="8909773149560697501">"प्रतीक्षा करा"</string>
</resources>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 473f392..067cc43 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Kosongkan"</string>
<string name="allow" msgid="8885707816848569619">"Benarkan"</string>
<string name="deny" msgid="6040983710442068936">"Tolak"</string>
- <string name="add" msgid="2894574044585549298">"Tambah"</string>
- <string name="deselect" msgid="4297825044827769490">"Nyahpilih"</string>
- <string name="select" msgid="2704765470563027689">"Pilih"</string>
- <string name="recent" msgid="6694613584743207874">"Terkini"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Lihat terpilih"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Pemprosesan media berjaya"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Pemprosesan media telah bermula"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Memproses media…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Batal"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Tunggu"</string>
</resources>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index c89a692..ab3766c 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"ရှင်းရန်"</string>
<string name="allow" msgid="8885707816848569619">"ခွင့်ပြုရန်"</string>
<string name="deny" msgid="6040983710442068936">"ပယ်ရန်"</string>
- <string name="add" msgid="2894574044585549298">"ထည့်ရန်"</string>
- <string name="deselect" msgid="4297825044827769490">"မရွေးပါနှင့်"</string>
- <string name="select" msgid="2704765470563027689">"ရွေးရန်"</string>
- <string name="recent" msgid="6694613584743207874">"မကြာသေးမီက"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"ပြသမှုကို ရွေးချယ်ထားသည်"</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="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_wait" msgid="8909773149560697501">"စောင့်ရန်"</string>
</resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index cca6753..35fd892 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Slett"</string>
<string name="allow" msgid="8885707816848569619">"Tillat"</string>
<string name="deny" msgid="6040983710442068936">"Avvis"</string>
- <string name="add" msgid="2894574044585549298">"Legg til"</string>
- <string name="deselect" msgid="4297825044827769490">"Fjern merking"</string>
- <string name="select" msgid="2704765470563027689">"Velg"</string>
- <string name="recent" msgid="6694613584743207874">"Nylige"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Vis valgte"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Mediene er blitt behandlet"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Behandlingen av mediene er startet"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Mediene behandles …"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Avbryt"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Vent"</string>
</resources>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 220bd66..8700dc1 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"हटाउनुहोस्"</string>
<string name="allow" msgid="8885707816848569619">"अनुमति दिनुहोस्"</string>
<string name="deny" msgid="6040983710442068936">"अस्वीकार गर्नुहोस्"</string>
- <string name="add" msgid="2894574044585549298">"हाल्नुहोस्"</string>
- <string name="deselect" msgid="4297825044827769490">"चयन रद्द गर्नुहोस्"</string>
- <string name="select" msgid="2704765470563027689">"चयन गर्नुहोस्"</string>
- <string name="recent" msgid="6694613584743207874">"हालसालैका"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"चयन गरिएका फोटो तथा भिडियोहरू हेर्नुहोस्"</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="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_wait" msgid="8909773149560697501">"पर्खनुहोस्"</string>
</resources>
diff --git a/res/values-night/colors.xml b/res/values-night/colors.xml
index 596dd5b..1478eb2 100644
--- a/res/values-night/colors.xml
+++ b/res/values-night/colors.xml
@@ -18,20 +18,4 @@
<resources>
<color name="thumb_gray_color">#3C4043</color>
<color name="clear_cache_icon_color">#DADCE0</color>
-
- <!-- PhotoPicker -->
- <color name="picker_primary_color">#8AB4F8</color>
- <color name="picker_background_color">#202124</color>
- <color name="picker_highlight_color">#3D8AB4F8</color>
- <color name="picker_date_header_text_color">@color/picker_default_white</color>
- <color name="picker_toolbar_icon_color">#E8EAED</color>
- <color name="picker_toolbar_chip_text_color">#E8EAED</color>
- <color name="picker_toolbar_title_color">#FFFFFF</color>
-
- <!-- PhotoPicker Profile Button -->
- <color name="picker_profile_button_content_color">#A8C7FA</color>
- <color name="picker_profile_button_background_color">#1F1F1F</color>
- <color name="picker_profile_disabled_button_content_color">#E3E3E3</color>
- <color name="picker_profile_disabled_button_background_color">#DADADA</color>
-
</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 72f2e41..d685675 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Wissen"</string>
<string name="allow" msgid="8885707816848569619">"Toestaan"</string>
<string name="deny" msgid="6040983710442068936">"Weigeren"</string>
- <string name="add" msgid="2894574044585549298">"Toevoegen"</string>
- <string name="deselect" msgid="4297825044827769490">"Deselecteren"</string>
- <string name="select" msgid="2704765470563027689">"Selecteren"</string>
- <string name="recent" msgid="6694613584743207874">"Recent"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Selectie bekijken"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Mediaverwerking afgerond"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Mediaverwerking gestart"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Media verwerken…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Annuleren"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Wachten"</string>
</resources>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index a257e9b..86491af 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"ଖାଲି କରନ୍ତୁ"</string>
<string name="allow" msgid="8885707816848569619">"ଅନୁମତି ଦିଅନ୍ତୁ"</string>
<string name="deny" msgid="6040983710442068936">"ଅଗ୍ରାହ୍ୟ କରନ୍ତୁ"</string>
- <string name="add" msgid="2894574044585549298">"ଯୋଗ କରନ୍ତୁ"</string>
- <string name="deselect" msgid="4297825044827769490">"ଅଚୟନ କରନ୍ତୁ"</string>
- <string name="select" msgid="2704765470563027689">"ଚୟନ କରନ୍ତୁ"</string>
- <string name="recent" msgid="6694613584743207874">"ବର୍ତ୍ତମାନର"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"ଚୟନିତଗୁଡ଼ିକୁ ଦେଖନ୍ତୁ"</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="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_wait" msgid="8909773149560697501">"ଅପେକ୍ଷା କରନ୍ତୁ"</string>
</resources>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 283f362..cb5e9b4 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"ਕਲੀਅਰ ਕਰੋ"</string>
<string name="allow" msgid="8885707816848569619">"ਆਗਿਆ ਦਿਓ"</string>
<string name="deny" msgid="6040983710442068936">"ਮਨ੍ਹਾ ਕਰੋ"</string>
- <string name="add" msgid="2894574044585549298">"ਸ਼ਾਮਲ ਕਰੋ"</string>
- <string name="deselect" msgid="4297825044827769490">"ਅਣ-ਚੁਣਿਆ ਕਰੋ"</string>
- <string name="select" msgid="2704765470563027689">"ਚੁਣੋ"</string>
- <string name="recent" msgid="6694613584743207874">"ਹਾਲੀਆ"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"ਚੁਣੀ ਗਈ ਕਾਰਵਾਈ ਦੇਖੋ"</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="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_wait" msgid="8909773149560697501">"ਉਡੀਕ ਕਰੋ"</string>
</resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index aedc6d4..ea4a8ce 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -47,209 +47,100 @@
<string name="clear" msgid="5524638938415865915">"Wyczyść"</string>
<string name="allow" msgid="8885707816848569619">"Zezwól"</string>
<string name="deny" msgid="6040983710442068936">"Odmów"</string>
- <string name="add" msgid="2894574044585549298">"Dodaj"</string>
- <string name="deselect" msgid="4297825044827769490">"Odznacz"</string>
- <string name="select" msgid="2704765470563027689">"Zaznacz"</string>
- <string name="recent" msgid="6694613584743207874">"Ostatnie"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Wyświetl wybrane"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Przetworzono multimedia"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Rozpoczęto przetwarzanie multimediów"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Przetwarzam multimedia…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Anuluj"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Czekaj"</string>
</resources>
diff --git a/res/values-pt-rBR/strings.xml b/res/values-pt-rBR/strings.xml
index 0552930..d0ff6d6 100644
--- a/res/values-pt-rBR/strings.xml
+++ b/res/values-pt-rBR/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Limpar"</string>
<string name="allow" msgid="8885707816848569619">"Permitir"</string>
<string name="deny" msgid="6040983710442068936">"Negar"</string>
- <string name="add" msgid="2894574044585549298">"Adicionar"</string>
- <string name="deselect" msgid="4297825044827769490">"Desmarcar"</string>
- <string name="select" msgid="2704765470563027689">"Selecionar"</string>
- <string name="recent" msgid="6694613584743207874">"Recentes"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Ver selecionado"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Processamento de mídia concluído"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Processamento de mídia iniciado"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Processando mídia…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Cancelar"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Aguardar"</string>
</resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 6d57833..27c1cb0 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Limpar"</string>
<string name="allow" msgid="8885707816848569619">"Permitir"</string>
<string name="deny" msgid="6040983710442068936">"Recusar"</string>
- <string name="add" msgid="2894574044585549298">"Adicionar"</string>
- <string name="deselect" msgid="4297825044827769490">"Desselecionar"</string>
- <string name="select" msgid="2704765470563027689">"Selecionar"</string>
- <string name="recent" msgid="6694613584743207874">"Recente"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Ver selecionado(s)"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Êxito do processamento de multimédia"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Processamento de multimédia iniciado"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"A processar multimédia…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Cancelar"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Aguardar"</string>
</resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 0552930..d0ff6d6 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Limpar"</string>
<string name="allow" msgid="8885707816848569619">"Permitir"</string>
<string name="deny" msgid="6040983710442068936">"Negar"</string>
- <string name="add" msgid="2894574044585549298">"Adicionar"</string>
- <string name="deselect" msgid="4297825044827769490">"Desmarcar"</string>
- <string name="select" msgid="2704765470563027689">"Selecionar"</string>
- <string name="recent" msgid="6694613584743207874">"Recentes"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Ver selecionado"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Processamento de mídia concluído"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Processamento de mídia iniciado"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Processando mídia…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Cancelar"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Aguardar"</string>
</resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 4f2e970..8df20b8 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -45,177 +45,84 @@
<string name="clear" msgid="5524638938415865915">"Ștergeți"</string>
<string name="allow" msgid="8885707816848569619">"Permiteți"</string>
<string name="deny" msgid="6040983710442068936">"Refuzați"</string>
- <string name="add" msgid="2894574044585549298">"Adăugați"</string>
- <string name="deselect" msgid="4297825044827769490">"Debifați"</string>
- <string name="select" msgid="2704765470563027689">"Selectați"</string>
- <string name="recent" msgid="6694613584743207874">"Recente"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Vedeți elementele selectate"</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="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>
</resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index dc2b2b1..d7a1aaa 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -47,209 +47,100 @@
<string name="clear" msgid="5524638938415865915">"Удалить"</string>
<string name="allow" msgid="8885707816848569619">"Разрешить"</string>
<string name="deny" msgid="6040983710442068936">"Запретить"</string>
- <string name="add" msgid="2894574044585549298">"Добавить"</string>
- <string name="deselect" msgid="4297825044827769490">"Отменить выбор"</string>
- <string name="select" msgid="2704765470563027689">"Выбрать"</string>
- <string name="recent" msgid="6694613584743207874">"Недавние"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Смотреть выбранное"</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="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_wait" msgid="8909773149560697501">"Подождать"</string>
</resources>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index b282c82..ad22297 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"හිස් කරන්න"</string>
<string name="allow" msgid="8885707816848569619">"ඉඩ දෙන්න"</string>
<string name="deny" msgid="6040983710442068936">"ප්රතික්ෂේප කරන්න"</string>
- <string name="add" msgid="2894574044585549298">"එක් කරන්න"</string>
- <string name="deselect" msgid="4297825044827769490">"නොතෝරන්න"</string>
- <string name="select" msgid="2704765470563027689">"තෝරන්න"</string>
- <string name="recent" msgid="6694613584743207874">"මෑත"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"තෝරා ගත් දේවල් බලන්න"</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="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_wait" msgid="8909773149560697501">"රැඳී සිටින්න"</string>
</resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 24d2162..359ba24 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -47,209 +47,100 @@
<string name="clear" msgid="5524638938415865915">"Vymazať"</string>
<string name="allow" msgid="8885707816848569619">"Povoliť"</string>
<string name="deny" msgid="6040983710442068936">"Zamietnuť"</string>
- <string name="add" msgid="2894574044585549298">"Pridať"</string>
- <string name="deselect" msgid="4297825044827769490">"Zrušiť výber"</string>
- <string name="select" msgid="2704765470563027689">"Vybrať"</string>
- <string name="recent" msgid="6694613584743207874">"Nedávne"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Zobraziť vybrané"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Médiá boli úspešne spracované"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Spracúvanie médií sa začalo"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Spracúvajú sa médiá…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Zrušiť"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Počkať"</string>
</resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index f1b2d3e..ff63498 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -47,209 +47,100 @@
<string name="clear" msgid="5524638938415865915">"Počisti"</string>
<string name="allow" msgid="8885707816848569619">"Dovoli"</string>
<string name="deny" msgid="6040983710442068936">"Zavrni"</string>
- <string name="add" msgid="2894574044585549298">"Dodaj"</string>
- <string name="deselect" msgid="4297825044827769490">"Počisti izbiro"</string>
- <string name="select" msgid="2704765470563027689">"Izberi"</string>
- <string name="recent" msgid="6694613584743207874">"Nedavno"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Prikaži izbrano"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Obdelava predstavnosti je uspešno dokončana."</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Obdelava predstavnosti se je začela."</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Obdelovanje predstavnosti …"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Prekliči"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Počakaj"</string>
</resources>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index f4dd5a6..1e90f35 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Pastro"</string>
<string name="allow" msgid="8885707816848569619">"Lejo"</string>
<string name="deny" msgid="6040983710442068936">"Refuzo"</string>
- <string name="add" msgid="2894574044585549298">"Shto"</string>
- <string name="deselect" msgid="4297825044827769490">"Hiq përzgjedhjen"</string>
- <string name="select" msgid="2704765470563027689">"Zgjidh"</string>
- <string name="recent" msgid="6694613584743207874">"Të fundit"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Shiko të zgjedhurat"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Përpunimi i medias u krye me sukses"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Përpunimi i medias ka filluar"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Media po përpunohet…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Anulo"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Prit"</string>
</resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 1c819d7..079f9aa 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -45,177 +45,84 @@
<string name="clear" msgid="5524638938415865915">"Обриши"</string>
<string name="allow" msgid="8885707816848569619">"Дозволи"</string>
<string name="deny" msgid="6040983710442068936">"Одбиј"</string>
- <string name="add" msgid="2894574044585549298">"Додај"</string>
- <string name="deselect" msgid="4297825044827769490">"Опозови избор"</string>
- <string name="select" msgid="2704765470563027689">"Изабери"</string>
- <string name="recent" msgid="6694613584743207874">"Недавно"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Прикажи изабранo"</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="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_wait" msgid="8909773149560697501">"Сачекај"</string>
</resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 9e1aaa1..b0b990c 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Rensa"</string>
<string name="allow" msgid="8885707816848569619">"Tillåt"</string>
<string name="deny" msgid="6040983710442068936">"Neka"</string>
- <string name="add" msgid="2894574044585549298">"Lägg till"</string>
- <string name="deselect" msgid="4297825044827769490">"Avmarkera"</string>
- <string name="select" msgid="2704765470563027689">"Välj"</string>
- <string name="recent" msgid="6694613584743207874">"Senaste"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Visa valda"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Mediebearbetningen har slutförts"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Mediebearbetningen har startats"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Bearbetar media …"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Avbryt"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Vänta"</string>
</resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 012eb76..4917e30 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Futa"</string>
<string name="allow" msgid="8885707816848569619">"Ruhusu"</string>
<string name="deny" msgid="6040983710442068936">"Kataa"</string>
- <string name="add" msgid="2894574044585549298">"Weka"</string>
- <string name="deselect" msgid="4297825044827769490">"Acha kuchagua"</string>
- <string name="select" msgid="2704765470563027689">"Chagua"</string>
- <string name="recent" msgid="6694613584743207874">"Za hivi majuzi"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Angalia uliyochagua"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Imemaliza kuchakata maudhui"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Imeanza kuchakata maudhui"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Inachakata maudhui…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Ghairi"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Subiri"</string>
</resources>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 98a5ff0..f13588a 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"அழி"</string>
<string name="allow" msgid="8885707816848569619">"அனுமதி"</string>
<string name="deny" msgid="6040983710442068936">"நிராகரி"</string>
- <string name="add" msgid="2894574044585549298">"சேர்"</string>
- <string name="deselect" msgid="4297825044827769490">"தேர்வுநீக்கு"</string>
- <string name="select" msgid="2704765470563027689">"தேர்ந்தெடு"</string>
- <string name="recent" msgid="6694613584743207874">"சமீபத்தியவை"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"தேர்ந்தெடுத்தவற்றைக் காட்டு"</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="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_wait" msgid="8909773149560697501">"காத்திருங்கள்"</string>
</resources>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 2a31260..12d9ba5 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"క్లియర్ చేయండి"</string>
<string name="allow" msgid="8885707816848569619">"అనుమతించండి"</string>
<string name="deny" msgid="6040983710442068936">"నిరాకరించు"</string>
- <string name="add" msgid="2894574044585549298">"జోడించు"</string>
- <string name="deselect" msgid="4297825044827769490">"ఎంపికను తొలగించండి"</string>
- <string name="select" msgid="2704765470563027689">"ఎంచుకోండి"</string>
- <string name="recent" msgid="6694613584743207874">"ఇటీవలివి"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"ఎంచుకున్న వాటిని చూడండి"</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="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_wait" msgid="8909773149560697501">"వేచి ఉండు"</string>
</resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 1e8d37e..db76bee 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"ล้าง"</string>
<string name="allow" msgid="8885707816848569619">"อนุญาต"</string>
<string name="deny" msgid="6040983710442068936">"ปฏิเสธ"</string>
- <string name="add" msgid="2894574044585549298">"เพิ่ม"</string>
- <string name="deselect" msgid="4297825044827769490">"ยกเลิกการเลือก"</string>
- <string name="select" msgid="2704765470563027689">"เลือก"</string>
- <string name="recent" msgid="6694613584743207874">"ล่าสุด"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"ดูรายการที่เลือก"</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="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_wait" msgid="8909773149560697501">"รอ"</string>
</resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 4fa3073..79452b3 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"I-clear"</string>
<string name="allow" msgid="8885707816848569619">"Payagan"</string>
<string name="deny" msgid="6040983710442068936">"Tanggihan"</string>
- <string name="add" msgid="2894574044585549298">"Magdagdag"</string>
- <string name="deselect" msgid="4297825044827769490">"I-deselect"</string>
- <string name="select" msgid="2704765470563027689">"Piliin"</string>
- <string name="recent" msgid="6694613584743207874">"Kamakailan"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Tingnan ang napili"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Matagumpay ang pagpoproseso ng media"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Sinimulan ang pagpoproseso ng media"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Pinoproseso ang media…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Kanselahin"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Maghintay"</string>
</resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 3b18710..669eac9 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Temizle"</string>
<string name="allow" msgid="8885707816848569619">"İzin ver"</string>
<string name="deny" msgid="6040983710442068936">"Reddet"</string>
- <string name="add" msgid="2894574044585549298">"Ekle"</string>
- <string name="deselect" msgid="4297825044827769490">"Seçimi kaldır"</string>
- <string name="select" msgid="2704765470563027689">"Seç"</string>
- <string name="recent" msgid="6694613584743207874">"En son"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Seçilenleri görüntüle"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Medya işleme başarılı"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Medya işleme başladı"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Medya işleniyor…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"İptal"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Bekle"</string>
</resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 0668fd8..281c8b2 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -47,209 +47,100 @@
<string name="clear" msgid="5524638938415865915">"Очистити"</string>
<string name="allow" msgid="8885707816848569619">"Дозволити"</string>
<string name="deny" msgid="6040983710442068936">"Заборонити"</string>
- <string name="add" msgid="2894574044585549298">"Додати"</string>
- <string name="deselect" msgid="4297825044827769490">"Не вибирати"</string>
- <string name="select" msgid="2704765470563027689">"Вибрати"</string>
- <string name="recent" msgid="6694613584743207874">"Нещодавні"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Переглянути вибране"</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="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_wait" msgid="8909773149560697501">"Зачекати"</string>
</resources>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index a0bdb5b..ad6cdf1 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"صاف کریں"</string>
<string name="allow" msgid="8885707816848569619">"اجازت دیں"</string>
<string name="deny" msgid="6040983710442068936">"مسترد کریں"</string>
- <string name="add" msgid="2894574044585549298">"شامل کریں"</string>
- <string name="deselect" msgid="4297825044827769490">"غیر منتخب کریں"</string>
- <string name="select" msgid="2704765470563027689">"منتخب کریں"</string>
- <string name="recent" msgid="6694613584743207874">"حالیہ"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"منتخب کردہ دیکھیں"</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="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_wait" msgid="8909773149560697501">"انتظار کریں"</string>
</resources>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 2d16617..0dcad99 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Tozalash"</string>
<string name="allow" msgid="8885707816848569619">"Ruxsat"</string>
<string name="deny" msgid="6040983710442068936">"Rad etish"</string>
- <string name="add" msgid="2894574044585549298">"Kiritish"</string>
- <string name="deselect" msgid="4297825044827769490">"Tanlovni bekor qilish"</string>
- <string name="select" msgid="2704765470563027689">"Tanlash"</string>
- <string name="recent" msgid="6694613584743207874">"Oxirgi"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Tanlanganni koʻrish"</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> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta suratni oʻzgartirishi uchun ruxsat berilsinmi?</item>
<item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu suratni oʻzgartirishi uchun 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> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta suratni chiqitdonga tashlashi uchun ruxsat berilsinmi?</item>
<item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu suratni chiqitdonga tashlashi uchun 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> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta suratni chiqitdondan chiqarib olishi uchun ruxsat berilsinmi?</item>
<item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu suratni chiqitdondan chiqarib olishi uchun 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> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta suratni oʻchirib tashlashi uchun ruxsat berilsinmi?</item>
<item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu suratni oʻchirib tashlashi uchun 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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Mediga ishlov berildi"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Mediaga ishlov berish boshlandi"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Mediaga ishlov berilmoqda…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Bekor qilish"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Kutish"</string>
</resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 6242263..d0cbec7 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -43,145 +43,68 @@
<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>
- <string name="add" msgid="2894574044585549298">"Thêm"</string>
- <string name="deselect" msgid="4297825044827769490">"Bỏ chọn"</string>
- <string name="select" msgid="2704765470563027689">"Chọn"</string>
- <string name="recent" msgid="6694613584743207874">"Gần đây"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Đã chọn Chế độ xem"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Đã xử lý thành công nội dung nghe nhìn"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Đã bắt đầu xử lý nội dung nghe nhìn"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Đang xử lý nội dung nghe nhìn..."</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Hủy"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Đợi"</string>
</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index dccb5d7..6cd41f7 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"清除"</string>
<string name="allow" msgid="8885707816848569619">"允许"</string>
<string name="deny" msgid="6040983710442068936">"拒绝"</string>
- <string name="add" msgid="2894574044585549298">"添加"</string>
- <string name="deselect" msgid="4297825044827769490">"取消选择"</string>
- <string name="select" msgid="2704765470563027689">"选择"</string>
- <string name="recent" msgid="6694613584743207874">"最近"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"查看所选内容"</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="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_wait" msgid="8909773149560697501">"等待"</string>
</resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 4a630fd..3243f9b 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"清除"</string>
<string name="allow" msgid="8885707816848569619">"允許"</string>
<string name="deny" msgid="6040983710442068936">"拒絕"</string>
- <string name="add" msgid="2894574044585549298">"新增"</string>
- <string name="deselect" msgid="4297825044827769490">"取消選取"</string>
- <string name="select" msgid="2704765470563027689">"選取"</string>
- <string name="recent" msgid="6694613584743207874">"最近"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"查看所選項目"</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="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_wait" msgid="8909773149560697501">"等待"</string>
</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 26ed779..1a37210 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"清除"</string>
<string name="allow" msgid="8885707816848569619">"允許"</string>
<string name="deny" msgid="6040983710442068936">"拒絕"</string>
- <string name="add" msgid="2894574044585549298">"新增"</string>
- <string name="deselect" msgid="4297825044827769490">"取消選取"</string>
- <string name="select" msgid="2704765470563027689">"選取"</string>
- <string name="recent" msgid="6694613584743207874">"最近"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"查看所選項目"</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="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_wait" msgid="8909773149560697501">"等待"</string>
</resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 2ed8217..e2f528d 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -43,145 +43,68 @@
<string name="clear" msgid="5524638938415865915">"Sula"</string>
<string name="allow" msgid="8885707816848569619">"Vumela"</string>
<string name="deny" msgid="6040983710442068936">"Nqaba"</string>
- <string name="add" msgid="2894574044585549298">"Engeza"</string>
- <string name="deselect" msgid="4297825044827769490">"Susa ukukhetha"</string>
- <string name="select" msgid="2704765470563027689">"Khetha"</string>
- <string name="recent" msgid="6694613584743207874">"Okwakamuva"</string>
- <string name="picker_view_selected" msgid="2266031384396143883">"Ukubuka kukhethiwe"</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="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>
- <string name="transcode_processing_success" msgid="447288876429730122">"Ukucubungula imidiya kuphumelele"</string>
- <string name="transcode_processing_started" msgid="7789086308155361523">"Ukucubungula imidiya kuqalile"</string>
- <string name="transcode_processing" msgid="6753136468864077258">"Icubungula imidiya…"</string>
- <string name="transcode_cancel" msgid="8555752601907598192">"Khansela"</string>
- <string name="transcode_wait" msgid="8909773149560697501">"Linda"</string>
</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 2337f98..ed0c6c2 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -17,34 +17,4 @@
<resources>
<color name="thumb_gray_color">#1F000000</color>
<color name="clear_cache_icon_color">#5F6368</color>
-
- <!-- PhotoPicker -->
- <color name="picker_default_white">@android:color/white</color>
-
- <!-- PhotoPicker photo grid -->
- <color name="picker_primary_color">#1A73E8</color>
- <color name="picker_background_color">@android:color/white</color>
- <color name="picker_highlight_color">#E8F0FE</color>
- <color name="picker_date_header_text_color">#3C4043</color>
- <color name="picker_toolbar_icon_color">#3C4043</color>
- <color name="picker_toolbar_chip_text_color">#5F6368</color>
- <color name="picker_toolbar_title_color">#3C4043</color>
-
- <!-- PhotoPicker gradient colors -->
- <color name="picker_item_gradient_color">#42000000</color>
- <color name="preview_gradient_color_light">@android:color/transparent</color>
- <color name="preview_gradient_color_dark">#80202124</color>
-
- <!-- PhotoPicker Preview -->
- <color name="preview_default_blue">#8AB4F8</color>
- <color name="preview_default_grey">#202124</color>
- <color name="preview_default_black">@android:color/black</color>
-
- <!-- PhotoPicker Profile Button -->
- <color name="picker_profile_button_content_color">#0B57D0</color>
- <color name="picker_profile_button_background_color">#E8F0FE</color>
- <color name="picker_profile_disabled_button_content_color">#1F1F1F</color>
- <color name="picker_profile_disabled_button_background_color">#DADADA</color>
- <color name="picker_profile_dialog_icon_and_button_color">#1A73E8</color>
- <color name="picker_profile_dialog_text_color">#3C4043</color>
</resources>
diff --git a/res/values/config.xml b/res/values/config.xml
deleted file mode 100644
index d640cb7..0000000
--- a/res/values/config.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?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-array name="config_supported_transcoding_relative_paths" translatable="false">
- <item>DCIM/Camera/</item>
- </string-array>
-</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index a56d8d5..c3bdf8f 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -19,56 +19,4 @@
<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">16dp</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_vertical_gap">10dp</dimen>
- <dimen name="picker_bottom_bar_elevation">8dp</dimen>
-
- <dimen name="picker_item_check_size">24dp</dimen>
- <dimen name="picker_item_check_margin">6dp</dimen>
- <dimen name="picker_item_badge_margin">5dp</dimen>
- <dimen name="picker_item_badge_text_margin">3dp</dimen>
- <dimen name="picker_item_badge_text_size">10dp</dimen>
- <dimen name="picker_item_gif_badge_margin">3dp</dimen>
- <dimen name="picker_item_gradient_height">56dp</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_bottom_bar_gap">20dp</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>
-
- <dimen name="picker_chip_text_size">14sp</dimen>
- <dimen name="picker_chip_touch_size">48dp</dimen>
- <dimen name="picker_chip_radius">16dp</dimen>
-
- <!-- PhotoPicker Preview -->
- <dimen name="preview_buttons_margin_horizontal">16dp</dimen>
- <dimen name="preview_buttons_margin_bottom">10dp</dimen>
- <dimen name="preview_deselect_padding_start">2dp</dimen>
- <!-- PhotoPicker Preview text -->
- <dimen name="preview_add_text_size">14sp</dimen>
- <dimen name="preview_deselect_text_size">16sp</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">32dp</dimen>
- <dimen name="picker_profile_dialog_radius">8dp</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/overlayable.xml b/res/values/overlayable.xml
deleted file mode 100644
index 6b89bf4..0000000
--- a/res/values/overlayable.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?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="array" name="config_supported_transcoding_relative_paths"/>
- </policy>
- </overlayable>
-</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 944c71b..d6171ca 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -57,7 +57,7 @@
<!-- 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="1">^1</xliff:g> additional item</item>
+ <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>
@@ -79,73 +79,6 @@
<!-- 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>
-
- <!-- Select button for PhotoPicker. [CHAR LIMIT=30] -->
- <string name="select">Select</string>
-
- <!-- Select up to max label message for PhotoPicker. [CHAR LIMIT=30] -->
- <plurals name="select_up_to">
- <item quantity="one">Select up to <xliff:g id="count" example="1">^1</xliff:g> item</item>
- <item quantity="other">Select up to <xliff:g id="count" example="42">^1</xliff:g> items</item>
- </plurals>
-
- <!-- Recent header for PhotoPicker. [CHAR LIMIT=50] -->
- <string name="recent">Recent</string>
-
- <!-- PhotoPicker view selected action text. [CHAR LIMIT=80] -->
- <string name="picker_view_selected">View selected</string>
-
- <!-- The text of the photos chip on the toolbar for PhotoPicker. [CHAR LIMIT=30] -->
- <string name="picker_photos">Photos</string>
-
- <!-- The text of the albums chip on the toolbar for PhotoPicker. [CHAR LIMIT=30] -->
- <string name="picker_albums">Albums</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>
-
- <!-- Text shown on the album item in PhotoPicker. [CHAR LIMIT=30] -->
- <plurals name="picker_album_item_count">
- <item quantity="one"><xliff:g id="count" example="1">^1</xliff:g> item</item>
- <item quantity="other"><xliff:g id="count" example="42">^1</xliff:g> items</item>
- </plurals>
-
- <!-- 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>
-
<!-- ========================= BEGIN AUTO-GENERATED BY gen_strings.py ========================= -->
<!-- ========================= WRITE STRINGS ========================= -->
@@ -155,41 +88,21 @@
<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>
- <!-- 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>
<!-- 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>
- <!-- 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>
<!-- 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>
- <!-- 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>
<!-- 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>
- <!-- 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>
<!-- ========================= TRASH STRINGS ========================= -->
@@ -198,41 +111,21 @@
<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>
- <!-- 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>
<!-- 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>
- <!-- 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>
<!-- 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>
- <!-- 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>
<!-- 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>
- <!-- 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>
<!-- ========================= UNTRASH STRINGS ========================= -->
@@ -241,41 +134,21 @@
<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>
- <!-- 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>
<!-- 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>
- <!-- 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>
<!-- 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>
- <!-- 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>
<!-- 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>
- <!-- 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>
<!-- ========================= DELETE STRINGS ========================= -->
@@ -284,67 +157,22 @@
<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>
- <!-- 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>
<!-- 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>
- <!-- 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>
<!-- 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>
- <!-- 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>
<!-- 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>
- <!-- 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>
<!-- ========================= END AUTO-GENERATED BY gen_strings.py ========================= -->
- <!-- ========================= TRANSCODE STRINGS ========================= -->
-
- <!-- Transcode denied toast text. [CHAR LIMIT=NONE] -->
- <string name="transcode_denied"><xliff:g id="app_name" example="File manager">%s</xliff:g> can\'t process media files</string>
-
- <!-- Transcode cancelled notification. -->
- <string name="transcode_processing_cancelled">Media processing cancelled</string>
-
- <!-- Transcode error notification. -->
- <string name="transcode_processing_error">Media processing error</string>
-
- <!-- Transcode success notification. -->
- <string name="transcode_processing_success">Media processing success</string>
-
- <!-- Transcode started notification. -->
- <string name="transcode_processing_started">Media processing started</string>
-
- <!-- Transcode processing notification. -->
- <string name="transcode_processing">Processing media…</string>
-
- <!-- Transcode intermediate ANR dialog cancel button. -->
- <string name="transcode_cancel">Cancel</string>
-
- <!-- Transcode intermediate ANR dialog wait button. -->
- <string name="transcode_wait">Wait</string>
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index f629e9d..5f1e662 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -49,46 +49,4 @@
parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
-
- <style name="ProfileDialogTheme"
- parent="@style/ThemeOverlay.MaterialComponents.MaterialAlertDialog.Centered">
- <item name="android:dialogCornerRadius">@dimen/picker_profile_dialog_radius</item>
- <item name="materialAlertDialogTitleTextStyle">@style/MaterialAlertDialogTitleStyle</item>
- <item name="materialAlertDialogBodyTextStyle">@style/MaterialAlertDialogBodyStyle</item>
- <item name="materialAlertDialogTitleIconStyle">@style/MaterialAlertDialogIconStyle</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">@color/picker_profile_dialog_icon_and_button_color</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="@style/Theme.MaterialComponents.DayNight.NoActionBar">
- <!-- Color section -->
- <item name="android:colorAccent">@color/picker_primary_color</item>
- <item name="android:colorBackground">@color/picker_background_color</item>
-
- <!-- System | Widget section -->
- <item name="android:statusBarColor">@android:color/transparent</item>
- <item name="android:navigationBarColor">?android:colorBackground</item>
- <item name="android:windowBackground">@android:color/transparent</item>
- <item name="android:windowIsTranslucent">true</item>
- <item name="android:backgroundDimEnabled">true</item>
- <item name="materialAlertDialogTheme">@style/ProfileDialogTheme</item>
- </style>
-
</resources>
diff --git a/res/values/styles_text.xml b/res/values/styles_text.xml
deleted file mode 100644
index b972c8b..0000000
--- a/res/values/styles_text.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?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="PickerDateHeader" parent="@android:style/TextAppearance.Material.Title">
- <item name="android:textColor">@color/picker_date_header_text_color</item>
- <item name="android:textSize">16sp</item>
- </style>
-
- <style name="PickerChipText" parent="@android:style/TextAppearance.Material.Title">
- <item name="android:textSize">@dimen/picker_chip_text_size</item>
- </style>
-
- <style name="PickerProfileButton" parent="@android:style/TextAppearance.Material.Button">
- <item name="android:textAllCaps">false</item>
- </style>
-
- <style name="PickerProfileDialogTitle"
- parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
- <item name="android:textAllCaps">false</item>
- <item name="android:textColor">@color/picker_profile_dialog_text_color</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">@color/picker_profile_dialog_text_color</item>
- </style>
-
-</resources>
diff --git a/sqlite3.sh b/sqlite3.sh
index cf9b4a5..7f7780b 100644
--- a/sqlite3.sh
+++ b/sqlite3.sh
@@ -59,26 +59,6 @@
adb shell am force-stop $package
}
-function get-id-from-data () {
- adb root
- path="$1"
- package=$(get-package)
- dir="/data/user/0/$package/databases/external.db"
- clause="\"select _id from files where _data='$path';\""
- echo $clause
- adb shell sqlite3 $dir $clause
-}
-
-function get-data-from-id () {
- adb root
- _id="$1"
- package=$(get-package)
- dir="/data/user/0/$package/databases/external.db"
- clause="\"select _data from files where _id='$_id';\""
- echo $clause
- adb shell sqlite3 $dir $clause
-}
-
function get-package() {
if [ -z "$(adb shell pm list package com.android.providers.media.module)" ]
then
diff --git a/src/com/android/providers/media/DatabaseHelper.java b/src/com/android/providers/media/DatabaseHelper.java
index f2dfd05..9c67aa6 100644
--- a/src/com/android/providers/media/DatabaseHelper.java
+++ b/src/com/android/providers/media/DatabaseHelper.java
@@ -39,7 +39,6 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
-import android.os.UserHandle;
import android.provider.MediaStore;
import android.provider.MediaStore.Audio;
import android.provider.MediaStore.Downloads;
@@ -94,15 +93,6 @@
* 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";
-
static final String INTERNAL_DATABASE_NAME = "internal.db";
static final String EXTERNAL_DATABASE_NAME = "external.db";
@@ -119,6 +109,7 @@
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;
@@ -149,17 +140,15 @@
public interface OnSchemaChangeListener {
public void onSchemaChange(@NonNull String volumeName, int versionFrom, int versionTo,
- long itemCount, long durationMillis, String databaseUuid);
+ long itemCount, long durationMillis);
}
public interface OnFilesChangeListener {
public void onInsert(@NonNull DatabaseHelper helper, @NonNull String volumeName, long id,
- int mediaType, boolean isDownload, boolean isPending);
+ 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,
- boolean oldIsTrashed, boolean newIsTrashed,
- boolean oldIsPending, boolean newIsPending,
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);
@@ -173,18 +162,18 @@
}
public DatabaseHelper(Context context, String name,
- boolean earlyUpgrade, boolean legacyProvider,
+ boolean internal, boolean earlyUpgrade, boolean legacyProvider,
@Nullable Class<? extends Annotation> columnAnnotation,
@Nullable OnSchemaChangeListener schemaListener,
@Nullable OnFilesChangeListener filesListener,
@NonNull OnLegacyMigrationListener migrationListener,
@Nullable UnaryOperator<String> idGenerator) {
- this(context, name, getDatabaseVersion(context), earlyUpgrade, legacyProvider,
+ this(context, name, getDatabaseVersion(context), internal, earlyUpgrade, legacyProvider,
columnAnnotation, schemaListener, filesListener, migrationListener, idGenerator);
}
public DatabaseHelper(Context context, String name, int version,
- boolean earlyUpgrade, boolean legacyProvider,
+ boolean internal, boolean earlyUpgrade, boolean legacyProvider,
@Nullable Class<? extends Annotation> columnAnnotation,
@Nullable OnSchemaChangeListener schemaListener,
@Nullable OnFilesChangeListener filesListener,
@@ -194,13 +183,8 @@
mContext = context;
mName = name;
mVersion = version;
- if (isInternal()) {
- mVolumeName = MediaStore.VOLUME_INTERNAL;
- } else if (isExternal()) {
- mVolumeName = MediaStore.VOLUME_EXTERNAL;
- } else {
- throw new IllegalStateException("Db must be internal/external");
- }
+ mVolumeName = internal ? MediaStore.VOLUME_INTERNAL : MediaStore.VOLUME_EXTERNAL;
+ mInternal = internal;
mEarlyUpgrade = earlyUpgrade;
mLegacyProvider = legacyProvider;
mColumnAnnotation = columnAnnotation;
@@ -211,9 +195,9 @@
mMigrationFileName = "." + mVolumeName;
// Configure default filters until we hear differently
- if (isInternal()) {
+ if (mInternal) {
mFilterVolumeNames.add(MediaStore.VOLUME_INTERNAL);
- } else if (isExternal()) {
+ } else {
mFilterVolumeNames.add(MediaStore.VOLUME_EXTERNAL_PRIMARY);
}
@@ -244,7 +228,7 @@
mSchemaLock.writeLock().lock();
try {
db.beginTransaction();
- createLatestViews(db);
+ createLatestViews(db, mInternal);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
@@ -275,17 +259,16 @@
db.setCustomScalarFunction("_INSERT", (arg) -> {
if (arg != null && mFilesListener != null
&& !mSchemaLock.isWriteLockedByCurrentThread()) {
- final String[] split = arg.split(":", 5);
+ final String[] split = arg.split(":", 4);
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;
Trace.beginSection("_INSERT");
try {
mFilesListener.onInsert(DatabaseHelper.this, volumeName, id,
- mediaType, isDownload, isPending);
+ mediaType, isDownload);
} finally {
Trace.endSection();
}
@@ -295,7 +278,7 @@
db.setCustomScalarFunction("_UPDATE", (arg) -> {
if (arg != null && mFilesListener != null
&& !mSchemaLock.isWriteLockedByCurrentThread()) {
- final String[] split = arg.split(":", 14);
+ final String[] split = arg.split(":", 10);
final String volumeName = split[0];
final long oldId = Long.parseLong(split[1]);
final int oldMediaType = Integer.parseInt(split[2]);
@@ -303,19 +286,14 @@
final long newId = Long.parseLong(split[4]);
final int newMediaType = Integer.parseInt(split[5]);
final boolean newIsDownload = Integer.parseInt(split[6]) != 0;
- 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 String oldOwnerPackage = split[11];
- final String newOwnerPackage = split[12];
- final String oldPath = split[13];
+ final String oldOwnerPackage = split[7];
+ final String newOwnerPackage = split[8];
+ final String oldPath = split[9];
Trace.beginSection("_UPDATE");
try {
mFilesListener.onUpdate(DatabaseHelper.this, volumeName, oldId,
oldMediaType, oldIsDownload, newId, newMediaType, newIsDownload,
- oldIsTrashed, newIsTrashed, oldIsPending, newIsPending,
oldOwnerPackage, newOwnerPackage, oldPath);
} finally {
Trace.endSection();
@@ -394,19 +372,10 @@
public void onOpen(final SQLiteDatabase db) {
Log.v(TAG, "onOpen() for " + mName);
- tryMigrateFromLegacy(db);
+ tryMigrateFromLegacy(db, mInternal ? sMigrationLockInternal : sMigrationLockExternal);
}
- 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");
- }
-
+ private void tryMigrateFromLegacy(SQLiteDatabase db, Object migrationLock) {
final File migration = new File(mContext.getFilesDir(), mMigrationFileName);
// Another thread entering migration block will be blocked until the
// migration is complete from current thread.
@@ -421,7 +390,7 @@
// Temporarily drop indexes to improve migration performance
makePristineIndexes(db);
migrateFromLegacy(db);
- createLatestIndexes(db);
+ createLatestIndexes(db, mInternal);
} finally {
mSchemaLock.writeLock().unlock();
// Clear flag, since we should only attempt once
@@ -459,7 +428,7 @@
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
- mProjectionMapCache.put(clazz, map);
+ mProjectionMapCache.put(clazz, map);
}
result.putAll(map);
}
@@ -745,11 +714,6 @@
@VisibleForTesting
static void makePristineSchema(SQLiteDatabase db) {
- // We are dropping all tables and recreating new schema. This
- // is a clear indication of major change in MediaStore version.
- // Hence reset the Uuid whenever we change the schema.
- resetAndGetUuid(db);
-
// drop all triggers
Cursor c = db.query("sqlite_master", new String[] {"name"}, "type is 'trigger'",
null, null, null, null);
@@ -849,22 +813,18 @@
+ "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)");
- db.execSQL("CREATE TABLE deleted_media (_id INTEGER PRIMARY KEY AUTOINCREMENT,"
- + "old_id INTEGER UNIQUE, generation_modified INTEGER NOT NULL)");
+ + "_modifier INTEGER DEFAULT 0)");
- if (isExternal()) {
+ db.execSQL("CREATE TABLE log (time DATETIME, message TEXT)");
+ if (!mInternal) {
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);
- createLatestTriggers(db);
- createLatestIndexes(db);
+ createLatestViews(db, mInternal);
+ createLatestTriggers(db, mInternal);
+ createLatestIndexes(db, mInternal);
// 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
@@ -904,9 +864,7 @@
db.beginTransaction();
Log.d(TAG, "Starting migration from legacy provider");
- if (mMigrationListener != null) {
- mMigrationListener.onStarted(client, mVolumeName);
- }
+ mMigrationListener.onStarted(client, mVolumeName);
try (Cursor c = client.query(queryUri, sMigrateColumns.toArray(new String[0]),
extras, null)) {
final ContentValues values = new ContentValues();
@@ -935,7 +893,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 (isExternal() && volumePath != null &&
+ if (!mInternal && volumePath != null &&
mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) {
File playlistFile = new File(data);
@@ -999,9 +957,7 @@
final int progress = c.getPosition();
final int total = c.getCount();
Log.v(TAG, "Migrated " + progress + " of " + total + "...");
- if (mMigrationListener != null) {
- mMigrationListener.onProgress(client, mVolumeName, progress, total);
- }
+ mMigrationListener.onProgress(client, mVolumeName, progress, total);
}
}
@@ -1016,9 +972,7 @@
// only have one possible shot, so mark everything successful
db.setTransactionSuccessful();
db.endTransaction();
- if (mMigrationListener != null) {
- mMigrationListener.onFinished(client, mVolumeName);
- }
+ mMigrationListener.onFinished(client, mVolumeName);
}
}
@@ -1200,7 +1154,7 @@
c.close();
}
- private void createLatestViews(SQLiteDatabase db) {
+ private void createLatestViews(SQLiteDatabase db, boolean internal) {
makePristineViews(db);
if (mColumnAnnotation == null) {
@@ -1213,9 +1167,9 @@
filterVolumeNames = bindList(mFilterVolumeNames.toArray());
}
- if (isExternal()) {
+ if (!internal) {
db.execSQL("CREATE VIEW audio_playlists AS SELECT "
- + getColumnsForCollection(Audio.Playlists.class)
+ + String.join(",", getProjectionMap(Audio.Playlists.class).keySet())
+ " FROM files WHERE media_type=4");
}
@@ -1239,16 +1193,16 @@
+ "3 AS grouporder FROM searchhelpertitle WHERE (title != '')");
db.execSQL("CREATE VIEW audio AS SELECT "
- + getColumnsForCollection(Audio.Media.class)
+ + String.join(",", getProjectionMap(Audio.Media.class).keySet())
+ " FROM files WHERE media_type=2");
db.execSQL("CREATE VIEW video AS SELECT "
- + getColumnsForCollection(Video.Media.class)
+ + String.join(",", getProjectionMap(Video.Media.class).keySet())
+ " FROM files WHERE media_type=3");
db.execSQL("CREATE VIEW images AS SELECT "
- + getColumnsForCollection(Images.Media.class)
+ + String.join(",", getProjectionMap(Images.Media.class).keySet())
+ " FROM files WHERE media_type=1");
db.execSQL("CREATE VIEW downloads AS SELECT "
- + getColumnsForCollection(Downloads.class)
+ + String.join(",", getProjectionMap(Downloads.class).keySet())
+ " FROM files WHERE is_download=1");
db.execSQL("CREATE VIEW audio_artists AS SELECT "
@@ -1262,25 +1216,6 @@
+ " AND volume_name IN " + filterVolumeNames
+ " 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 " + filterVolumeNames
- + " 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
@@ -1307,10 +1242,6 @@
+ " GROUP BY genre_id");
}
- private String getColumnsForCollection(Class<?> collection) {
- return String.join(",", getProjectionMap(collection).keySet()) + ",_modifier";
- }
-
private static void makePristineTriggers(SQLiteDatabase db) {
// drop all triggers
Cursor c = db.query("sqlite_master", new String[] {"name"}, "type is 'trigger'",
@@ -1322,17 +1253,14 @@
c.close();
}
- private static void createLatestTriggers(SQLiteDatabase db) {
+ private static void createLatestTriggers(SQLiteDatabase db, boolean internal) {
makePristineTriggers(db);
final String insertArg =
- "new.volume_name||':'||new._id||':'||new.media_type||':'||new.is_download"
- + "||':'||new.is_pending";
+ "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"
- + "||':'||old.is_trashed||':'||new.is_trashed"
- + "||':'||old.is_pending||':'||new.is_pending"
+ "||':'||ifnull(old.owner_package_name,'null')"
+ "||':'||ifnull(new.owner_package_name,'null')||':'||old._data";
final String deleteArg =
@@ -1358,7 +1286,7 @@
c.close();
}
- private static void createLatestIndexes(SQLiteDatabase db) {
+ private static void createLatestIndexes(SQLiteDatabase db, boolean internal) {
makePristineIndexes(db);
db.execSQL("CREATE INDEX image_id_index on thumbnails(image_id)");
@@ -1396,7 +1324,7 @@
+ " WHERE (is_alarm IS 1) OR (is_ringtone IS 1) OR (is_notification IS 1)");
}
- private static void updateAddOwnerPackageName(SQLiteDatabase db) {
+ private static void updateAddOwnerPackageName(SQLiteDatabase db, boolean internal) {
db.execSQL("ALTER TABLE files ADD COLUMN owner_package_name TEXT DEFAULT NULL");
// Derive new column value based on well-known paths
@@ -1429,94 +1357,83 @@
db.execSQL("ALTER TABLE files ADD COLUMN color_range INTEGER;");
}
- private static void updateAddHashAndPending(SQLiteDatabase db) {
+ private static void updateAddHashAndPending(SQLiteDatabase db, boolean internal) {
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) {
+ private static void updateAddDownloadInfo(SQLiteDatabase db, boolean internal) {
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) {
+ private static void updateAddAudiobook(SQLiteDatabase db, boolean internal) {
db.execSQL("ALTER TABLE files ADD COLUMN is_audiobook INTEGER DEFAULT 0;");
}
- 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;");
- }
-
- private static void updateAddRedactedUriId(SQLiteDatabase db) {
- db.execSQL("ALTER TABLE files ADD COLUMN redacted_uri_id TEXT DEFAULT NULL;");
- }
-
- private static void updateClearLocation(SQLiteDatabase db) {
+ private static void updateClearLocation(SQLiteDatabase db, boolean internal) {
db.execSQL("UPDATE files SET latitude=NULL, longitude=NULL;");
}
- private static void updateSetIsDownload(SQLiteDatabase db) {
+ private static void updateSetIsDownload(SQLiteDatabase db, boolean internal) {
db.execSQL("UPDATE files SET is_download=1 WHERE _data REGEXP '"
+ FileUtils.PATTERN_DOWNLOADS_FILE + "'");
}
- private static void updateAddExpiresAndTrashed(SQLiteDatabase db) {
+ private static void updateAddExpiresAndTrashed(SQLiteDatabase db, boolean internal) {
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) {
+ private static void updateAddGroupId(SQLiteDatabase db, boolean internal) {
db.execSQL("ALTER TABLE files ADD COLUMN group_id INTEGER DEFAULT NULL;");
}
- private static void updateAddDirectories(SQLiteDatabase db) {
+ private static void updateAddDirectories(SQLiteDatabase db, boolean internal) {
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) {
+ private static void updateAddXmpMm(SQLiteDatabase db, boolean internal) {
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) {
+ private static void updateAddPath(SQLiteDatabase db, boolean internal) {
db.execSQL("ALTER TABLE files ADD COLUMN relative_path TEXT DEFAULT NULL;");
}
- private static void updateAddVolumeName(SQLiteDatabase db) {
+ private static void updateAddVolumeName(SQLiteDatabase db, boolean internal) {
db.execSQL("ALTER TABLE files ADD COLUMN volume_name TEXT DEFAULT NULL;");
}
- private static void updateDirsMimeType(SQLiteDatabase db) {
+ private static void updateDirsMimeType(SQLiteDatabase db, boolean internal) {
db.execSQL("UPDATE files SET mime_type=NULL WHERE format="
+ MtpConstants.FORMAT_ASSOCIATION);
}
- private static void updateRelativePath(SQLiteDatabase db) {
+ private static void updateRelativePath(SQLiteDatabase db, boolean internal) {
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) {
+ private static void updateAddTranscodeSatus(SQLiteDatabase db, boolean internal) {
db.execSQL("ALTER TABLE files ADD COLUMN _transcode_status INTEGER DEFAULT 0;");
}
-
- private static void updateAddVideoCodecType(SQLiteDatabase db) {
+ private static void updateAddVideoCodecType(SQLiteDatabase db, boolean internal) {
db.execSQL("ALTER TABLE files ADD COLUMN _video_codec_type TEXT DEFAULT NULL;");
}
- private static void updateClearDirectories(SQLiteDatabase db) {
+ private static void updateClearDirectories(SQLiteDatabase db, boolean internal) {
db.execSQL("UPDATE files SET primary_directory=NULL, secondary_directory=NULL;");
}
- private static void updateRestructureAudio(SQLiteDatabase db) {
+ private static void updateRestructureAudio(SQLiteDatabase db, boolean internal) {
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;");
@@ -1540,7 +1457,7 @@
db.execSQL("UPDATE files SET date_modified=0 WHERE media_type=2;");
}
- private static void updateAddMetadata(SQLiteDatabase db) {
+ private static void updateAddMetadata(SQLiteDatabase db, boolean internal) {
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;");
@@ -1555,11 +1472,11 @@
db.execSQL("ALTER TABLE files ADD COLUMN iso INTEGER DEFAULT NULL;");
}
- private static void updateAddSceneCaptureType(SQLiteDatabase db) {
+ private static void updateAddSceneCaptureType(SQLiteDatabase db, boolean internal) {
db.execSQL("ALTER TABLE files ADD COLUMN scene_capture_type INTEGER DEFAULT NULL;");
}
- private static void updateMigrateLogs(SQLiteDatabase db) {
+ private static void updateMigrateLogs(SQLiteDatabase db, boolean internal) {
// Migrate any existing logs to new system
try (Cursor c = db.query("log", new String[] { "time", "message" },
null, null, null, null, null)) {
@@ -1572,42 +1489,32 @@
db.execSQL("DELETE FROM log;");
}
- private static void updateAddLocalMetadata(SQLiteDatabase db) {
+ private static void updateAddLocalMetadata(SQLiteDatabase db, boolean internal) {
db.execSQL("CREATE TABLE local_metadata (generation INTEGER DEFAULT 0)");
db.execSQL("INSERT INTO local_metadata VALUES (0)");
}
- private static void updateAddGeneration(SQLiteDatabase db) {
+ private static void updateAddGeneration(SQLiteDatabase db, boolean internal) {
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) {
+ private static void updateAddXmp(SQLiteDatabase db, boolean internal) {
db.execSQL("ALTER TABLE files ADD COLUMN xmp BLOB DEFAULT NULL;");
}
- private static void updateAudioAlbumId(SQLiteDatabase db) {
+ private static void updateAudioAlbumId(SQLiteDatabase db, boolean internal) {
// 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) {
+ private static void updateAddModifier(SQLiteDatabase db, boolean internal) {
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;",
- UserHandle.myUserId()));
- }
-
- private static void recomputeDataValues(SQLiteDatabase db) {
+ private static void recomputeDataValues(SQLiteDatabase db, boolean internal) {
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");
@@ -1638,7 +1545,9 @@
Log.d(TAG, "Recomputing " + c.getCount() + " MediaType values");
// Accumulate all the new MEDIA_TYPE updates.
+ final ContentValues values = new ContentValues();
while (c.moveToNext()) {
+ values.clear();
final long id = c.getLong(0);
final String mimeType = c.getString(1);
// Only update Document and Subtitle media type
@@ -1667,11 +1576,10 @@
static final int VERSION_P = 900;
static final int VERSION_Q = 1023;
static final int VERSION_R = 1115;
- static final int VERSION_S = 1209;
- // Leave some gaps in database version tagging to allow S schema changes
- // to go independent of T schema changes.
- static final int VERSION_T = 1301;
- public static final int VERSION_LATEST = VERSION_T;
+ // Leave some gaps in database version tagging to allow R schema changes
+ // to go independent of S schema changes.
+ static final int VERSION_S = 1204;
+ static final int VERSION_LATEST = VERSION_S;
/**
* This method takes care of updating all the tables in the database to the
@@ -1682,6 +1590,7 @@
*/
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
@@ -1695,25 +1604,25 @@
updateAddTitleResource(db);
}
if (fromVersion < 1000) {
- updateAddOwnerPackageName(db);
+ updateAddOwnerPackageName(db, internal);
}
if (fromVersion < 1003) {
updateAddColorSpaces(db);
}
if (fromVersion < 1004) {
- updateAddHashAndPending(db);
+ updateAddHashAndPending(db, internal);
}
if (fromVersion < 1005) {
- updateAddDownloadInfo(db);
+ updateAddDownloadInfo(db, internal);
}
if (fromVersion < 1006) {
- updateAddAudiobook(db);
+ updateAddAudiobook(db, internal);
}
if (fromVersion < 1007) {
- updateClearLocation(db);
+ updateClearLocation(db, internal);
}
if (fromVersion < 1008) {
- updateSetIsDownload(db);
+ updateSetIsDownload(db, internal);
}
if (fromVersion < 1009) {
// This database version added "secondary_bucket_id", but that
@@ -1721,18 +1630,18 @@
// update step is no longer needed.
}
if (fromVersion < 1010) {
- updateAddExpiresAndTrashed(db);
+ updateAddExpiresAndTrashed(db, internal);
}
if (fromVersion < 1012) {
recomputeDataValues = true;
}
if (fromVersion < 1013) {
- updateAddGroupId(db);
- updateAddDirectories(db);
+ updateAddGroupId(db, internal);
+ updateAddDirectories(db, internal);
recomputeDataValues = true;
}
if (fromVersion < 1014) {
- updateAddXmpMm(db);
+ updateAddXmpMm(db, internal);
}
if (fromVersion < 1015) {
// Empty version bump to ensure views are recreated
@@ -1741,43 +1650,43 @@
// Empty version bump to ensure views are recreated
}
if (fromVersion < 1017) {
- updateSetIsDownload(db);
+ updateSetIsDownload(db, internal);
recomputeDataValues = true;
}
if (fromVersion < 1018) {
- updateAddPath(db);
+ updateAddPath(db, internal);
recomputeDataValues = true;
}
if (fromVersion < 1019) {
// Only trigger during "external", so that it runs only once.
- if (isExternal()) {
+ if (!internal) {
deleteLegacyThumbnailData();
}
}
if (fromVersion < 1020) {
- updateAddVolumeName(db);
+ updateAddVolumeName(db, internal);
recomputeDataValues = true;
}
if (fromVersion < 1021) {
// Empty version bump to ensure views are recreated
}
if (fromVersion < 1022) {
- updateDirsMimeType(db);
+ updateDirsMimeType(db, internal);
}
if (fromVersion < 1023) {
- updateRelativePath(db);
+ updateRelativePath(db, internal);
}
if (fromVersion < 1100) {
// Empty version bump to ensure triggers are recreated
}
if (fromVersion < 1101) {
- updateClearDirectories(db);
+ updateClearDirectories(db, internal);
}
if (fromVersion < 1102) {
- updateRestructureAudio(db);
+ updateRestructureAudio(db, internal);
}
if (fromVersion < 1103) {
- updateAddMetadata(db);
+ updateAddMetadata(db, internal);
}
if (fromVersion < 1104) {
// Empty version bump to ensure views are recreated
@@ -1786,16 +1695,16 @@
recomputeDataValues = true;
}
if (fromVersion < 1106) {
- updateMigrateLogs(db);
+ updateMigrateLogs(db, internal);
}
if (fromVersion < 1107) {
- updateAddSceneCaptureType(db);
+ updateAddSceneCaptureType(db, internal);
}
if (fromVersion < 1108) {
- updateAddLocalMetadata(db);
+ updateAddLocalMetadata(db, internal);
}
if (fromVersion < 1109) {
- updateAddGeneration(db);
+ updateAddGeneration(db, internal);
}
if (fromVersion < 1110) {
// Empty version bump to ensure triggers are recreated
@@ -1804,7 +1713,7 @@
recomputeMediaTypeValues(db);
}
if (fromVersion < 1112) {
- updateAddXmp(db);
+ updateAddXmp(db, internal);
}
if (fromVersion < 1113) {
// Empty version bump to ensure triggers are recreated
@@ -1813,16 +1722,16 @@
// Empty version bump to ensure triggers are recreated
}
if (fromVersion < 1115) {
- updateAudioAlbumId(db);
+ updateAudioAlbumId(db, internal);
}
if (fromVersion < 1200) {
- updateAddTranscodeSatus(db);
+ updateAddTranscodeSatus(db, internal);
}
if (fromVersion < 1201) {
- updateAddVideoCodecType(db);
+ updateAddVideoCodecType(db, internal);
}
if (fromVersion < 1202) {
- updateAddModifier(db);
+ updateAddModifier(db, internal);
}
if (fromVersion < 1203) {
// Empty version bump to ensure views are recreated
@@ -1830,24 +1739,6 @@
if (fromVersion < 1204) {
// Empty version bump to ensure views are recreated
}
- if (fromVersion < 1205) {
- updateAddRecording(db);
- }
- if (fromVersion < 1206) {
- // Empty version bump to ensure views are recreated
- }
- if (fromVersion < 1207) {
- updateAddRedactedUriId(db);
- }
- if (fromVersion < 1208) {
- updateUserId(db);
- }
- if (fromVersion < 1209) {
- // Empty version bump to ensure views are recreated
- }
- if (fromVersion < 1301) {
- updateAddDeletedMediaTable(db);
- }
// If this is the legacy database, it's not worth recomputing data
// values locally, since they'll be recomputed after the migration
@@ -1856,21 +1747,21 @@
}
if (recomputeDataValues) {
- recomputeDataValues(db);
+ recomputeDataValues(db, internal);
}
}
// 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);
- createLatestTriggers(db);
+ createLatestViews(db, internal);
+ createLatestTriggers(db, internal);
getOrCreateUuid(db);
final long elapsedMillis = (SystemClock.elapsedRealtime() - startTime);
if (mSchemaListener != null) {
mSchemaListener.onSchemaChange(mVolumeName, fromVersion, toVersion,
- getItemCount(db), elapsedMillis, getOrCreateUuid(db));
+ getItemCount(db), elapsedMillis);
}
}
@@ -1883,7 +1774,7 @@
final long elapsedMillis = (SystemClock.elapsedRealtime() - startTime);
if (mSchemaListener != null) {
mSchemaListener.onSchemaChange(mVolumeName, fromVersion, toVersion,
- getItemCount(db), elapsedMillis, getOrCreateUuid(db));
+ getItemCount(db), elapsedMillis);
}
}
@@ -1899,23 +1790,19 @@
} catch (ErrnoException e) {
if (e.errno == OsConstants.ENODATA) {
// Doesn't exist yet, so generate and persist a UUID
- return resetAndGetUuid(db);
+ final String uuid = UUID.randomUUID().toString();
+ try {
+ Os.setxattr(db.getPath(), XATTR_UUID, uuid.getBytes(), 0);
+ } catch (ErrnoException e2) {
+ throw new RuntimeException(e);
+ }
+ return uuid;
} else {
throw new RuntimeException(e);
}
}
}
- private static @NonNull String resetAndGetUuid(SQLiteDatabase db) {
- final String uuid = UUID.randomUUID().toString();
- try {
- Os.setxattr(db.getPath(), XATTR_UUID, uuid.getBytes(), 0);
- } catch (ErrnoException e) {
- throw new RuntimeException(e);
- }
- return uuid;
- }
-
private static final long PASSTHROUGH_WAIT_TIMEOUT = 10 * DateUtils.SECOND_IN_MILLIS;
/**
@@ -1990,25 +1877,7 @@
null);
}
- public boolean isInternal() {
- return mName.equals(INTERNAL_DATABASE_NAME);
- }
-
public boolean isExternal() {
- // 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;
- }
+ return !mInternal;
}
}
diff --git a/src/com/android/providers/media/FileLookupResult.java b/src/com/android/providers/media/FileLookupResult.java
deleted file mode 100644
index f0e6bfb..0000000
--- a/src/com/android/providers/media/FileLookupResult.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media;
-
-/**
- * Wrapper class which contains transforms, transforms completion status and ioPath for transform
- * lookup query for a file and uid pair.
- */
-public final class FileLookupResult {
- public final int transforms;
- public final int transformsReason;
- public final int uid;
- public final boolean transformsComplete;
- public final boolean transformsSupported;
- public final String ioPath;
-
- public FileLookupResult(int transforms, int transformsReason, int uid,
- boolean transformsComplete, boolean transformsSupported, String ioPath) {
- this.transforms = transforms;
- this.transformsReason = transformsReason;
- this.uid = uid;
- this.transformsComplete = transformsComplete;
- this.transformsSupported = transformsSupported;
- this.ioPath = ioPath;
- }
-}
diff --git a/src/com/android/providers/media/FileOpenResult.java b/src/com/android/providers/media/FileOpenResult.java
deleted file mode 100644
index 1052f98..0000000
--- a/src/com/android/providers/media/FileOpenResult.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media;
-
-/**
- * Wrapper class which contains the result of an open.
- */
-public final class FileOpenResult {
- public final int status;
- public final int uid;
- public final int transformsUid;
- public final long[] redactionRanges;
-
- public FileOpenResult(int status, int uid, int transformsUid, long[] redactionRanges) {
- this.status = status;
- this.uid = uid;
- this.transformsUid = transformsUid;
- this.redactionRanges = redactionRanges;
- }
-}
diff --git a/src/com/android/providers/media/LocalCallingIdentity.java b/src/com/android/providers/media/LocalCallingIdentity.java
index 6191457..393ee50 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;
@@ -43,9 +42,6 @@
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.compat.CompatChanges;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
-import android.compat.annotation.EnabledSince;
import android.content.ContentProvider;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -61,28 +57,24 @@
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
-import com.android.modules.utils.build.SdkLevel;
import com.android.providers.media.util.LongArray;
-import com.android.providers.media.util.UserCache;
import java.util.Locale;
public class LocalCallingIdentity {
public final int pid;
public final int uid;
- private final UserHandle user;
private final Context context;
private final String packageNameUnchecked;
// Info used for logging permission checks
private final @Nullable String attributionTag;
private final Object lock = new Object();
- private LocalCallingIdentity(Context context, int pid, int uid, UserHandle user,
- String packageNameUnchecked, @Nullable String attributionTag) {
+ private LocalCallingIdentity(Context context, int pid, int uid, String packageNameUnchecked,
+ @Nullable String attributionTag) {
this.context = context;
this.pid = pid;
this.uid = uid;
- this.user = user;
this.packageNameUnchecked = packageNameUnchecked;
this.attributionTag = attributionTag;
}
@@ -99,8 +91,7 @@
private static final long UNKNOWN_ROW_ID = -1;
- public static LocalCallingIdentity fromBinder(Context context, ContentProvider provider,
- UserCache userCache) {
+ public static LocalCallingIdentity fromBinder(Context context, ContentProvider provider) {
String callingPackage = provider.getCallingPackageUnchecked();
int binderUid = Binder.getCallingUid();
if (callingPackage == null) {
@@ -116,37 +107,16 @@
if (callingAttributionTag == null) {
callingAttributionTag = context.getAttributionTag();
}
- UserHandle user;
- if (binderUid == Process.SHELL_UID || binderUid == Process.ROOT_UID) {
- // For requests coming from the shell (eg `content query`), assume they are
- // for the user we are running as.
- user = Process.myUserHandle();
- } else {
- user = UserHandle.getUserHandleForUid(binderUid);
- }
- // We need to use the cached variant here, because the uncached version may
- // make a binder transaction, which would cause infinite recursion here.
- // Using the cached variant is fine, because we shouldn't be getting any binder
- // requests for this volume before it has been mounted anyway, at which point
- // we must already know about the new user.
- if (!userCache.userSharesMediaWithParentCached(user)) {
- // It's possible that we got a cross-profile intent from a regular work profile; in
- // that case, the request was explicitly targeted at the media database of the owner
- // user; reflect that here.
- user = Process.myUserHandle();
- }
return new LocalCallingIdentity(context, Binder.getCallingPid(), binderUid,
- user, callingPackage, callingAttributionTag);
+ callingPackage, callingAttributionTag);
}
- public static LocalCallingIdentity fromExternal(Context context, @Nullable UserCache userCache,
- int uid) {
+ public static LocalCallingIdentity fromExternal(Context context, int uid) {
final String[] sharedPackageNames = context.getPackageManager().getPackagesForUid(uid);
if (sharedPackageNames == null || sharedPackageNames.length == 0) {
throw new IllegalArgumentException("UID " + uid + " has no associated package");
}
- LocalCallingIdentity ident = fromExternal(context, userCache, uid, sharedPackageNames[0],
- null);
+ LocalCallingIdentity ident = fromExternal(context, uid, sharedPackageNames[0], null);
ident.sharedPackageNames = sharedPackageNames;
ident.sharedPackageNamesResolved = true;
if (uid == Process.SHELL_UID) {
@@ -156,34 +126,19 @@
ident.hasPermissionResolved = PERMISSION_IS_REDACTION_NEEDED;
}
}
-
return ident;
}
- public static LocalCallingIdentity fromExternal(Context context, @Nullable UserCache userCache,
- int uid, String packageName, @Nullable String attributionTag) {
- UserHandle user = UserHandle.getUserHandleForUid(uid);
- if (userCache != null && !userCache.userSharesMediaWithParentCached(user)) {
- // This can happen on some proprietary app clone solutions, where the owner
- // and clone user each have their own MediaProvider instance, but refer to
- // each other for cross-user file access through the use of bind mounts.
- // In this case, assume the access is for the owner user, since that is
- // the only user for which we manage volumes anyway.
- user = Process.myUserHandle();
- }
- return new LocalCallingIdentity(context, -1, uid, user, packageName, attributionTag);
+ public static LocalCallingIdentity fromExternal(Context context, int uid, String packageName,
+ @Nullable String attributionTag) {
+ return new LocalCallingIdentity(context, -1, uid, packageName, attributionTag);
}
public static LocalCallingIdentity fromSelf(Context context) {
- return fromSelfAsUser(context, Process.myUserHandle());
- }
-
- public static LocalCallingIdentity fromSelfAsUser(Context context, UserHandle user) {
final LocalCallingIdentity ident = new LocalCallingIdentity(
context,
android.os.Process.myPid(),
android.os.Process.myUid(),
- user,
context.getOpPackageName(),
context.getAttributionTag());
@@ -192,8 +147,6 @@
// Use ident.attributionTag from context, hence no change
ident.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
ident.targetSdkVersionResolved = true;
- ident.shouldBypass = false;
- ident.shouldBypassResolved = true;
ident.hasPermission = ~(PERMISSION_IS_LEGACY_GRANTED | PERMISSION_IS_LEGACY_WRITE
| PERMISSION_IS_LEGACY_READ | PERMISSION_IS_REDACTION_NEEDED
| PERMISSION_IS_SHELL | PERMISSION_IS_DELEGATOR);
@@ -258,10 +211,6 @@
return Build.VERSION_CODES.CUR_DEVELOPMENT;
}
- public UserHandle getUser() {
- return user;
- }
-
public static final int PERMISSION_IS_SELF = 1 << 0;
public static final int PERMISSION_IS_SHELL = 1 << 1;
public static final int PERMISSION_IS_MANAGER = 1 << 2;
@@ -386,84 +335,7 @@
return true;
}
- return checkIsLegacyStorageGranted(context, uid, getPackageName(), attributionTag);
- }
-
- private volatile boolean shouldBypass;
- private volatile boolean shouldBypassResolved;
-
- /**
- * Allow apps holding {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
- * permission to request raw external storage access.
- */
- @ChangeId
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
- static final long ENABLE_RAW_MANAGE_EXTERNAL_STORAGE_ACCESS = 178209446L;
-
- /**
- * Allow apps holding {@link android.app.role}#SYSTEM_GALLERY role to request raw external
- * storage access.
- */
- @ChangeId
- @EnabledSince(targetSdkVersion = Build.VERSION_CODES.R)
- static final long ENABLE_RAW_SYSTEM_GALLERY_ACCESS = 183372781L;
-
- /**
- * Checks if app chooses to bypass database operations.
- *
- * <p>
- * Note that this method doesn't check if app qualifies to bypass database operations.
- *
- * @return {@code true} if AndroidManifest.xml of this app has
- * android:requestRawExternalStorageAccess=true
- * {@code false} otherwise.
- */
- public boolean shouldBypassDatabase(boolean isSystemGallery) {
- if (!shouldBypassResolved) {
- shouldBypass = shouldBypassDatabaseInternal(isSystemGallery);
- shouldBypassResolved = true;
- }
- return shouldBypass;
- }
-
- private boolean shouldBypassDatabaseInternal(boolean isSystemGallery) {
- if (!SdkLevel.isAtLeastS()) {
- // We need to parse the manifest flag ourselves here.
- // TODO(b/178209446): Parse app manifest to get new flag values
- return true;
- }
-
- final ApplicationInfo ai;
- try {
- ai = context.getPackageManager()
- .getApplicationInfo(getPackageName(), 0);
- if (ai != null) {
- final int requestRawExternalStorageValue
- = ai.getRequestRawExternalStorageAccess();
- if (requestRawExternalStorageValue
- != ApplicationInfo.RAW_EXTERNAL_STORAGE_ACCESS_DEFAULT) {
- return requestRawExternalStorageValue
- == ApplicationInfo.RAW_EXTERNAL_STORAGE_ACCESS_REQUESTED;
- }
- // Manifest flag is not set, hence return default value based on the category of the
- // app and targetSDK.
- if (isSystemGallery) {
- if (CompatChanges.isChangeEnabled(
- ENABLE_RAW_SYSTEM_GALLERY_ACCESS, uid)) {
- // If systemGallery, then the flag will default to false when they are
- // targeting targetSDK>=30.
- return false;
- }
- } else if (CompatChanges.isChangeEnabled(
- ENABLE_RAW_MANAGE_EXTERNAL_STORAGE_ACCESS, uid)) {
- // If app has MANAGE_EXTERNAL_STORAGE, the flag will default to false when they
- // are targeting targetSDK>=31.
- return false;
- }
- }
- } catch (NameNotFoundException e) {
- }
- return true;
+ return checkIsLegacyStorageGranted(context, uid, getPackageName());
}
private boolean isScopedStorageEnforced(boolean defaultScopedStorage,
@@ -552,20 +424,4 @@
return rowIdOfDeletedPaths.getOrDefault(path.toLowerCase(Locale.ROOT), UNKNOWN_ROW_ID);
}
}
-
- private volatile int applicationMediaCapabilitiesSupportedFlags = -1;
- private volatile int applicationMediaCapabilitiesUnsupportedFlags = -1;
-
- public int getApplicationMediaCapabilitiesSupportedFlags() {
- return applicationMediaCapabilitiesSupportedFlags;
- }
-
- public int getApplicationMediaCapabilitiesUnsupportedFlags() {
- return applicationMediaCapabilitiesUnsupportedFlags;
- }
-
- public void setApplicationMediaCapabilitiesFlags(int supportedFlags, int unsupportedFlags) {
- applicationMediaCapabilitiesSupportedFlags = supportedFlags;
- applicationMediaCapabilitiesUnsupportedFlags = unsupportedFlags;
- }
}
diff --git a/src/com/android/providers/media/MediaDocumentsProvider.java b/src/com/android/providers/media/MediaDocumentsProvider.java
index 798614f..222b9ea 100644
--- a/src/com/android/providers/media/MediaDocumentsProvider.java
+++ b/src/com/android/providers/media/MediaDocumentsProvider.java
@@ -23,7 +23,6 @@
import static android.provider.DocumentsContract.QUERY_ARG_FILE_SIZE_OVER;
import static android.provider.DocumentsContract.QUERY_ARG_LAST_MODIFIED_AFTER;
import static android.provider.DocumentsContract.QUERY_ARG_MIME_TYPES;
-import static android.provider.MediaStore.GET_MEDIA_URI_CALL;
import android.content.ContentResolver;
import android.content.ContentUris;
@@ -72,7 +71,6 @@
import com.android.providers.media.util.FileUtils;
import java.io.FileNotFoundException;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -202,7 +200,7 @@
* When underlying provider is ready, we kick off a notification of roots
* changed so they can be refreshed.
*/
- static void onMediaStoreReady(Context context) {
+ static void onMediaStoreReady(Context context, String volumeName) {
sMediaStoreReady = true;
notifyRootsChanged(context);
}
@@ -432,28 +430,6 @@
}
@Override
- public Bundle call(String method, String arg, Bundle extras) {
- Bundle bundle = super.call(method, arg, extras);
- if (bundle == null && !TextUtils.isEmpty(method)) {
- switch (method) {
- case GET_MEDIA_URI_CALL: {
- getContext().enforceCallingOrSelfPermission(
- android.Manifest.permission.WRITE_MEDIA_STORAGE, TAG);
- final Uri documentUri = extras.getParcelable(MediaStore.EXTRA_URI);
- final String docId = DocumentsContract.getDocumentId(documentUri);
- final Bundle out = new Bundle();
- final Uri uri = getUriForDocumentId(docId);
- out.putParcelable(MediaStore.EXTRA_URI, uri);
- return out;
- }
- default:
- Log.w(TAG, "unknown method passed to call(): " + method);
- }
- }
- return bundle;
- }
-
- @Override
public void deleteDocument(String docId) throws FileNotFoundException {
enforceShellRestrictions();
final Uri target = getUriForDocumentId(docId);
@@ -1042,7 +1018,6 @@
throws FileNotFoundException {
enforceShellRestrictions();
final Uri target = getUriForDocumentId(docId);
- final int callingUid = Binder.getCallingUid();
if (!"r".equals(mode)) {
throw new IllegalArgumentException("Media is read-only");
@@ -1051,27 +1026,12 @@
// Delegate to real provider
final long token = Binder.clearCallingIdentity();
try {
- return openFileForRead(target, callingUid);
+ return getContext().getContentResolver().openFileDescriptor(target, mode);
} finally {
Binder.restoreCallingIdentity(token);
}
}
- public ParcelFileDescriptor openFileForRead(final Uri target, final int callingUid)
- throws FileNotFoundException {
- final Bundle opts = new Bundle();
- opts.putInt(MediaStore.EXTRA_MEDIA_CAPABILITIES_UID, callingUid);
-
- AssetFileDescriptor afd =
- getContext().getContentResolver().openTypedAssetFileDescriptor(target, "*/*",
- opts);
- if (afd == null) {
- return null;
- }
-
- return afd.getParcelFileDescriptor();
- }
-
@Override
public AssetFileDescriptor openDocumentThumbnail(
String docId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException {
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index 4c3e5f6..f973786 100644
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -23,19 +23,19 @@
import static android.app.PendingIntent.FLAG_ONE_SHOT;
import static android.content.ContentResolver.QUERY_ARG_SQL_SELECTION;
import static android.content.ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS;
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.database.Cursor.FIELD_TYPE_BLOB;
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.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_RELATED_URI;
import static android.provider.MediaStore.getVolumeName;
-import static android.system.OsConstants.F_GETFL;
import static com.android.providers.media.DatabaseHelper.EXTERNAL_DATABASE_NAME;
import static com.android.providers.media.DatabaseHelper.INTERNAL_DATABASE_NAME;
@@ -64,7 +64,6 @@
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.extractDisplayName;
-import static com.android.providers.media.util.FileUtils.extractFileExtension;
import static com.android.providers.media.util.FileUtils.extractFileName;
import static com.android.providers.media.util.FileUtils.extractPathOwnerPackageName;
import static com.android.providers.media.util.FileUtils.extractRelativePath;
@@ -73,11 +72,9 @@
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.getAbsoluteSanitizedPath;
-import static com.android.providers.media.util.FileUtils.isCrossUserEnabled;
import static com.android.providers.media.util.FileUtils.isDataOrObbPath;
-import static com.android.providers.media.util.FileUtils.isDownload;
-import static com.android.providers.media.util.FileUtils.isExternalMediaDirectory;
import static com.android.providers.media.util.FileUtils.isObbOrChildPath;
+import static com.android.providers.media.util.FileUtils.isDownload;
import static com.android.providers.media.util.FileUtils.sanitizePath;
import static com.android.providers.media.util.Logging.LOGV;
import static com.android.providers.media.util.Logging.TAG;
@@ -90,9 +87,6 @@
import android.app.RecoverableSecurityException;
import android.app.RemoteAction;
import android.app.admin.DevicePolicyManager;
-import android.app.compat.CompatChanges;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ClipDescription;
@@ -139,23 +133,17 @@
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.OnCloseListener;
-import android.os.Parcelable;
-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;
import android.os.storage.StorageManager;
import android.os.storage.StorageManager.StorageVolumeCallback;
import android.os.storage.StorageVolume;
import android.preference.PreferenceManager;
import android.provider.BaseColumns;
import android.provider.Column;
-import android.provider.DeviceConfig;
-import android.provider.DeviceConfig.OnPropertiesChangedListener;
-import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.provider.MediaStore.Audio;
import android.provider.MediaStore.Audio.AudioColumns;
@@ -186,18 +174,13 @@
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
-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.fuse.ExternalStorageServiceImpl;
import com.android.providers.media.fuse.FuseDaemon;
-import com.android.providers.media.metrics.PulledMetrics;
-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.metrics.StatsdPuller;
import com.android.providers.media.playlist.Playlist;
import com.android.providers.media.scan.MediaScanner;
import com.android.providers.media.scan.ModernMediaScanner;
@@ -212,8 +195,8 @@
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.RedactingFileDescriptor;
import com.android.providers.media.util.SQLiteQueryBuilder;
-import com.android.providers.media.util.UserCache;
import com.android.providers.media.util.XmpInterface;
import com.google.common.hash.Hashing;
@@ -233,7 +216,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
@@ -241,7 +223,6 @@
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
-import java.util.UUID;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
@@ -256,23 +237,15 @@
*/
public class MediaProvider extends ContentProvider {
/**
- * Enables checks to stop apps from inserting and updating to private files via media provider.
- */
- @ChangeId
- @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.R)
- static final long ENABLE_CHECKS_FOR_PRIVATE_FILES = 172100307L;
-
- /**
* Regex of a selection string that matches a specific ID.
*/
static final Pattern PATTERN_SELECTION_ID = Pattern.compile(
"(?:image_id|video_id)\\s*=\\s*(\\d+)");
- /** File access by uid requires the transcoding transform */
- private static final int FLAG_TRANSFORM_TRANSCODING = 1 << 0;
-
- /** File access by uid is a synthetic path corresponding to a redacted URI */
- private static final int FLAG_TRANSFORM_REDACTION = 1 << 1;
+ /**
+ * Property that indicates whether fuse is enabled.
+ */
+ private static final String PROP_FUSE = "persist.sys.fuse";
/**
* These directory names aren't declared in Environment as final variables, and so we need to
@@ -292,17 +265,10 @@
private static final String DIRECTORY_DCIM_LOWER_CASE = "dcim";
private static final String DIRECTORY_DOCUMENTS_LOWER_CASE = "documents";
private static final String DIRECTORY_AUDIOBOOKS_LOWER_CASE = "audiobooks";
- private static final String DIRECTORY_RECORDINGS_LOWER_CASE = "recordings";
private static final String DIRECTORY_ANDROID_LOWER_CASE = "android";
private static final String DIRECTORY_MEDIA = "media";
private static final String DIRECTORY_THUMBNAILS = ".thumbnails";
- private static final List<String> PRIVATE_SUBDIRECTORIES_ANDROID = Arrays.asList("data", "obb");
- 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
@@ -339,29 +305,12 @@
private static final String MATCH_PENDING_FROM_FUSE = String.format("lower(%s) NOT REGEXP '%s'",
MediaColumns.DATA, PATTERN_PENDING_FILEPATH_FOR_SQL);
- /**
- * This flag is replaced with {@link MediaStore#QUERY_ARG_DEFER_SCAN} from S onwards and only
- * kept around for app compatibility in R.
- */
- private static final String QUERY_ARG_DO_ASYNC_SCAN = "android:query-arg-do-async-scan";
- /**
- * Enable option to defer the scan triggered as part of MediaProvider#update()
- */
- @ChangeId
- @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.R)
- static final long ENABLE_DEFERRED_SCAN = 180326732L;
-
- /**
- * Enable option to include database rows of files from recently unmounted
- * volume in MediaProvider#query
- */
- @ChangeId
- @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();
+ private static final boolean PROP_CROSS_USER_ALLOWED =
+ SystemProperties.getBoolean("external_storage.cross_user.enabled", false);
+ private static final String PROP_CROSS_USER_ROOT =
+ SystemProperties.get("external_storage.cross_user.root", null);
/**
* Set of {@link Cursor} columns that refer to raw filesystem paths.
@@ -376,32 +325,65 @@
sDataColumns.put(MediaStore.Audio.AlbumColumns.ALBUM_ART, null);
}
+ private static final Object sCacheLock = new Object();
+
+ @GuardedBy("sCacheLock")
+ private static final Set<String> sCachedExternalVolumeNames = new ArraySet<>();
+ @GuardedBy("sCacheLock")
+ private static final Map<String, File> sCachedVolumePaths = new ArrayMap<>();
+ @GuardedBy("sCacheLock")
+ private static final Map<String, Collection<File>> sCachedVolumeScanPaths = new ArrayMap<>();
+ @GuardedBy("sCacheLock")
+ private static final ArrayMap<File, String> sCachedVolumePathToId = new ArrayMap<>();
+
private static final int sUserId = UserHandle.myUserId();
- /**
- * Please use {@link getDownloadsProviderAuthority()} instead of using this directly.
- */
+ // WARNING/TODO (b/173505864): This will be replaced by signature APIs in S
private static final String DOWNLOADS_PROVIDER_AUTHORITY = "downloads";
- @GuardedBy("mPendingOpenInfo")
- private final Map<Integer, PendingOpenInfo> mPendingOpenInfo = new ArrayMap<>();
+ @GuardedBy("mShouldRedactThreadIds")
+ private final LongArray mShouldRedactThreadIds = new LongArray();
@GuardedBy("mNonHiddenPaths")
private final LRUCache<String, Integer> mNonHiddenPaths = new LRUCache<>(NON_HIDDEN_CACHE_SIZE);
public void updateVolumes() {
- mVolumeCache.update();
+ synchronized (sCacheLock) {
+ sCachedExternalVolumeNames.clear();
+ sCachedExternalVolumeNames.addAll(MediaStore.getExternalVolumeNames(getContext()));
+ Log.v(TAG, "Updated external volumes to: " + sCachedExternalVolumeNames.toString());
+
+ sCachedVolumePaths.clear();
+ sCachedVolumeScanPaths.clear();
+ sCachedVolumePathToId.clear();
+ try {
+ sCachedVolumeScanPaths.put(MediaStore.VOLUME_INTERNAL,
+ FileUtils.getVolumeScanPaths(getContext(), MediaStore.VOLUME_INTERNAL));
+ } catch (FileNotFoundException e) {
+ Log.wtf(TAG, "Failed to update volume " + MediaStore.VOLUME_INTERNAL, e);
+ }
+
+ for (String volumeName : sCachedExternalVolumeNames) {
+ try {
+ final Uri uri = MediaStore.Files.getContentUri(volumeName);
+ final StorageVolume volume = mStorageManager.getStorageVolume(uri);
+ sCachedVolumePaths.put(volumeName, volume.getDirectory());
+ sCachedVolumeScanPaths.put(volumeName,
+ FileUtils.getVolumeScanPaths(getContext(), volumeName));
+ sCachedVolumePathToId.put(volume.getDirectory(), volume.getId());
+ } catch (IllegalStateException | FileNotFoundException e) {
+ Log.wtf(TAG, "Failed to update volume " + volumeName, e);
+ }
+ }
+ }
+
// Update filters to reflect mounted volumes so users don't get
// confused by metadata from ejected volumes
ForegroundThread.getExecutor().execute(() -> {
- mExternalDatabase.setFilterVolumeNames(mVolumeCache.getExternalVolumeNames());
+ mExternalDatabase.setFilterVolumeNames(getExternalVolumeNames());
});
}
- public @NonNull MediaVolume getVolume(@NonNull String volumeName) throws FileNotFoundException {
- return mVolumeCache.findVolume(volumeName, mCallingIdentity.get().getUser());
- }
-
public @NonNull File getVolumePath(@NonNull String volumeName) throws FileNotFoundException {
// Ugly hack to keep unit tests passing, where we don't always have a
// Context to discover volumes with
@@ -409,57 +391,64 @@
return Environment.getExternalStorageDirectory();
}
- return mVolumeCache.getVolumePath(volumeName, mCallingIdentity.get().getUser());
+ synchronized (sCacheLock) {
+ if (sCachedVolumePaths.containsKey(volumeName)) {
+ return sCachedVolumePaths.get(volumeName);
+ }
+
+ // Nothing found above; let's ask directly and cache the answer
+ final File res = FileUtils.getVolumePath(getContext(), volumeName);
+ sCachedVolumePaths.put(volumeName, res);
+ return res;
+ }
}
public @NonNull String getVolumeId(@NonNull File file) throws FileNotFoundException {
- return mVolumeCache.getVolumeId(file);
- }
+ synchronized (sCacheLock) {
+ for (int i = 0; i < sCachedVolumePathToId.size(); i++) {
+ if (FileUtils.contains(sCachedVolumePathToId.keyAt(i), file)) {
+ return sCachedVolumePathToId.valueAt(i);
+ }
+ }
- private @NonNull Collection<File> getAllowedVolumePaths(String volumeName)
- throws FileNotFoundException {
- // This method is used to verify whether a path belongs to a certain volume name;
- // we can't always use the calling user's identity here to determine exactly which
- // volume is meant, because the MediaScanner may scan paths belonging to another user,
- // eg a clone user.
- // So, for volumes like external_primary, just return allowed paths for all users.
- List<UserHandle> users = mUserCache.getUsersCached();
- ArrayList<File> allowedPaths = new ArrayList<>();
- for (UserHandle user : users) {
- Collection<File> volumeScanPaths = mVolumeCache.getVolumeScanPaths(volumeName, user);
- allowedPaths.addAll(volumeScanPaths);
+ // Nothing found above; let's ask directly and cache the answer
+ final StorageVolume volume = mStorageManager.getStorageVolume(file);
+ if (volume == null) {
+ throw new FileNotFoundException("Missing volume for " + file);
+ }
+ sCachedVolumePathToId.put(volume.getDirectory(), volume.getId());
+ return volume.getId();
}
-
- return allowedPaths;
}
- /**
- * Frees any cache held by MediaProvider.
- *
- * @param bytes number of bytes which need to be freed
- */
- public void freeCache(long bytes) {
- mTranscodeHelper.freeCache(bytes);
+ public @NonNull Set<String> getExternalVolumeNames() {
+ synchronized (sCacheLock) {
+ return new ArraySet<>(sCachedExternalVolumeNames);
+ }
}
- public void onAnrDelayStarted(@NonNull String packageName, int uid, int tid, int reason) {
- mTranscodeHelper.onAnrDelayStarted(packageName, uid, tid, reason);
- }
+ public @NonNull Collection<File> getVolumeScanPaths(String volumeName)
+ throws FileNotFoundException {
+ synchronized (sCacheLock) {
+ if (sCachedVolumeScanPaths.containsKey(volumeName)) {
+ return new ArrayList<>(sCachedVolumeScanPaths.get(volumeName));
+ }
- private volatile Locale mLastLocale = Locale.getDefault();
+ // Nothing found above; let's ask directly and cache the answer
+ final Collection<File> res = FileUtils.getVolumeScanPaths(getContext(), volumeName);
+ sCachedVolumeScanPaths.put(volumeName, res);
+ return res;
+ }
+ }
private StorageManager mStorageManager;
private AppOpsManager mAppOpsManager;
private PackageManager mPackageManager;
private DevicePolicyManager mDevicePolicyManager;
- private UserManager mUserManager;
- private PickerUriResolver mPickerUriResolver;
-
- private UserCache mUserCache;
- private VolumeCache mVolumeCache;
private int mExternalStorageAuthorityAppId;
private int mDownloadsAuthorityAppId;
+
private Size mThumbSize;
/**
@@ -475,8 +464,8 @@
if (active) {
// TODO moltmann: Set correct featureId
mCachedCallingIdentity.put(uid,
- LocalCallingIdentity.fromExternal(getContext(), mUserCache, uid,
- packageName, null));
+ LocalCallingIdentity.fromExternal(getContext(), uid, packageName,
+ null));
} else {
mCachedCallingIdentity.remove(uid);
}
@@ -495,6 +484,9 @@
private OnOpChangedListener mModeListener =
(op, packageName) -> invalidateLocalCallingIdentityCache(packageName, "op " + op);
+ @GuardedBy("mNonWorkProfileUsers")
+ private final List<Integer> mNonWorkProfileUsers = new ArrayList<>();
+
/**
* Retrieves a cached calling identity or creates a new one. Also, always sets the app-op
* description for the calling identity.
@@ -502,11 +494,11 @@
private LocalCallingIdentity getCachedCallingIdentityForFuse(int uid) {
synchronized (mCachedCallingIdentityForFuse) {
PermissionUtils.setOpDescription("via FUSE");
- LocalCallingIdentity identity = mCachedCallingIdentityForFuse.get(uid);
- if (identity == null) {
- identity = LocalCallingIdentity.fromExternal(getContext(), mUserCache, uid);
+ LocalCallingIdentity ident = mCachedCallingIdentityForFuse.get(uid);
+ if (ident == null) {
+ ident = LocalCallingIdentity.fromExternal(getContext(), uid);
if (uid / PER_USER_RANGE == sUserId) {
- mCachedCallingIdentityForFuse.put(uid, identity);
+ mCachedCallingIdentityForFuse.put(uid, ident);
} else {
// In some app cloning designs, MediaProvider user 0 may
// serve requests for apps running as a "clone" user; in
@@ -514,7 +506,7 @@
// we don't get any invalidation events for these users.
}
}
- return identity;
+ return ident;
}
}
@@ -530,7 +522,7 @@
final LocalCallingIdentity cached = mCachedCallingIdentity
.get(Binder.getCallingUid());
return (cached != null) ? cached
- : LocalCallingIdentity.fromBinder(getContext(), this, mUserCache);
+ : LocalCallingIdentity.fromBinder(getContext(), this);
}
});
@@ -660,7 +652,7 @@
private final OnFilesChangeListener mFilesListener = new OnFilesChangeListener() {
@Override
public void onInsert(@NonNull DatabaseHelper helper, @NonNull String volumeName, long id,
- int mediaType, boolean isDownload, boolean isPending) {
+ int mediaType, boolean isDownload) {
handleInsertedRowForFuse(id);
acceptWithExpansion(helper::notifyInsert, volumeName, id, mediaType, isDownload);
@@ -673,10 +665,6 @@
// Tell our SAF provider so it knows when views are no longer empty
MediaDocumentsProvider.onMediaStoreInsert(getContext(), volumeName, mediaType, id);
-
- if (mExternalDbFacade.onFileInserted(mediaType, isPending)) {
- mPickerSyncController.notifyMediaEvent();
- }
});
}
@@ -684,8 +672,6 @@
public void onUpdate(@NonNull DatabaseHelper helper, @NonNull String volumeName,
long oldId, int oldMediaType, boolean oldIsDownload,
long newId, int newMediaType, boolean newIsDownload,
- boolean oldIsTrashed, boolean newIsTrashed,
- boolean oldIsPending, boolean newIsPending,
String oldOwnerPackage, String newOwnerPackage, String oldPath) {
final boolean isDownload = oldIsDownload || newIsDownload;
final Uri fileUri = MediaStore.Files.getContentUri(volumeName, oldId);
@@ -698,11 +684,6 @@
// Update the quota type on the filesystem
updateQuotaTypeForUri(fileUri, newMediaType);
}
-
- if (mExternalDbFacade.onFileUpdated(oldId, oldMediaType, newMediaType, oldIsTrashed,
- newIsTrashed, oldIsPending, newIsPending)) {
- mPickerSyncController.notifyMediaEvent();
- }
});
if (newMediaType != oldMediaType) {
@@ -721,8 +702,6 @@
int mediaType, boolean isDownload, String ownerPackageName, String path) {
handleDeletedRowForFuse(path, ownerPackageName, id);
acceptWithExpansion(helper::notifyDelete, volumeName, id, mediaType, isDownload);
- // Remove cached transcoded file if any
- mTranscodeHelper.deleteCachedTranscodeFile(id);
helper.postBackground(() -> {
// Item no longer exists, so revoke all access to it
@@ -748,10 +727,6 @@
// Tell our SAF provider so it can revoke too
MediaDocumentsProvider.onMediaStoreDelete(getContext(), volumeName, mediaType, id);
-
- if (mExternalDbFacade.onFileDeleted(id, mediaType)) {
- mPickerSyncController.notifyMediaEvent();
- }
});
}
};
@@ -838,27 +813,48 @@
}
}
+ private static boolean isCrossUserEnabled() {
+ return PROP_CROSS_USER_ALLOWED && Build.VERSION.SDK_INT <= Build.VERSION_CODES.R;
+ }
+
/**
* 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.
*/
- private void ensureDefaultFolders(@NonNull MediaVolume volume, @NonNull SQLiteDatabase db) {
- final String key = "created_default_folders_" + volume.getId();
-
- final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
- if (prefs.getInt(key, 0) == 0) {
- for (String folderName : DEFAULT_FOLDER_NAMES) {
- final File folder = new File(volume.getPath(), folderName);
- if (!folder.exists()) {
- folder.mkdirs();
- insertDirectory(db, folder.getAbsolutePath());
- }
+ private void ensureDefaultFolders(@NonNull String volumeName, @NonNull SQLiteDatabase db) {
+ try {
+ final File path = getVolumePath(volumeName);
+ final StorageVolume vol = mStorageManager.getStorageVolume(path);
+ final String key;
+ if (vol == null) {
+ Log.w(TAG, "Failed to ensure default folders for " + volumeName);
+ return;
}
- SharedPreferences.Editor editor = prefs.edit();
- editor.putInt(key, 1);
- editor.commit();
+ if (vol.isPrimary()) {
+ key = "created_default_folders";
+ } else {
+ key = "created_default_folders_" + vol.getMediaStoreVolumeName();
+ }
+
+ final SharedPreferences prefs = PreferenceManager
+ .getDefaultSharedPreferences(getContext());
+ if (prefs.getInt(key, 0) == 0) {
+ for (String folderName : DEFAULT_FOLDER_NAMES) {
+ final File folder = new File(vol.getDirectory(), folderName);
+ if (!folder.exists()) {
+ folder.mkdirs();
+ insertDirectory(db, folder.getAbsolutePath());
+ }
+ }
+
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(key, 1);
+ editor.commit();
+ }
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to ensure default folders for " + volumeName, e);
}
}
@@ -868,10 +864,10 @@
* {@link DatabaseHelper#getOrCreateUuid} doesn't match the UUID found on
* disk, then all thumbnails will be considered stable and will be deleted.
*/
- private void ensureThumbnailsValid(@NonNull MediaVolume volume, @NonNull SQLiteDatabase db) {
+ private void ensureThumbnailsValid(@NonNull String volumeName, @NonNull SQLiteDatabase db) {
final String uuidFromDatabase = DatabaseHelper.getOrCreateUuid(db);
try {
- for (File dir : getThumbnailDirectories(volume)) {
+ for (File dir : getThumbnailDirectories(volumeName)) {
if (!dir.exists()) {
dir.mkdirs();
}
@@ -899,7 +895,7 @@
}
}
} catch (IOException e) {
- Log.w(TAG, "Failed to ensure thumbnails valid for " + volume.getName(), e);
+ Log.w(TAG, "Failed to ensure thumbnails valid for " + volumeName, e);
}
}
@@ -916,8 +912,6 @@
public boolean onCreate() {
final Context context = getContext();
- mUserCache = new UserCache(context);
-
// Shift call statistics back to the original caller
Binder.setProxyTransactListener(mTransactListener);
@@ -925,9 +919,6 @@
mAppOpsManager = context.getSystemService(AppOpsManager.class);
mPackageManager = context.getPackageManager();
mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
- mUserManager = context.getSystemService(UserManager.class);
- mVolumeCache = new VolumeCache(context, mUserCache);
- mPickerUriResolver = new PickerUriResolver(context);
// Reasonable thumbnail size is half of the smallest screen edge width
final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
@@ -936,24 +927,12 @@
mMediaScanner = new ModernMediaScanner(context);
- mInternalDatabase = new DatabaseHelper(context, INTERNAL_DATABASE_NAME, false, false,
- Column.class, Metrics::logSchemaChange, mFilesListener, MIGRATION_LISTENER,
- mIdGenerator);
- mExternalDatabase = new DatabaseHelper(context, EXTERNAL_DATABASE_NAME, false, false,
- Column.class, Metrics::logSchemaChange, mFilesListener, MIGRATION_LISTENER,
- mIdGenerator);
- mExternalDbFacade = new ExternalDbFacade(getContext(), mExternalDatabase);
- mPickerDbFacade = new PickerDbFacade(context);
- mPickerSyncController = new PickerSyncController(context, mPickerDbFacade);
-
- if (SdkLevel.isAtLeastS()) {
- mTranscodeHelper = new TranscodeHelperImpl(context, this);
- } else {
- mTranscodeHelper = new TranscodeHelperNoOp();
- }
-
- // Create dir for redacted URI's path.
- new File("/storage/emulated/" + UserHandle.myUserId(), REDACTED_URI_DIR).mkdirs();
+ 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);
final IntentFilter packageFilter = new IntentFilter();
packageFilter.setPriority(10);
@@ -972,9 +951,9 @@
});
updateVolumes();
- attachVolume(MediaVolume.fromInternal(), /* validate */ false);
- for (MediaVolume volume : mVolumeCache.getExternalVolumes()) {
- attachVolume(volume, /* validate */ false);
+ attachVolume(MediaStore.VOLUME_INTERNAL, /* validate */ false);
+ for (String volumeName : getExternalVolumeNames()) {
+ attachVolume(volumeName, /* validate */ false);
}
// Watch for performance-sensitive activity
@@ -1005,26 +984,27 @@
// throw an IllegalArgumentException during MediaProvider startup. In combination with
// MediaProvider's CTS tests it should give us guarantees that OPSTR_NO_ISOLATED_STORAGE
// is defined.
- mAppOpsManager.startWatchingMode(AppOpsManager.OPSTR_NO_ISOLATED_STORAGE,
+ mAppOpsManager.startWatchingMode(PermissionUtils.OPSTR_NO_ISOLATED_STORAGE,
null /* all packages */, mModeListener);
} catch (IllegalArgumentException e) {
- Log.w(TAG, "Failed to start watching " + AppOpsManager.OPSTR_NO_ISOLATED_STORAGE, e);
+ Log.w(TAG, "Failed to start watching " + PermissionUtils.OPSTR_NO_ISOLATED_STORAGE, e);
}
ProviderInfo provider = mPackageManager.resolveContentProvider(
- getDownloadsProviderAuthority(), PackageManager.MATCH_DIRECT_BOOT_AWARE
+ DOWNLOADS_PROVIDER_AUTHORITY, PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
if (provider != null) {
mDownloadsAuthorityAppId = UserHandle.getAppId(provider.applicationInfo.uid);
}
- provider = mPackageManager.resolveContentProvider(getExternalStorageProviderAuthority(),
- PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
+ provider = mPackageManager.resolveContentProvider(
+ MediaStore.EXTERNAL_STORAGE_PROVIDER_AUTHORITY, PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
if (provider != null) {
mExternalStorageAuthorityAppId = UserHandle.getAppId(provider.applicationInfo.uid);
}
- PulledMetrics.initialize(context);
+ StatsdPuller.initialize(context);
return true;
}
@@ -1035,11 +1015,7 @@
}
public LocalCallingIdentity clearLocalCallingIdentity() {
- // We retain the user part of the calling identity, since we are executing
- // the call on behalf of that user, and we need to maintain the user context
- // to correctly resolve things like volumes
- UserHandle user = mCallingIdentity.get().getUser();
- return clearLocalCallingIdentity(LocalCallingIdentity.fromSelfAsUser(getContext(), user));
+ return clearLocalCallingIdentity(LocalCallingIdentity.fromSelf(getContext()));
}
public LocalCallingIdentity clearLocalCallingIdentity(LocalCallingIdentity replacement) {
@@ -1080,19 +1056,19 @@
Logging.trimPersistent();
// Scan all volumes to resolve any staleness
- for (MediaVolume volume : mVolumeCache.getExternalVolumes()) {
+ for (String volumeName : getExternalVolumeNames()) {
// Possibly bail before digging into each volume
signal.throwIfCanceled();
try {
- MediaService.onScanVolume(getContext(), volume, REASON_IDLE);
+ MediaService.onScanVolume(getContext(), volumeName, REASON_IDLE);
} catch (IOException e) {
Log.w(TAG, e);
}
// Ensure that our thumbnails are valid
mExternalDatabase.runWithTransaction((db) -> {
- ensureThumbnailsValid(volume, db);
+ ensureThumbnailsValid(volumeName, db);
return null;
});
}
@@ -1181,7 +1157,7 @@
final long expiredOneWeek =
((System.currentTimeMillis() - DateUtils.WEEK_IN_MILLIS) / 1000);
final long now = (System.currentTimeMillis() / 1000);
- final Long expiredTime = now + (FileUtils.DEFAULT_DURATION_EXTENDED / 1000);
+ final Long extendedTime = 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(
@@ -1192,7 +1168,6 @@
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);
@@ -1202,13 +1177,10 @@
totalDeleteCount += delete(Files.getContentUri(volumeName, id), null, null);
} else {
final String oriPath = c.getString(3);
-
- final boolean success = extendExpiredItem(db, oriPath, id, expiredTime,
- expiredTime + index);
+ final boolean success = extendExpiredItem(db, oriPath, id, extendedTime);
if (success) {
totalExtendedCount++;
}
- index++;
}
}
return new int[]{totalDeleteCount, totalExtendedCount};
@@ -1218,84 +1190,32 @@
}
/**
- * 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.
+ * 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}
*/
private boolean extendExpiredItem(@NonNull SQLiteDatabase db, @NonNull String originalPath,
- long id, long newExpiredTime, long adjustedExpiredTime) {
- String newPath = FileUtils.getAbsoluteExtendedPath(originalPath, newExpiredTime);
+ Long id, Long extendedTime) {
+ final String newPath = FileUtils.getAbsoluteExtendedPath(originalPath, extendedTime);
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
- + " in lower file system for extending item failed.";
+ final String errorMessage = "Rename " + originalPath + " to " + newPath + " failed.";
Log.e(TAG, errorMessage, e);
+ return false;
}
- 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;
}
public void onIdleMaintenanceStopped() {
@@ -1344,7 +1264,7 @@
}
public Uri scanFile(File file, int reason) {
- return scanFile(file, reason, null);
+ return mMediaScanner.scanFile(file, reason);
}
public Uri scanFile(File file, int reason, String ownerPackage) {
@@ -1378,23 +1298,9 @@
}
private boolean isAppCloneUserPair(int userId1, int userId2) {
- UserHandle user1 = UserHandle.of(userId1);
- UserHandle user2 = UserHandle.of(userId2);
- if (SdkLevel.isAtLeastS()) {
- if (mUserCache.userSharesMediaWithParent(user1)
- || mUserCache.userSharesMediaWithParent(user2)) {
- return true;
- }
- if (Build.VERSION.DEVICE_INITIAL_SDK_INT >= Build.VERSION_CODES.S) {
- // If we're on S or higher, and we shipped with S or higher, only allow the new
- // app cloning functionality
- return false;
- }
- // else, fall back to deprecated solution below on updating devices
- }
try {
Method isAppCloneUserPair = StorageManager.class.getMethod("isAppCloneUserPair",
- int.class, int.class);
+ int.class, int.class);
return (Boolean) isAppCloneUserPair.invoke(mStorageManager, userId1, userId2);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
Log.w(TAG, "isAppCloneUserPair failed. Users: " + userId1 + " and " + userId2);
@@ -1416,7 +1322,6 @@
return false;
}
boolean result = isAppCloneUserPair(0, userId);
-
Log.w(TAG, "isAppCloneUserPair for user " + userId + ": " + result);
return result;
@@ -1438,13 +1343,13 @@
return false;
}
- if (callingUserId != pathUserId && callingUserId != 0 && pathUserId != 0) {
+ if (callingUserId != 0 && pathUserId != 0) {
Log.w(TAG, "CrossUser at least one user is 0 check failed. Users: " + callingUserId
+ " and " + pathUserId);
return false;
}
- if (mUserCache.isWorkProfile(callingUserId) || mUserCache.isWorkProfile(pathUserId)) {
+ if (isWorkProfile(callingUserId) || isWorkProfile(pathUserId)) {
// Cross-user lookup not allowed if one user in the pair has a profile owner app
Log.w(TAG, "CrossUser work profile check failed. Users: " + callingUserId + " and "
+ pathUserId);
@@ -1462,167 +1367,30 @@
return result;
}
- /**
- * Called from FUSE to transform a file
- *
- * A transform can change the file contents for {@code uid} from {@code src} to {@code dst}
- * depending on {@code flags}. This allows the FUSE daemon serve different file contents for
- * the same file to different apps.
- *
- * The only supported transform for now is transcoding which re-encodes a file taken in a modern
- * format like HEVC to a legacy format like AVC.
- *
- * @param src file path to transform
- * @param dst file path to save transformed file
- * @param flags determines the kind of transform
- * @param readUid app that called us requesting transform
- * @param openUid app that originally made the open call
- * @param mediaCapabilitiesUid app for which the transform decision was made,
- * 0 if decision was made with openUid
- *
- * Called from JNI in jni/MediaProviderWrapper.cpp
- */
- @Keep
- public boolean transformForFuse(String src, String dst, int transforms, int transformsReason,
- int readUid, int openUid, int mediaCapabilitiesUid) {
- if ((transforms & FLAG_TRANSFORM_TRANSCODING) != 0) {
- if (mTranscodeHelper.isTranscodeFileCached(src, dst)) {
- Log.d(TAG, "Using transcode cache for " + src);
+ private boolean isWorkProfile(int userId) {
+ synchronized (mNonWorkProfileUsers) {
+ if (mNonWorkProfileUsers.contains(userId)) {
+ return false;
+ }
+ if (userId == 0) {
+ mNonWorkProfileUsers.add(userId);
+ // user 0 cannot have a profile owner
+ return false;
+ }
+ }
+
+ List<Integer> uids = new ArrayList<>();
+ for (ApplicationInfo ai : mPackageManager.getInstalledApplications(MATCH_DIRECT_BOOT_AWARE
+ | MATCH_DIRECT_BOOT_UNAWARE | MATCH_ANY_USER)) {
+ if (((ai.uid / PER_USER_RANGE) == userId)
+ && mDevicePolicyManager.isProfileOwnerApp(ai.packageName)) {
return true;
}
-
- // In general we always mark the opener as causing transcoding.
- // However, if the mediaCapabilitiesUid is available then we mark the reader as causing
- // transcoding. This handles the case where a malicious app might want to take
- // advantage of mediaCapabilitiesUid by setting it to another app's uid and reading the
- // media contents itself; in such cases we'd mark the reader (malicious app) for the
- // cost of transcoding.
- //
- // openUid readUid mediaCapabilitiesUid
- // -------------------------------------------------------------------------------------
- // using picker SAF app app
- // abusive case bad app bad app victim
- // modern to lega-
- // -cy sharing modern legacy legacy
- //
- // we'd not be here in the below case.
- // legacy to mode-
- // -rn sharing legacy modern modern
-
- int transcodeUid = openUid;
- if (mediaCapabilitiesUid > 0) {
- Log.d(TAG, "Fix up transcodeUid to " + readUid + ". openUid " + openUid
- + ", mediaCapabilitiesUid " + mediaCapabilitiesUid);
- transcodeUid = readUid;
- }
- return mTranscodeHelper.transcode(src, dst, transcodeUid, transformsReason);
- }
- return true;
- }
-
- /**
- * Called from FUSE to get {@link FileLookupResult} for a {@code path} and {@code uid}
- *
- * {@link FileLookupResult} contains transforms, transforms completion status and ioPath
- * for transform lookup query for a file and uid.
- *
- * @param path file path to get transforms for
- * @param uid app requesting IO form kernel
- * @param tid FUSE thread id handling IO request from kernel
- *
- * Called from JNI in jni/MediaProviderWrapper.cpp
- */
- @Keep
- public FileLookupResult onFileLookupForFuse(String path, int uid, int tid) {
- uid = getBinderUidForFuse(uid, tid);
- if (isSyntheticFilePathForRedactedUri(path, uid)) {
- return getFileLookupResultsForRedactedUriPath(uid, 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;
- }
- }
-
- return new FileLookupResult(transforms, transformsReason, uid, transformsComplete,
- transformsSupported, ioPath);
- }
-
- private boolean isSyntheticFilePathForRedactedUri(String path, int uid) {
- if (path == null) return false;
-
- 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;
- }
-
- 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) {
- final LocalCallingIdentity token = clearLocalCallingIdentity();
- final String fileName = extractFileName(path);
-
- final DatabaseHelper helper;
- try {
- helper = getDatabaseForUri(FileUtils.getContentUriForPath(path));
- } catch (VolumeNotFoundException e) {
- throw new IllegalStateException("Volume not found for file: " + path);
- }
-
- try (final Cursor c = helper.runWithoutTransaction(
- (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);
- }
-
- return new FileLookupResult(FLAG_TRANSFORM_REDACTION, 0, uid, true, true,
- c.getString(0));
- } finally {
- restoreLocalCallingIdentity(token);
- }
- }
-
- public int getBinderUidForFuse(int uid, int tid) {
- if (uid != MY_UID) {
- return uid;
- }
-
- synchronized (mPendingOpenInfo) {
- PendingOpenInfo info = mPendingOpenInfo.get(tid);
- if (info == null) {
- return uid;
- }
- return info.uid;
+ synchronized (mNonWorkProfileUsers) {
+ mNonWorkProfileUsers.add(userId);
+ return false;
}
}
@@ -1838,23 +1606,16 @@
* <li> {@code column} is set or
* <li> {@code column} is {@link MediaColumns#IS_PENDING} and is set by FUSE and not owned by
* calling package.
- * <li> {@code column} is {@link MediaColumns#IS_PENDING}, is unset and is waiting for
- * metadata update from a deferred scan.
* </ul>
*/
private String getWhereClauseForMatchExclude(@NonNull String column) {
if (column.equalsIgnoreCase(MediaColumns.IS_PENDING)) {
- // Don't include rows that are pending for metadata
- final String pendingForMetadata = FileColumns._MODIFIER + "="
- + FileColumns._MODIFIER_CR_PENDING_METADATA;
- final String notPending = String.format("(%s=0 AND NOT %s)", column,
- pendingForMetadata);
+ final String callingPackage = getCallingPackageOrSelf();
final String matchSharedPackagesClause = FileColumns.OWNER_PACKAGE_NAME + " IN "
+ getSharedPackages();
// Include owned pending files from Fuse
- final String pendingFromFuse = String.format("(%s=1 AND %s AND %s)", column,
+ return String.format("%s=0 OR (%s=1 AND %s AND %s)", column, column,
MATCH_PENDING_FROM_FUSE, matchSharedPackagesClause);
- return "(" + notPending + " OR " + pendingFromFuse + ")";
}
return column + "=0";
}
@@ -1976,7 +1737,6 @@
public String[] getFilesInDirectoryForFuse(String path, int uid) {
final LocalCallingIdentity token =
clearLocalCallingIdentity(getCachedCallingIdentityForFuse(uid));
- PulledMetrics.logFileAccessViaFuse(getCallingUidOrSelf(), path);
try {
if (isPrivatePackagePathNotAccessibleByCaller(path)) {
@@ -2127,19 +1887,13 @@
return updateDatabaseForFuseRename(helper, oldPath, newPath, values, Bundle.EMPTY);
}
- private boolean updateDatabaseForFuseRename(@NonNull DatabaseHelper helper,
- @NonNull String oldPath, @NonNull String newPath, @NonNull ContentValues values,
- @NonNull Bundle qbExtras) {
- return updateDatabaseForFuseRename(helper, oldPath, newPath, values, qbExtras,
- FileUtils.getContentUriForPath(oldPath));
- }
-
/**
* Updates database entry for given {@code path} with {@code values}
*/
private boolean updateDatabaseForFuseRename(@NonNull DatabaseHelper helper,
@NonNull String oldPath, @NonNull String newPath, @NonNull ContentValues values,
- @NonNull Bundle qbExtras, Uri uriOldPath) {
+ @NonNull Bundle qbExtras) {
+ final Uri uriOldPath = FileUtils.getContentUriForPath(oldPath);
boolean allowHidden = isCallingPackageAllowedHidden();
final SQLiteQueryBuilder qbForUpdate = getQueryBuilder(TYPE_UPDATE,
matchUri(uriOldPath, allowHidden), uriOldPath, qbExtras, null);
@@ -2161,7 +1915,12 @@
}
if (retryUpdateWithReplace) {
- if (deleteForFuseRename(helper, oldPath, newPath, qbExtras, selection, 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) {
Log.i(TAG, "Retrying database update after deleting conflicting entry");
count = qbForUpdate.update(helper, values, selection, new String[]{oldPath});
} else {
@@ -2171,30 +1930,6 @@
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}.
*/
@@ -2490,17 +2225,12 @@
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,
- wasHidden, isHidden, isSameMimeType);
-
- if (!updateDatabaseForFuseRename(helper, oldPath, newPath, contentValues)) {
+ if (!updateDatabaseForFuseRename(helper, oldPath, newPath,
+ getContentValuesForFuseRename(newPath, newMimeType, wasHidden, isHidden,
+ isSameMimeType))) {
if (!bypassRestrictions) {
- // Check for other URI format grants for oldPath only. Check right before
- // returning EPERM, to leave positive case performance unaffected.
- if (!renameWithOtherUriGrants(helper, oldPath, newPath, contentValues)) {
- Log.e(TAG, "Calling package doesn't have write permission to rename file.");
- return OsConstants.EPERM;
- }
+ Log.e(TAG, "Calling package doesn't have write permission to rename file.");
+ return OsConstants.EPERM;
} else if (!maybeRemoveOwnerPackageForFuseRename(helper, newPath)) {
Log.wtf(TAG, "Couldn't clear owner package name for " + newPath);
return OsConstants.EPERM;
@@ -2538,21 +2268,6 @@
}
/**
- * Rename file by checking for other URI grants on oldPath
- *
- * We don't support replace scenario by checking for other URI grants on newPath (if it exists).
- */
- private boolean renameWithOtherUriGrants(DatabaseHelper helper, String oldPath, String newPath,
- ContentValues contentValues) {
- final Uri oldPathGrantedUri = getOtherUriGrantsForPath(oldPath, /* forWrite */ true);
- if (oldPathGrantedUri == null) {
- return false;
- }
- return updateDatabaseForFuseRename(helper, oldPath, newPath, contentValues, Bundle.EMPTY,
- oldPathGrantedUri);
- }
-
- /**
* Rename file/directory without imposing any restrictions.
*
* We don't impose any rename restrictions for apps that bypass scoped storage restrictions.
@@ -2588,7 +2303,6 @@
final String errorMessage = "Rename " + oldPath + " to " + newPath + " failed. ";
final LocalCallingIdentity token =
clearLocalCallingIdentity(getCachedCallingIdentityForFuse(uid));
- PulledMetrics.logFileAccessViaFuse(getCallingUidOrSelf(), oldPath);
try {
if (isPrivatePackagePathNotAccessibleByCaller(oldPath)
@@ -2683,25 +2397,7 @@
public int checkUriPermission(@NonNull Uri uri, int uid,
/* @Intent.AccessUriMode */ int modeFlags) {
final LocalCallingIdentity token = clearLocalCallingIdentity(
- LocalCallingIdentity.fromExternal(getContext(), mUserCache, uid));
-
- 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;
- }
-
- 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;
- }
+ LocalCallingIdentity.fromExternal(getContext(), uid));
try {
final boolean allowHidden = isCallingPackageAllowedHidden();
@@ -2715,51 +2411,28 @@
}
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);
try (Cursor c = qb.query(helper,
new String[] { BaseColumns._ID }, null, null, null, null, null, null, null)) {
if (c.getCount() == 1) {
- c.moveToFirst();
- final long cursorId = c.getLong(0);
-
- long uriId = -1;
- try {
- uriId = ContentUris.parseId(uri);
- } catch (NumberFormatException ignored) {
- // if the id is not a number, the uri doesn't have a valid ID at the end of
- // the uri, (i.e., uri is uri of the table not of the item/row)
- }
-
- if (uriId != -1 && cursorId == uriId) {
- return PackageManager.PERMISSION_GRANTED;
- }
+ return PackageManager.PERMISSION_GRANTED;
}
}
- // For the uri with id cases, if it isn't returned in above query section, the result
- // isn't as expected. Don't grant the permission.
- switch (table) {
- case AUDIO_MEDIA_ID:
- case IMAGES_MEDIA_ID:
- case VIDEO_MEDIA_ID:
- case DOWNLOADS_ID:
- case FILES_ID:
- case AUDIO_MEDIA_ID_GENRES_ID:
- case AUDIO_GENRES_ID:
- case AUDIO_PLAYLISTS_ID:
- case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
- case AUDIO_ARTISTS_ID:
- case AUDIO_ALBUMS_ID:
+ try {
+ if (ContentUris.parseId(uri) != -1) {
return PackageManager.PERMISSION_DENIED;
- default:
- // continue below
- }
+ }
+ } catch (NumberFormatException ignored) { }
// If the uri is a valid content uri and doesn't have a valid ID at the end of the uri,
// (i.e., uri is uri of the table not of the item/row), and app doesn't request prefix
@@ -2769,6 +2442,9 @@
if ((modeFlags & Intent.FLAG_GRANT_PREFIX_URI_PERMISSION) == 0) {
return PackageManager.PERMISSION_GRANTED;
}
+
+ // For prefix grant on the uri with content uri without id, we don't allow apps to
+ // grant access as they might end up granting access to all files.
} finally {
restoreLocalCallingIdentity(token);
}
@@ -2801,13 +2477,6 @@
private Cursor queryInternal(Uri uri, String[] projection, Bundle queryArgs,
CancellationSignal signal, boolean forSelf) throws FallbackException {
- if (isPickerUri(uri)) {
- return mPickerUriResolver.query(uri, projection, queryArgs, signal,
- mCallingIdentity.get().pid, mCallingIdentity.get().uid);
- }
-
- final String volumeName = getVolumeName(uri);
- PulledMetrics.logVolumeAccessViaMediaProvider(getCallingUidOrSelf(), volumeName);
queryArgs = (queryArgs != null) ? queryArgs : new Bundle();
// INCLUDED_DEFAULT_DIRECTORIES extra should only be set inside MediaProvider.
@@ -2816,17 +2485,9 @@
final ArraySet<String> honoredArgs = new ArraySet<>();
DatabaseUtils.resolveQueryArgs(queryArgs, honoredArgs::add, this::ensureCustomCollator);
- Uri redactedUri = null;
- // REDACTED_URI_BUNDLE_KEY extra should only be set inside MediaProvider.
- queryArgs.remove(QUERY_ARG_REDACTED_URI);
- if (isRedactedUri(uri)) {
- redactedUri = uri;
- uri = getUriForRedactedUri(uri);
- queryArgs.putParcelable(QUERY_ARG_REDACTED_URI, redactedUri);
- }
-
uri = safeUncanonicalize(uri);
+ final String volumeName = getVolumeName(uri);
final int targetSdkVersion = getCallingPackageTargetSdkVersion();
final boolean allowHidden = isCallingPackageAllowedHidden();
final int table = matchUri(uri, allowHidden);
@@ -2855,22 +2516,6 @@
return c;
}
- // TODO(b/195008831): Add test to verify that apps can't access
- if (table == PICKER_INTERNAL) {
- final int limit = queryArgs.getInt(MediaStore.QUERY_ARG_LIMIT,
- PickerDbFacade.QueryFilterBuilder.LIMIT_DEFAULT);
- final long sizeBytes = queryArgs.getLong(MediaStore.QUERY_ARG_SIZE_BYTES,
- PickerDbFacade.QueryFilterBuilder.LONG_DEFAULT);
- final String mimeType = queryArgs.getString(MediaStore.QUERY_ARG_MIME_TYPE,
- PickerDbFacade.QueryFilterBuilder.STRING_DEFAULT);
-
- PickerDbFacade.QueryFilterBuilder qfb = new PickerDbFacade.QueryFilterBuilder(limit);
- qfb.setSizeBytes(sizeBytes);
- qfb.setMimeType(mimeType);
-
- return mPickerDbFacade.queryMedia(qfb.build());
- }
-
final DatabaseHelper helper = getDatabaseForUri(uri);
final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_QUERY, table, uri, queryArgs,
honoredArgs::add);
@@ -2932,12 +2577,6 @@
}
}
- // Update locale if necessary.
- if (helper.isInternal() && !Locale.getDefault().equals(mLastLocale)) {
- Log.i(TAG, "Updating locale within queryInternal");
- onLocaleChanged(false);
- }
-
final Cursor c = qb.query(helper, projection, queryArgs, signal);
if (c != null && !forSelf) {
// As a performance optimization, only configure notifications when
@@ -2952,131 +2591,9 @@
honoredArgs.toArray(new String[honoredArgs.size()]));
c.setExtras(extras);
}
-
- // Query was on a redacted URI, update the sensitive information such as the _ID, DATA etc.
- if (redactedUri != null && c != null) {
- try {
- return getRedactedUriCursor(redactedUri, c);
- } finally {
- c.close();
- }
- }
-
return c;
}
- private boolean isUriSupportedForRedaction(Uri uri) {
- final int match = matchUri(uri, true);
- return REDACTED_URI_SUPPORTED_TYPES.contains(match);
- }
-
- private Cursor getRedactedUriCursor(Uri redactedUri, @NonNull Cursor c) {
- final HashSet<String> columnNames = new HashSet<>(Arrays.asList(c.getColumnNames()));
- final MatrixCursor redactedUriCursor = new MatrixCursor(c.getColumnNames());
- final String redactedUriId = redactedUri.getLastPathSegment();
-
- if (!c.moveToFirst()) {
- return redactedUriCursor;
- }
-
- // NOTE: It is safe to assume that there will only be one entry corresponding to a
- // redacted URI as it corresponds to a unique DB entry.
- if (c.getCount() != 1) {
- throw new AssertionError("Two rows corresponding to " + redactedUri.toString()
- + " found, when only one expected");
- }
-
- final MatrixCursor.RowBuilder row = redactedUriCursor.newRow();
- for (String columnName : c.getColumnNames()) {
- final int colIndex = c.getColumnIndex(columnName);
- if (c.getType(colIndex) == FIELD_TYPE_BLOB) {
- row.add(c.getBlob(colIndex));
- } else {
- row.add(c.getString(colIndex));
- }
- }
-
- String ext = getFileExtensionFromCursor(c, columnNames);
- ext = ext == null ? "" : "." + ext;
- final String displayName = redactedUriId + ext;
- final String data = getPathForRedactedUriId(displayName);
-
-
- 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.DATA, row, data);
- updateRow(columnNames, MediaColumns.DOCUMENT_ID, row, null);
- updateRow(columnNames, MediaColumns.INSTANCE_ID, row, null);
- updateRow(columnNames, MediaColumns.BUCKET_ID, row, null);
-
- return redactedUriCursor;
- }
-
- @Nullable
- private static String getFileExtensionFromCursor(@NonNull Cursor c,
- @NonNull HashSet<String> columnNames) {
- if (columnNames.contains(MediaColumns.DATA)) {
- return extractFileExtension(c.getString(c.getColumnIndex(MediaColumns.DATA)));
- }
- if (columnNames.contains(MediaColumns.DISPLAY_NAME)) {
- return extractFileExtension(c.getString(c.getColumnIndex(MediaColumns.DISPLAY_NAME)));
- }
- 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)) {
- row.add(columnName, val);
- }
- }
-
- private Uri getUriForRedactedUri(Uri redactedUri) {
- final Uri.Builder builder = redactedUri.buildUpon();
- builder.path(null);
- final List<String> segments = redactedUri.getPathSegments();
- for (int i = 0; i < segments.size() - 1; i++) {
- builder.appendPath(segments.get(i));
- }
-
- DatabaseHelper helper;
- try {
- helper = getDatabaseForUri(redactedUri);
- } catch (VolumeNotFoundException e) {
- throw e.rethrowAsIllegalArgumentException();
- }
-
- try (final Cursor c = helper.runWithoutTransaction(
- (db) -> db.query("files", new String[]{MediaColumns._ID},
- FileColumns.REDACTED_URI_ID + "=?",
- new String[]{redactedUri.getLastPathSegment()}, null, null, null))) {
- if (!c.moveToFirst()) {
- throw new IllegalArgumentException(
- "Uri: " + redactedUri.toString() + " not found.");
- }
-
- builder.appendPath(c.getString(0));
- return builder.build();
- }
- }
-
- private boolean isRedactedUri(Uri uri) {
- String id = uri.getLastPathSegment();
- return id != null && id.startsWith(REDACTED_URI_ID_PREFIX)
- && id.length() == REDACTED_URI_ID_SIZE;
- }
-
@Override
public String getType(Uri url) {
final int match = matchUri(url, true);
@@ -3126,9 +2643,6 @@
return Video.Media.CONTENT_TYPE;
case DOWNLOADS:
return Downloads.CONTENT_TYPE;
-
- case PICKER_ID:
- return mPickerUriResolver.getType(url);
}
throw new IllegalStateException("Unknown URL : " + url);
}
@@ -3155,7 +2669,7 @@
/**
* Get the various file-related {@link MediaColumns} in the given
- * {@link ContentValues} into a consistent condition. Also validates that defined
+ * {@link ContentValues} into sane condition. Also validates that defined
* columns are valid for the given {@link Uri}, such as ensuring that only
* {@code image/*} can be inserted into
* {@link android.provider.MediaStore.Images}.
@@ -3183,25 +2697,13 @@
defaultMimeType = "audio/mpeg";
defaultMediaType = FileColumns.MEDIA_TYPE_AUDIO;
defaultPrimary = Environment.DIRECTORY_MUSIC;
- if (SdkLevel.isAtLeastS()) {
- allowedPrimary = Arrays.asList(
- Environment.DIRECTORY_ALARMS,
- Environment.DIRECTORY_AUDIOBOOKS,
- Environment.DIRECTORY_MUSIC,
- Environment.DIRECTORY_NOTIFICATIONS,
- Environment.DIRECTORY_PODCASTS,
- Environment.DIRECTORY_RECORDINGS,
- Environment.DIRECTORY_RINGTONES);
- } else {
- allowedPrimary = Arrays.asList(
- Environment.DIRECTORY_ALARMS,
- Environment.DIRECTORY_AUDIOBOOKS,
- Environment.DIRECTORY_MUSIC,
- Environment.DIRECTORY_NOTIFICATIONS,
- Environment.DIRECTORY_PODCASTS,
- FileUtils.DIRECTORY_RECORDINGS,
- Environment.DIRECTORY_RINGTONES);
- }
+ allowedPrimary = Arrays.asList(
+ Environment.DIRECTORY_ALARMS,
+ Environment.DIRECTORY_AUDIOBOOKS,
+ Environment.DIRECTORY_MUSIC,
+ Environment.DIRECTORY_NOTIFICATIONS,
+ Environment.DIRECTORY_PODCASTS,
+ Environment.DIRECTORY_RINGTONES);
break;
case VIDEO_MEDIA:
case VIDEO_MEDIA_ID:
@@ -3334,7 +2836,7 @@
}
}
- // Give ourselves reasonable defaults when missing
+ // Give ourselves sane defaults when missing
if (TextUtils.isEmpty(values.getAsString(MediaColumns.DISPLAY_NAME))) {
values.put(MediaColumns.DISPLAY_NAME,
String.valueOf(System.currentTimeMillis()));
@@ -3346,9 +2848,8 @@
}
mimeType = values.getAsString(MediaColumns.MIME_TYPE);
- // Quick check MIME type against table
+ // Sanity check MIME type against table
if (mimeType != null) {
- PulledMetrics.logMimeTypeAccess(getCallingUidOrSelf(), mimeType);
final int actualMediaType = MimeUtils.resolveMediaType(mimeType);
if (defaultMediaType == FileColumns.MEDIA_TYPE_NONE) {
// Give callers an opportunity to work with playlists and
@@ -3427,9 +2928,18 @@
// Next, consider allowing based on allowed primary directory
final String[] relativePath = values.getAsString(MediaColumns.RELATIVE_PATH).split("/");
- final String primary = extractTopLevelDir(relativePath);
+ final String primary = (relativePath.length > 0) ? relativePath[0] : null;
if (!validPath) {
validPath = containsIgnoreCase(allowedPrimary, primary);
+ if (!validPath) {
+ // Some app-clone implementations use a subdirectory of the main user's root
+ // to store app clone files; allow these as well.
+ if (isCrossUserEnabled() && primary.equals(PROP_CROSS_USER_ROOT) &&
+ relativePath.length >= 2) {
+ final String crossUserPrimary = relativePath[1];
+ validPath = containsIgnoreCase(allowedPrimary, crossUserPrimary);
+ }
+ }
}
// Next, consider allowing paths when referencing a related item
@@ -3519,11 +3029,9 @@
// files DISPLAY_NAME will not be same as file name.
FileUtils.computeValuesFromData(values, isFuseThread);
} else {
- assertFileColumnsConsistent(match, uri, values);
+ assertFileColumnsSane(match, uri, values);
}
- assertPrivatePathNotInValues(values);
-
// Drop columns that aren't relevant for special tables
switch (match) {
case AUDIO_ALBUMART:
@@ -3543,44 +3051,6 @@
}
/**
- * Check that values don't contain any external private path.
- * NOTE: The checks are gated on targetSDK S.
- */
- private void assertPrivatePathNotInValues(ContentValues values)
- throws IllegalArgumentException {
- if (!CompatChanges.isChangeEnabled(ENABLE_CHECKS_FOR_PRIVATE_FILES,
- Binder.getCallingUid())) {
- // For legacy apps, let the behaviour be as it is.
- return;
- }
-
- ArrayList<String> relativePaths = new ArrayList<String>();
- relativePaths.add(extractRelativePath(values.getAsString(MediaColumns.DATA)));
- relativePaths.add(values.getAsString(MediaColumns.RELATIVE_PATH));
- /**
- * Don't allow apps to insert/update database row to files in Android/data or
- * Android/obb dirs. These are app private directories and files in these private
- * directories can't be added to public media collection.
- */
- for (final String relativePath : relativePaths) {
- if (relativePath == null) continue;
-
- final String[] relativePathSegments = relativePath.split("/", 3);
- final String primary =
- (relativePathSegments.length > 0) ? relativePathSegments[0] : null;
- final String secondary =
- (relativePathSegments.length > 1) ? relativePathSegments[1] : "";
-
- if (DIRECTORY_ANDROID_LOWER_CASE.equalsIgnoreCase(primary)
- && PRIVATE_SUBDIRECTORIES_ANDROID.contains(
- secondary.toLowerCase(Locale.ROOT))) {
- throw new IllegalArgumentException(
- "Inserting private file: " + relativePath + " is not allowed.");
- }
- }
- }
-
- /**
* @return the default dir if {@code file} is a child of default dir and it's missing,
* {@code null} otherwise.
*/
@@ -3616,14 +3086,14 @@
* Check that any requested {@link MediaColumns#DATA} paths actually
* live on the storage volume being targeted.
*/
- private void assertFileColumnsConsistent(int match, Uri uri, ContentValues values)
+ private void assertFileColumnsSane(int match, Uri uri, ContentValues values)
throws VolumeArgumentException, VolumeNotFoundException {
if (!values.containsKey(MediaColumns.DATA)) return;
final String volumeName = resolveVolumeName(uri);
try {
- // Quick check that the requested path actually lives on volume
- final Collection<File> allowed = getAllowedVolumePaths(volumeName);
+ // Sanity check that the requested path actually lives on volume
+ final Collection<File> allowed = getVolumeScanPaths(volumeName);
final File actual = new File(values.getAsString(MediaColumns.DATA))
.getCanonicalFile();
if (!FileUtils.contains(allowed, actual)) {
@@ -3858,15 +3328,8 @@
}
public void onLocaleChanged() {
- onLocaleChanged(true);
- }
-
- private void onLocaleChanged(boolean forceUpdate) {
mInternalDatabase.runWithTransaction((db) -> {
- if (forceUpdate || !mLastLocale.equals(Locale.getDefault())) {
- localizeTitles(db);
- mLastLocale = Locale.getDefault();
- }
+ localizeTitles(db);
return null;
});
}
@@ -3974,20 +3437,7 @@
values.put(FileColumns._MODIFIER, FileColumns._MODIFIER_CR);
}
- // There is no meaning of an owner in the internal storage. It is shared by all users.
- // 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.isExternal()) {
- if (isAppCloneUserForFuse(ownerUserId)) {
- values.put(FileColumns._USER_ID, ownerUserId);
- } else {
- values.put(FileColumns._USER_ID, sUserId);
- }
- }
-
final long rowId;
- Uri newUri = uri;
{
if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) {
String name = values.getAsString(Audio.Playlists.NAME);
@@ -4014,12 +3464,6 @@
values.put(FileColumns.SIZE, file.length());
}
}
- // 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)) {
- newUri = MediaStore.Files.getContentUri(MediaStore.getVolumeName(uri));
- }
}
rowId = insertAllowingUpsert(qb, helper, values, path);
@@ -4030,7 +3474,7 @@
}
}
- return ContentUris.withAppendedId(newUri, rowId);
+ return ContentUris.withAppendedId(uri, rowId);
}
/**
@@ -4199,12 +3643,7 @@
private @Nullable Uri insertInternal(@NonNull Uri uri, @Nullable ContentValues initialValues,
@Nullable Bundle extras) throws FallbackException {
- final String originalVolumeName = getVolumeName(uri);
- PulledMetrics.logVolumeAccessViaMediaProvider(getCallingUidOrSelf(), originalVolumeName);
-
extras = (extras != null) ? extras : new Bundle();
- // REDACTED_URI_BUNDLE_KEY extra should only be set inside MediaProvider.
- extras.remove(QUERY_ARG_REDACTED_URI);
// INCLUDED_DEFAULT_DIRECTORIES extra should only be set inside MediaProvider.
extras.remove(INCLUDED_DEFAULT_DIRECTORIES);
@@ -4213,6 +3652,7 @@
final int match = matchUri(uri, allowHidden);
final int targetSdkVersion = getCallingPackageTargetSdkVersion();
+ final String originalVolumeName = getVolumeName(uri);
final String resolvedVolumeName = resolveVolumeName(uri);
// handle MEDIA_SCANNER before calling getDatabaseForUri()
@@ -4228,20 +3668,13 @@
if (match == VOLUMES) {
String name = initialValues.getAsString("name");
- MediaVolume volume = null;
- try {
- volume = getVolume(name);
- Uri attachedVolume = attachVolume(volume, /* validate */ true);
- if (mMediaScannerVolume != null && mMediaScannerVolume.equals(name)) {
- final DatabaseHelper helper = getDatabaseForUri(
- MediaStore.Files.getContentUri(mMediaScannerVolume));
- helper.mScanStartTime = SystemClock.elapsedRealtime();
- }
- return attachedVolume;
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Couldn't find volume with name " + volume.getName());
- return null;
+ Uri attachedVolume = attachVolume(name, /* validate */ true);
+ if (mMediaScannerVolume != null && mMediaScannerVolume.equals(name)) {
+ final DatabaseHelper helper = getDatabaseForUri(
+ MediaStore.Files.getContentUri(mMediaScannerVolume));
+ helper.mScanStartTime = SystemClock.elapsedRealtime();
}
+ return attachedVolume;
}
final DatabaseHelper helper = getDatabaseForUri(uri);
@@ -4363,7 +3796,7 @@
}
case IMAGES_THUMBNAILS: {
- if (helper.isInternal()) {
+ if (helper.mInternal) {
throw new UnsupportedOperationException(
"Writing to internal storage is not supported.");
}
@@ -4385,7 +3818,7 @@
}
case VIDEO_THUMBNAILS: {
- if (helper.isInternal()) {
+ if (helper.mInternal) {
throw new UnsupportedOperationException(
"Writing to internal storage is not supported.");
}
@@ -4464,7 +3897,7 @@
}
case AUDIO_ALBUMART: {
- if (helper.isInternal()) {
+ if (helper.mInternal) {
throw new UnsupportedOperationException("no internal album art allowed");
}
@@ -4586,14 +4019,6 @@
}
}
- /**
- * Gets {@link LocalCallingIdentity} for the calling package
- * TODO(b/170465810) Change the method name after refactoring.
- */
- LocalCallingIdentity getCachedCallingIdentityForTranscoding(int uid) {
- return getCachedCallingIdentityForFuse(uid);
- }
-
@Deprecated
private String getSharedPackages() {
final String[] sharedPackageNames = mCallingIdentity.get().getSharedPackageNames();
@@ -4619,23 +4044,6 @@
private static final int TYPE_DELETE = 3;
/**
- * Creating a new method for Transcoding to avoid any merge conflicts.
- * TODO(b/170465810): Remove this when getQueryBuilder code is refactored.
- */
- @NonNull SQLiteQueryBuilder getQueryBuilderForTranscoding(int type, int match,
- @NonNull Uri uri, @NonNull Bundle extras, @Nullable Consumer<String> honored) {
- // Force MediaProvider calling identity when accessing the db from transcoding to avoid
- // generating 'strict' SQL e.g forcing owner_package_name matches
- // We already handle the required permission checks for the app before we get here
- final LocalCallingIdentity token = clearLocalCallingIdentity();
- try {
- return getQueryBuilder(type, match, uri, extras, honored);
- } finally {
- restoreLocalCallingIdentity(token);
- }
- }
-
- /**
* Generate a {@link SQLiteQueryBuilder} that is filtered based on the
* runtime permissions and/or {@link Uri} grants held by the caller.
* <ul>
@@ -4689,7 +4097,7 @@
final String volumeName = MediaStore.getVolumeName(uri);
final String includeVolumes;
if (MediaStore.VOLUME_EXTERNAL.equals(volumeName)) {
- includeVolumes = bindList(mVolumeCache.getExternalVolumeNames().toArray());
+ includeVolumes = bindList(getExternalVolumeNames().toArray());
} else {
includeVolumes = bindList(volumeName);
}
@@ -4697,18 +4105,7 @@
final String matchSharedPackagesClause = FileColumns.OWNER_PACKAGE_NAME + " IN "
+ sharedPackages;
- boolean allowGlobal;
- final Uri redactedUri = extras.getParcelable(QUERY_ARG_REDACTED_URI);
- if (redactedUri != null) {
- if (forWrite) {
- throw new UnsupportedOperationException(
- "Writes on: " + redactedUri.toString() + " are not supported");
- }
- allowGlobal = checkCallingPermissionGlobal(redactedUri, false);
- } else {
- allowGlobal = checkCallingPermissionGlobal(uri, forWrite);
- }
-
+ final boolean allowGlobal = checkCallingPermissionGlobal(uri, forWrite);
final boolean allowLegacy =
forWrite ? isCallingPackageLegacyWrite() : isCallingPackageLegacyRead();
final boolean allowLegacyRead = allowLegacy && !forWrite;
@@ -4742,7 +4139,8 @@
// Only accept ALL_VOLUMES parameter up until R, because we're not convinced we want
// to commit to this as an API.
- final boolean includeAllVolumes = shouldIncludeRecentlyUnmountedVolumes(uri, extras);
+ final boolean includeAllVolumes = (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) ?
+ "1".equals(uri.getQueryParameter(ALL_VOLUMES)) : false;
final String callingPackage = getCallingPackageOrSelf();
switch (match) {
@@ -4987,8 +4385,8 @@
}
case AUDIO_ARTISTS_ID_ALBUMS: {
if (type == TYPE_QUERY) {
- qb.setTables("audio_artists_albums");
- qb.setProjectionMap(getProjectionMap(Audio.Artists.Albums.class));
+ qb.setTables("audio_albums");
+ qb.setProjectionMap(getProjectionMap(Audio.Albums.class));
final String artistId = uri.getPathSegments().get(3);
appendWhereStandalone(qb, "artist_id=?", artistId);
@@ -5220,36 +4618,6 @@
}
/**
- * @return {@code true} if app requests to include database rows from
- * recently unmounted volume.
- * {@code false} otherwise.
- */
- private boolean shouldIncludeRecentlyUnmountedVolumes(Uri uri, Bundle extras) {
- if (isFuseThread()) {
- // File path requests don't require to query from unmounted volumes.
- return false;
- }
-
- boolean isIncludeVolumesChangeEnabled = SdkLevel.isAtLeastS() &&
- CompatChanges.isChangeEnabled(ENABLE_INCLUDE_ALL_VOLUMES, Binder.getCallingUid());
- if ("1".equals(uri.getQueryParameter(ALL_VOLUMES))) {
- // Support uri parameter only in R OS and below. Apps should use
- // MediaStore#QUERY_ARG_RECENTLY_UNMOUNTED_VOLUMES on S OS onwards.
- if (!isIncludeVolumesChangeEnabled) {
- return true;
- }
- throw new IllegalArgumentException("Unsupported uri parameter \"all_volumes\"");
- }
- if (isIncludeVolumesChangeEnabled) {
- // MediaStore#QUERY_ARG_INCLUDE_RECENTLY_UNMOUNTED_VOLUMES is only supported on S OS and
- // for app targeting targetSdk>=S.
- return extras.getBoolean(MediaStore.QUERY_ARG_INCLUDE_RECENTLY_UNMOUNTED_VOLUMES,
- false);
- }
- return false;
- }
-
- /**
* Determine if given {@link Uri} has a
* {@link MediaColumns#OWNER_PACKAGE_NAME} column.
*/
@@ -5295,17 +4663,7 @@
private int deleteInternal(@NonNull Uri uri, @Nullable Bundle extras)
throws FallbackException {
- final String volumeName = getVolumeName(uri);
- PulledMetrics.logVolumeAccessViaMediaProvider(getCallingUidOrSelf(), volumeName);
-
extras = (extras != null) ? extras : new Bundle();
- // REDACTED_URI_BUNDLE_KEY extra should only be set inside MediaProvider.
- extras.remove(QUERY_ARG_REDACTED_URI);
-
- if (isRedactedUri(uri)) {
- // we don't support deletion on redacted uris.
- return 0;
- }
// INCLUDED_DEFAULT_DIRECTORIES extra should only be set inside MediaProvider.
extras.remove(INCLUDED_DEFAULT_DIRECTORIES);
@@ -5314,7 +4672,7 @@
final boolean allowHidden = isCallingPackageAllowedHidden();
final int match = matchUri(uri, allowHidden);
- switch (match) {
+ switch(match) {
case AUDIO_MEDIA_ID:
case AUDIO_PLAYLISTS_ID:
case VIDEO_MEDIA_ID:
@@ -5342,6 +4700,7 @@
int count = 0;
+ final String volumeName = getVolumeName(uri);
final int targetSdkVersion = getCallingPackageTargetSdkVersion();
// handle MEDIA_SCANNER before calling getDatabaseForUri()
@@ -5422,8 +4781,6 @@
final int isDownload = c.getInt(3);
final String mimeType = c.getString(4);
- // TODO(b/188782594) Consider logging mime type access on delete too.
-
// Forget that caller is owner of this item
mCallingIdentity.get().setOwned(id, false);
@@ -5436,6 +4793,8 @@
countPerMediaType[mediaType] += res;
}
+ // Only need to inform DownloadProvider about the downloads deleted on
+ // external volume.
if (isDownload == 1) {
deletedDownloadIds.put(id, mimeType);
}
@@ -5478,14 +4837,14 @@
}
if (deletedDownloadIds.size() > 0) {
- notifyDownloadManagerOnDelete(helper, deletedDownloadIds);
- }
-
- // Check for other URI format grants for File API call only. Check right before
- // returning count = 0, to leave positive cases performance unaffected.
- if (count == 0 && isFuseThread()) {
- count += deleteWithOtherUriGrants(uri, helper, projection, userWhere, userWhereArgs,
- extras);
+ // Do this on a background thread, since we don't want to make binder
+ // calls as part of a FUSE call.
+ helper.postBackground(() -> {
+ DownloadManager dm = getContext().getSystemService(DownloadManager.class);
+ if (dm != null) {
+ dm.onMediaStoreDownloadsDeleted(deletedDownloadIds);
+ }
+ });
}
if (isFilesTable && !isCallingPackageSelf()) {
@@ -5497,54 +4856,6 @@
return count;
}
- 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);
- final int mediaType = c.getInt(0);
- final String data = c.getString(1);
- final long id = c.getLong(2);
- final int isDownload = c.getInt(3);
- final String mimeType = c.getString(4);
-
- final Uri uriGranted = getOtherUriGrantsForPath(data, mediaType, Long.toString(id),
- /* forWrite */ true);
- if (uriGranted != null) {
- // 1. delete file
- deleteIfAllowed(uriGranted, extras, data);
- // 2. delete file row from the db
- final boolean allowHidden = isCallingPackageAllowedHidden();
- final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_DELETE,
- matchUri(uriGranted, allowHidden), uriGranted, extras, null);
- int count = qb.delete(helper, BaseColumns._ID + "=" + id, null);
-
- if (isDownload == 1) {
- final LongSparseArray<String> deletedDownloadIds = new LongSparseArray<>();
- deletedDownloadIds.put(id, mimeType);
- notifyDownloadManagerOnDelete(helper, deletedDownloadIds);
- }
- return count;
- }
- } catch (FileNotFoundException ignored) {
- // Do nothing. Returns 0 files deleted.
- }
- return 0;
- }
-
- private void notifyDownloadManagerOnDelete(DatabaseHelper helper,
- LongSparseArray<String> deletedDownloadIds) {
- // Do this on a background thread, since we don't want to make binder
- // calls as part of a FUSE call.
- helper.postBackground(() -> {
- DownloadManager dm = getContext().getSystemService(DownloadManager.class);
- if (dm != null) {
- dm.onMediaStoreDownloadsDeleted(deletedDownloadIds);
- }
- });
- }
-
/**
* Executes identical delete repeatedly within a single transaction until
* stability is reached. Combined with {@link #ID_NOT_PARENT_CLAUSE}, this
@@ -5568,65 +4879,6 @@
});
}
- @Nullable
- @VisibleForTesting
- Uri getRedactedUri(@NonNull Uri uri) {
- if (!isUriSupportedForRedaction(uri)) {
- return null;
- }
-
- DatabaseHelper helper;
- try {
- helper = getDatabaseForUri(uri);
- } catch (VolumeNotFoundException e) {
- throw e.rethrowAsIllegalArgumentException();
- }
-
- try (final Cursor c = helper.runWithoutTransaction(
- (db) -> db.query("files",
- new String[]{FileColumns.REDACTED_URI_ID}, FileColumns._ID + "=?",
- new String[]{uri.getLastPathSegment()}, null, null, null))) {
- // Database entry for uri not found.
- if (!c.moveToFirst()) return null;
-
- String redactedUriID = c.getString(c.getColumnIndex(FileColumns.REDACTED_URI_ID));
- if (redactedUriID == null) {
- // No redacted has even been created for this uri. Create a new redacted URI ID for
- // the uri and store it in the DB.
- redactedUriID = REDACTED_URI_ID_PREFIX + UUID.randomUUID().toString().replace("-",
- "");
-
- ContentValues cv = new ContentValues();
- cv.put(FileColumns.REDACTED_URI_ID, redactedUriID);
- int rowsAffected = helper.runWithTransaction(
- (db) -> db.update("files", cv, FileColumns._ID + "=?",
- new String[]{uri.getLastPathSegment()}));
- if (rowsAffected == 0) {
- // this shouldn't happen ideally, only reason this might happen is if the db
- // entry got deleted in b/w in which case we should return null.
- return null;
- }
- }
-
- // Create and return a uri with ID = redactedUriID.
- final Uri.Builder builder = ContentUris.removeId(uri).buildUpon();
- builder.appendPath(redactedUriID);
-
- return builder.build();
- }
- }
-
- @NonNull
- @VisibleForTesting
- List<Uri> getRedactedUri(@NonNull List<Uri> uris) {
- ArrayList<Uri> redactedUris = new ArrayList<>();
- for (Uri uri : uris) {
- redactedUris.add(getRedactedUri(uri));
- }
-
- return redactedUris;
- }
-
@Override
public Bundle call(String method, String arg, Bundle extras) {
Trace.beginSection("call");
@@ -5673,7 +4925,6 @@
}
case MediaStore.SCAN_FILE_CALL:
case MediaStore.SCAN_VOLUME_CALL: {
- final int userId = Binder.getCallingUid() / PER_USER_RANGE;
final LocalCallingIdentity token = clearLocalCallingIdentity();
final CallingIdentity providerToken = clearCallingIdentity();
try {
@@ -5686,13 +4937,7 @@
}
case MediaStore.SCAN_VOLUME_CALL: {
final String volumeName = arg;
- try {
- MediaVolume volume = mVolumeCache.findVolume(volumeName,
- UserHandle.of(userId));
- MediaService.onScanVolume(getContext(), volume, REASON_DEMAND);
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Failed to find volume " + volumeName, e);
- }
+ MediaService.onScanVolume(getContext(), volumeName, REASON_DEMAND);
break;
}
}
@@ -5756,7 +5001,7 @@
try (ContentProviderClient client = getContext().getContentResolver()
.acquireUnstableContentProviderClient(
- getExternalStorageProviderAuthority())) {
+ MediaStore.EXTERNAL_STORAGE_PROVIDER_AUTHORITY)) {
extras.putParcelable(MediaStore.EXTRA_URI, fileUri);
return client.call(method, null, extras);
} catch (RemoteException e) {
@@ -5768,61 +5013,24 @@
getContext().enforceCallingUriPermission(documentUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION, TAG);
- final int callingPid = mCallingIdentity.get().pid;
- final int callingUid = mCallingIdentity.get().uid;
- final String callingPackage = getCallingPackage();
- final CallingIdentity token = clearCallingIdentity();
- final String authority = documentUri.getAuthority();
-
- if (!authority.equals(MediaDocumentsProvider.AUTHORITY) &&
- !authority.equals(DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY)) {
- throw new IllegalArgumentException("Provider for this Uri is not supported.");
+ final Uri fileUri;
+ try (ContentProviderClient client = getContext().getContentResolver()
+ .acquireUnstableContentProviderClient(
+ MediaStore.EXTERNAL_STORAGE_PROVIDER_AUTHORITY)) {
+ final Bundle res = client.call(method, null, extras);
+ fileUri = res.getParcelable(MediaStore.EXTRA_URI);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e);
}
- try (ContentProviderClient client = getContext().getContentResolver()
- .acquireUnstableContentProviderClient(authority)) {
- final Bundle clientRes = client.call(method, null, extras);
- final Uri fileUri = clientRes.getParcelable(MediaStore.EXTRA_URI);
+ final LocalCallingIdentity token = clearLocalCallingIdentity();
+ try {
final Bundle res = new Bundle();
- final Uri mediaStoreUri = fileUri.getAuthority().equals(MediaStore.AUTHORITY) ?
- fileUri : queryForMediaUri(new File(fileUri.getPath()), null);
- copyUriPermissionGrants(documentUri, mediaStoreUri, callingPid,
- callingUid, callingPackage);
- res.putParcelable(MediaStore.EXTRA_URI, mediaStoreUri);
+ res.putParcelable(MediaStore.EXTRA_URI,
+ queryForMediaUri(new File(fileUri.getPath()), null));
return res;
} catch (FileNotFoundException e) {
throw new IllegalArgumentException(e);
- } catch (RemoteException e) {
- throw new IllegalStateException(e);
- } finally {
- restoreCallingIdentity(token);
- }
- }
- case MediaStore.GET_REDACTED_MEDIA_URI_CALL: {
- final Uri uri = extras.getParcelable(MediaStore.EXTRA_URI);
- // NOTE: It is ok to update the DB and return a redacted URI for the cases when
- // the user code only has read access, hence we don't check for write permission.
- enforceCallingPermission(uri, Bundle.EMPTY, false);
- final LocalCallingIdentity token = clearLocalCallingIdentity();
- try {
- final Bundle res = new Bundle();
- res.putParcelable(MediaStore.EXTRA_URI, getRedactedUri(uri));
- return res;
- } finally {
- restoreLocalCallingIdentity(token);
- }
- }
- case MediaStore.GET_REDACTED_MEDIA_URI_LIST_CALL: {
- final List<Uri> uris = extras.getParcelableArrayList(MediaStore.EXTRA_URI_LIST);
- // NOTE: It is ok to update the DB and return a redacted URI for the cases when
- // the user code only has read access, hence we don't check for write permission.
- enforceCallingPermission(uris, false);
- final LocalCallingIdentity token = clearLocalCallingIdentity();
- try {
- final Bundle res = new Bundle();
- res.putParcelableArrayList(MediaStore.EXTRA_URI_LIST,
- (ArrayList<? extends Parcelable>) getRedactedUri(uris));
- return res;
} finally {
restoreLocalCallingIdentity(token);
}
@@ -5836,105 +5044,11 @@
res.putParcelable(MediaStore.EXTRA_RESULT, pi);
return res;
}
- case MediaStore.IS_SYSTEM_GALLERY_CALL:
- final LocalCallingIdentity token = clearLocalCallingIdentity();
- try {
- String packageName = arg;
- int uid = extras.getInt(MediaStore.EXTRA_IS_SYSTEM_GALLERY_UID);
- boolean isSystemGallery = PermissionUtils.checkWriteImagesOrVideoAppOps(
- getContext(), uid, packageName, getContext().getAttributionTag());
- Bundle res = new Bundle();
- res.putBoolean(MediaStore.EXTRA_IS_SYSTEM_GALLERY_RESPONSE, isSystemGallery);
- return res;
- } 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, "Developer initiated cloud provider switch: " + cloudProvider);
- mPickerSyncController.setCloudProvider(cloudProvider);
- // fall through
- case MediaStore.SYNC_PROVIDERS_CALL:
- // Clear the binder calling identity so that we can sync the unexported
- // local_provider while running as MediaProvider
- final long t = Binder.clearCallingIdentity();
- try {
- // TODO(b/190713331): Remove after initial development
- Log.i(TAG, "Developer initiated provider sync");
- mPickerSyncController.syncPicker();
- return new Bundle();
- } finally {
- Binder.restoreCallingIdentity(t);
- }
default:
throw new UnsupportedOperationException("Unsupported call: " + method);
}
}
- private AssetFileDescriptor getOriginalMediaFormatFileDescriptor(Bundle extras)
- throws FileNotFoundException {
- try (ParcelFileDescriptor inputPfd =
- extras.getParcelable(MediaStore.EXTRA_FILE_DESCRIPTOR)) {
- final File file = getFileFromFileDescriptor(inputPfd);
- 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
- // requires either a db query or media scan which can lead to ANRs if apps
- // or the system implicitly call this method as part of a
- // MediaPlayer#setDataSource.
- throw new FileNotFoundException("Input file descriptor is already original");
- }
-
- FuseDaemon fuseDaemon = getFuseDaemonForFile(file);
- String outputPath = fuseDaemon.getOriginalMediaFormatFilePath(inputPfd);
- if (TextUtils.isEmpty(outputPath)) {
- throw new FileNotFoundException("Invalid path for original media format file");
- }
-
- 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 */,
- 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);
- throw new FileNotFoundException("Failed to fetch access mode for file descriptor");
- }
- }
-
- /**
- * Grant similar read/write access for mediaStoreUri as the caller has for documentsUri.
- *
- * Note: This function assumes that read permission check for documentsUri is already enforced.
- * Note: This function currently does not check/grant for persisted Uris. Support for this can
- * be added eventually, but the calling application will have to call
- * ContentResolver#takePersistableUriPermission(Uri, int) for the mediaStoreUri to persist.
- *
- * @param documentsUri DocumentsProvider format content Uri
- * @param mediaStoreUri MediaStore format content Uri
- * @param callingPid pid of the caller
- * @param callingUid uid of the caller
- * @param callingPackage package name of the caller
- */
- private void copyUriPermissionGrants(Uri documentsUri, Uri mediaStoreUri,
- int callingPid, int callingUid, String callingPackage) {
- // No need to check for read permission, as we enforce it already.
- int modeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION;
- if (getContext().checkUriPermission(documentsUri, callingPid, callingUid,
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == PERMISSION_GRANTED) {
- modeFlags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
- }
- getContext().grantUriPermission(callingPackage, mediaStoreUri, modeFlags);
- }
-
static List<Uri> collectUris(ClipData clipData) {
final ArrayList<Uri> res = new ArrayList<>();
for (int i = 0; i < clipData.getItemCount(); i++) {
@@ -5944,28 +5058,8 @@
}
/**
- * Return the filesystem path of the real file on disk that is represented
- * by the given {@link ParcelFileDescriptor}.
- *
- * Copied from {@link ParcelFileDescriptor#getFile}
- */
- private static File getFileFromFileDescriptor(ParcelFileDescriptor fileDescriptor)
- throws IOException {
- try {
- final String path = Os.readlink("/proc/self/fd/" + fileDescriptor.getFd());
- if (OsConstants.S_ISREG(Os.stat(path).st_mode)) {
- return new File(path);
- } else {
- throw new IOException("Not a regular file: " + path);
- }
- } catch (ErrnoException e) {
- throw e.rethrowAsIOException();
- }
- }
-
- /**
* Generate the {@link PendingIntent} for the given grant request. This
- * method also checks the incoming arguments for security purposes
+ * method also sanity checks the incoming arguments for security purposes
* before creating the privileged {@link PendingIntent}.
*/
private @NonNull PendingIntent createRequest(@NonNull String method, @NonNull Bundle extras) {
@@ -6050,7 +5144,7 @@
* {@code ORDER BY} clauses.
*/
private @NonNull String ensureCustomCollator(@NonNull String locale) {
- // Quick check that requested locale looks reasonable
+ // Quick sanity check that requested locale looks sane
new ULocale(locale);
final String collationName = "custom_" + locale.replaceAll("[^a-zA-Z]", "");
@@ -6087,12 +5181,12 @@
final long[] knownIdsRaw = knownIds.toArray();
Arrays.sort(knownIdsRaw);
- for (MediaVolume volume : mVolumeCache.getExternalVolumes()) {
+ for (String volumeName : getExternalVolumeNames()) {
final List<File> thumbDirs;
try {
- thumbDirs = getThumbnailDirectories(volume);
+ thumbDirs = getThumbnailDirectories(volumeName);
} catch (FileNotFoundException e) {
- Log.w(TAG, "Failed to resolve volume " + volume.getName(), e);
+ Log.w(TAG, "Failed to resolve volume " + volumeName, e);
continue;
}
@@ -6234,8 +5328,8 @@
}
};
- private List<File> getThumbnailDirectories(MediaVolume volume) throws FileNotFoundException {
- final File volumePath = volume.getPath();
+ private List<File> getThumbnailDirectories(String volumeName) throws FileNotFoundException {
+ final File volumePath = getVolumePath(volumeName);
return Arrays.asList(
FileUtils.buildPath(volumePath, Environment.DIRECTORY_MUSIC, DIRECTORY_THUMBNAILS),
FileUtils.buildPath(volumePath, Environment.DIRECTORY_MOVIES, DIRECTORY_THUMBNAILS),
@@ -6312,17 +5406,7 @@
private int updateInternal(@NonNull Uri uri, @Nullable ContentValues initialValues,
@Nullable Bundle extras) throws FallbackException {
- final String volumeName = getVolumeName(uri);
- PulledMetrics.logVolumeAccessViaMediaProvider(getCallingUidOrSelf(), volumeName);
-
extras = (extras != null) ? extras : new Bundle();
- // REDACTED_URI_BUNDLE_KEY extra should only be set inside MediaProvider.
- extras.remove(QUERY_ARG_REDACTED_URI);
-
- if (isRedactedUri(uri)) {
- // we don't support update on redacted uris.
- return 0;
- }
// Related items are only considered for new media creation, and they
// can't be leveraged to move existing content into blocked locations
@@ -6350,6 +5434,7 @@
int count;
+ final String volumeName = getVolumeName(uri);
final int targetSdkVersion = getCallingPackageTargetSdkVersion();
final boolean allowHidden = isCallingPackageAllowedHidden();
final int match = matchUri(uri, allowHidden);
@@ -6415,7 +5500,6 @@
boolean triggerInvalidate = false;
boolean triggerScan = false;
- boolean isUriPublished = false;
if (initialValues != null) {
// IDs are forever; nobody should be editing them
initialValues.remove(MediaColumns._ID);
@@ -6502,7 +5586,7 @@
// make sure metadata is updated
if (MediaColumns.IS_PENDING.equals(column)) {
triggerScan = true;
- isUriPublished = true;
+
// Explicitly clear columns used to ignore no-op scans,
// since we need to force a scan on publish
initialValues.putNull(MediaColumns.DATE_MODIFIED);
@@ -6628,7 +5712,7 @@
} else if (!Objects.equals(beforeVolume, probeVolume)) {
throw new IllegalArgumentException("Changing volume from " + beforePath + " to "
+ probePath + " not allowed");
- } else if (!isUpdateAllowedForOwnedPath(beforeOwner, probeOwner)) {
+ } else if (!Objects.equals(beforeOwner, probeOwner)) {
throw new IllegalArgumentException("Changing ownership from " + beforePath + " to "
+ probePath + " not allowed");
} else {
@@ -6676,10 +5760,8 @@
Trace.endSection();
}
- assertPrivatePathNotInValues(initialValues);
-
- // Make sure any updated paths look consistent
- assertFileColumnsConsistent(match, uri, initialValues);
+ // Make sure any updated paths look sane
+ assertFileColumnsSane(match, uri, initialValues);
if (initialValues.containsKey(FileColumns.DATA)) {
// If we're changing paths, invalidate any thumbnails
@@ -6740,31 +5822,6 @@
}
}
- boolean deferScan = false;
- if (triggerScan) {
- if (SdkLevel.isAtLeastS() &&
- CompatChanges.isChangeEnabled(ENABLE_DEFERRED_SCAN, Binder.getCallingUid())) {
- if (extras.containsKey(QUERY_ARG_DO_ASYNC_SCAN)) {
- throw new IllegalArgumentException("Unsupported argument " +
- QUERY_ARG_DO_ASYNC_SCAN + " used in extras");
- }
- deferScan = extras.getBoolean(QUERY_ARG_DEFER_SCAN, false);
- if (deferScan && initialValues.containsKey(MediaColumns.IS_PENDING) &&
- (initialValues.getAsInteger(MediaColumns.IS_PENDING) == 1)) {
- // if the scan runs in async, ensure that the database row is excluded in
- // default query until the metadata is updated by deferred scan.
- // Apps will still be able to see this database row when queried with
- // QUERY_ARG_MATCH_PENDING=MATCH_INCLUDE
- values.put(FileColumns._MODIFIER, FileColumns._MODIFIER_CR_PENDING_METADATA);
- qb.allowColumn(FileColumns._MODIFIER);
- }
- } else {
- // Allow apps to use QUERY_ARG_DO_ASYNC_SCAN if the device is R or app is targeting
- // targetSDK<=R.
- deferScan = extras.getBoolean(QUERY_ARG_DO_ASYNC_SCAN, false);
- }
- }
-
count = updateAllowingReplace(qb, helper, values, userWhere, userWhereArgs);
// If the caller tried (and failed) to update metadata, the file on disk
@@ -6784,20 +5841,15 @@
try (Cursor c = queryForSingleItem(updatedUri,
new String[] { FileColumns.DATA }, null, null, null)) {
final File file = new File(c.getString(0));
- final boolean notifyTranscodeHelper = isUriPublished;
- if (deferScan) {
+ boolean runScanFileInBackground =
+ extras.getBoolean(MediaStore.QUERY_ARG_DO_ASYNC_SCAN, false);
+ if (runScanFileInBackground) {
helper.postBackground(() -> {
scanFileAsMediaProvider(file, REASON_DEMAND);
- if (notifyTranscodeHelper) {
- notifyTranscodeHelperOnUriPublished(updatedUri);
- }
});
} else {
helper.postBlocking(() -> {
scanFileAsMediaProvider(file, REASON_DEMAND);
- if (notifyTranscodeHelper) {
- notifyTranscodeHelperOnUriPublished(updatedUri);
- }
});
}
} catch (Exception e) {
@@ -6814,52 +5866,6 @@
return count;
}
- private boolean isUpdateAllowedForOwnedPath(@Nullable String srcOwner,
- @Nullable String destOwner) {
- // 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);
-
- // 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();
- try {
- mTranscodeHelper.onUriPublished(uri);
- } finally {
- restoreLocalCallingIdentity(token);
- }
- });
- }
-
- private void notifyTranscodeHelperOnFileOpen(String path, String ioPath, int uid,
- int transformsReason) {
- BackgroundThread.getExecutor().execute(() -> {
- final LocalCallingIdentity token = clearLocalCallingIdentity();
- try {
- mTranscodeHelper.onFileOpen(path, ioPath, uid, transformsReason);
- } finally {
- restoreLocalCallingIdentity(token);
- }
- });
- }
-
/**
* Update row(s) that match {@code userWhere} in MediaProvider database with {@code values}.
* Treats update as replace for updates with conflicts.
@@ -7230,86 +6236,54 @@
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);
+ return openFileCommon(uri, mode, null);
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode, CancellationSignal signal)
throws FileNotFoundException {
- return openFileCommon(uri, mode, signal, /*opts*/ null);
+ return openFileCommon(uri, mode, signal);
}
- private ParcelFileDescriptor openFileCommon(Uri uri, String mode, CancellationSignal signal,
- @Nullable Bundle opts)
+ private ParcelFileDescriptor openFileCommon(Uri uri, String mode, CancellationSignal signal)
throws FileNotFoundException {
- opts = opts == null ? new Bundle() : opts;
- // REDACTED_URI_BUNDLE_KEY extra should only be set inside MediaProvider.
- opts.remove(QUERY_ARG_REDACTED_URI);
- if (isRedactedUri(uri)) {
- opts.putParcelable(QUERY_ARG_REDACTED_URI, uri);
- uri = getUriForRedactedUri(uri);
- }
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);
// Handle some legacy cases where we need to redirect thumbnails
- try {
- switch (match) {
- case AUDIO_ALBUMART_ID: {
- final long albumId = Long.parseLong(uri.getPathSegments().get(3));
- final Uri targetUri = ContentUris
- .withAppendedId(Audio.Albums.getContentUri(volumeName), albumId);
- return ensureThumbnail(targetUri, signal);
- }
- case AUDIO_ALBUMART_FILE_ID: {
- final long audioId = Long.parseLong(uri.getPathSegments().get(3));
- final Uri targetUri = ContentUris
- .withAppendedId(Audio.Media.getContentUri(volumeName), audioId);
- return ensureThumbnail(targetUri, signal);
- }
- case VIDEO_MEDIA_ID_THUMBNAIL: {
- final long videoId = Long.parseLong(uri.getPathSegments().get(3));
- final Uri targetUri = ContentUris
- .withAppendedId(Video.Media.getContentUri(volumeName), videoId);
- return ensureThumbnail(targetUri, signal);
- }
- case IMAGES_MEDIA_ID_THUMBNAIL: {
- final long imageId = Long.parseLong(uri.getPathSegments().get(3));
- final Uri targetUri = ContentUris
- .withAppendedId(Images.Media.getContentUri(volumeName), imageId);
- return ensureThumbnail(targetUri, signal);
- }
+ switch (match) {
+ case AUDIO_ALBUMART_ID: {
+ final long albumId = Long.parseLong(uri.getPathSegments().get(3));
+ final Uri targetUri = ContentUris
+ .withAppendedId(Audio.Albums.getContentUri(volumeName), albumId);
+ return ensureThumbnail(targetUri, signal);
}
- } finally {
- // We have to log separately here because openFileAndEnforcePathPermissionsHelper calls
- // a public MediaProvider API and so logs the access there.
- PulledMetrics.logVolumeAccessViaMediaProvider(getCallingUidOrSelf(), volumeName);
+ case AUDIO_ALBUMART_FILE_ID: {
+ final long audioId = Long.parseLong(uri.getPathSegments().get(3));
+ final Uri targetUri = ContentUris
+ .withAppendedId(Audio.Media.getContentUri(volumeName), audioId);
+ return ensureThumbnail(targetUri, signal);
+ }
+ case VIDEO_MEDIA_ID_THUMBNAIL: {
+ final long videoId = Long.parseLong(uri.getPathSegments().get(3));
+ final Uri targetUri = ContentUris
+ .withAppendedId(Video.Media.getContentUri(volumeName), videoId);
+ return ensureThumbnail(targetUri, signal);
+ }
+ case IMAGES_MEDIA_ID_THUMBNAIL: {
+ final long imageId = Long.parseLong(uri.getPathSegments().get(3));
+ final Uri targetUri = ContentUris
+ .withAppendedId(Images.Media.getContentUri(volumeName), imageId);
+ return ensureThumbnail(targetUri, signal);
+ }
}
- return openFileAndEnforcePathPermissionsHelper(uri, match, mode, signal, opts);
+ return openFileAndEnforcePathPermissionsHelper(uri, match, mode, signal);
}
@Override
@@ -7328,29 +6302,6 @@
Bundle opts, CancellationSignal signal) throws FileNotFoundException {
uri = safeUncanonicalize(uri);
- if (opts != null && opts.containsKey(MediaStore.EXTRA_FILE_DESCRIPTOR)) {
- // This is called as part of MediaStore#getOriginalMediaFormatFileDescriptor
- // We don't need to use the |uri| because the input fd already identifies the file and
- // we actually don't have a valid URI, we are going to identify the file via the fd.
- // While identifying the file, we also perform the following security checks.
- // 1. Find the FUSE file with the associated inode
- // 2. Verify that the binder caller opened it
- // 3. Verify the access level the fd is opened with (r/w)
- // 4. Open the original (non-transcoded) file *with* redaction enabled and the access
- // 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);
- }
-
- // 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
@@ -7362,7 +6313,7 @@
}
// Worst case, return the underlying file
- return new AssetFileDescriptor(openFileCommon(uri, "r", signal, opts), 0,
+ return new AssetFileDescriptor(openFileCommon(uri, "r", signal), 0,
AssetFileDescriptor.UNKNOWN_LENGTH);
}
@@ -7573,40 +6524,16 @@
final boolean callerIsOwner = Objects.equals(getCallingPackageOrSelf(), itemOwner);
if (hasOwner && !callerIsOwner) {
throw new IllegalStateException(
- "Only owner is able to interact with pending/trashed item " + item);
+ "Only owner is able to interact with pending item " + item);
}
}
- public File getFuseFile(File file) {
+ private 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 {
- Log.d(TAG, "Open with FUSE. FilePath: " + filePath
- + ". Uid: " + uid
- + ". Media Capabilities Uid: " + mediaCapabilitiesUid
- + ". ShouldRedact: " + shouldRedact
- + ". ShouldTranscode: " + shouldTranscode);
-
- int tid = android.os.Process.myTid();
- synchronized (mPendingOpenInfo) {
- mPendingOpenInfo.put(tid,
- new PendingOpenInfo(uid, mediaCapabilitiesUid, shouldRedact, transcodeReason));
- }
-
- try {
- return FileUtils.openSafely(getFuseFile(new File(filePath)), modeBits);
- } finally {
- synchronized (mPendingOpenInfo) {
- mPendingOpenInfo.remove(tid);
- }
- }
- }
-
private @NonNull FuseDaemon getFuseDaemonForFile(@NonNull File file)
throws FileNotFoundException {
final FuseDaemon daemon = ExternalStorageServiceImpl.getFuseDaemon(getVolumeId(file));
@@ -7645,16 +6572,10 @@
* a "/mnt/user" path.
*/
private ParcelFileDescriptor openFileAndEnforcePathPermissionsHelper(Uri uri, int match,
- String mode, CancellationSignal signal, @NonNull Bundle opts)
- throws FileNotFoundException {
+ String mode, CancellationSignal signal) throws FileNotFoundException {
int modeBits = ParcelFileDescriptor.parseMode(mode);
boolean forWrite = (modeBits & ParcelFileDescriptor.MODE_WRITE_ONLY) != 0;
- final Uri redactedUri = opts.getParcelable(QUERY_ARG_REDACTED_URI);
if (forWrite) {
- if (redactedUri != null) {
- throw new UnsupportedOperationException(
- "Write is not supported on " + redactedUri.toString());
- }
// Upgrade 'w' only to 'rw'. This allows us acquire a WR_LOCK when calling
// #shouldOpenWithFuse
modeBits |= ParcelFileDescriptor.MODE_READ_WRITE;
@@ -7686,11 +6607,7 @@
restoreLocalCallingIdentity(token);
}
- if (redactedUri == null) {
- checkAccess(uri, Bundle.EMPTY, file, forWrite);
- } else {
- checkAccess(redactedUri, Bundle.EMPTY, file, false);
- }
+ checkAccess(uri, Bundle.EMPTY, file, forWrite);
// We don't check ownership for files with IS_PENDING set by FUSE
if (isPending && !isPendingFromFuse(file)) {
@@ -7699,13 +6616,12 @@
final boolean callerIsOwner = Objects.equals(getCallingPackageOrSelf(), ownerPackageName);
// Figure out if we need to redact contents
- final boolean redactionNeeded =
- (redactedUri != null) || (!callerIsOwner && isRedactionNeeded(uri));
+ final boolean redactionNeeded = callerIsOwner ? false : isRedactionNeeded(uri);
final RedactionInfo redactionInfo;
try {
redactionInfo = redactionNeeded ? getRedactionRanges(file)
: new RedactionInfo(new long[0], new long[0]);
- } catch (IOException e) {
+ } catch(IOException e) {
throw new IllegalStateException(e);
}
@@ -7745,23 +6661,33 @@
// First, handle any redaction that is needed for caller
final ParcelFileDescriptor pfd;
final String filePath = file.getPath();
- final int uid = Binder.getCallingUid();
- final int transcodeReason = mTranscodeHelper.shouldTranscode(filePath, uid, opts);
- final boolean shouldTranscode = transcodeReason > 0;
- int mediaCapabilitiesUid = opts.getInt(MediaStore.EXTRA_MEDIA_CAPABILITIES_UID);
- if (!shouldTranscode || mediaCapabilitiesUid < Process.FIRST_APPLICATION_UID) {
- // Although 0 is a valid UID, it's not a valid app uid.
- // So, we use it to signify that mediaCapabilitiesUid is not set.
- mediaCapabilitiesUid = 0;
- }
if (redactionInfo.redactionRanges.length > 0) {
- // If fuse is enabled, we can provide an fd that points to the fuse
- // file system and handle redaction in the fuse handler when the caller reads.
- pfd = openWithFuse(filePath, uid, mediaCapabilitiesUid, modeBits,
- true /* shouldRedact */, shouldTranscode, transcodeReason);
- } else if (shouldTranscode) {
- pfd = openWithFuse(filePath, uid, mediaCapabilitiesUid, modeBits,
- false /* shouldRedact */, shouldTranscode, transcodeReason);
+ if (SystemProperties.getBoolean(PROP_FUSE, false)) {
+ // If fuse is enabled, we can provide an fd that points to the fuse
+ // file system and handle redaction in the fuse handler when the caller reads.
+ Log.i(TAG, "Redacting with new FUSE for " + filePath);
+ long tid = android.os.Process.myTid();
+ synchronized (mShouldRedactThreadIds) {
+ mShouldRedactThreadIds.add(tid);
+ }
+ try {
+ pfd = FileUtils.openSafely(getFuseFile(file), modeBits);
+ } finally {
+ synchronized (mShouldRedactThreadIds) {
+ mShouldRedactThreadIds.remove(mShouldRedactThreadIds.indexOf(tid));
+ }
+ }
+ } else {
+ // TODO(b/135341978): Remove this and associated code
+ // when fuse is on by default.
+ Log.i(TAG, "Redacting with old FUSE for " + filePath);
+ pfd = RedactingFileDescriptor.open(
+ getContext(),
+ file,
+ modeBits,
+ redactionInfo.redactionRanges,
+ redactionInfo.freeOffsets);
+ }
} else {
FuseDaemon daemon = null;
try {
@@ -7772,23 +6698,22 @@
// Always acquire a readLock. This allows us make multiple opens via lower
// filesystem
boolean shouldOpenWithFuse = daemon != null
- && daemon.shouldOpenWithFuse(filePath, true /* forRead */,
- lowerFsFd.getFd());
+ && daemon.shouldOpenWithFuse(filePath, true /* forRead */, lowerFsFd.getFd());
- if (shouldOpenWithFuse) {
+ if (SystemProperties.getBoolean(PROP_FUSE, false) && shouldOpenWithFuse) {
// If the file is already opened on the FUSE mount with VFS caching enabled
// we return an upper filesystem fd (via FUSE) to avoid file corruption
// resulting from cache inconsistencies between the upper and lower
// filesystem caches
- pfd = openWithFuse(filePath, uid, mediaCapabilitiesUid, modeBits,
- false /* shouldRedact */, shouldTranscode, transcodeReason);
+ Log.w(TAG, "Using FUSE for " + filePath);
+ pfd = FileUtils.openSafely(getFuseFile(file), modeBits);
try {
lowerFsFd.close();
} catch (IOException e) {
Log.w(TAG, "Failed to close lower filesystem fd " + file.getPath(), e);
}
} else {
- Log.i(TAG, "Open with lower FS for " + filePath + ". Uid: " + uid);
+ Log.i(TAG, "Using lower FS for " + filePath);
if (forWrite) {
// When opening for write on the lower filesystem, invalidate the VFS dentry
// so subsequent open/getattr calls will return correctly.
@@ -7868,27 +6793,6 @@
return mCallingIdentity.get().hasPermission(PERMISSION_IS_LEGACY_GRANTED);
}
- private boolean shouldBypassDatabase(int uid) {
- if (uid != android.os.Process.SHELL_UID && isCallingPackageManager()) {
- return mCallingIdentity.get().shouldBypassDatabase(false /*isSystemGallery*/);
- } else if (isCallingPackageSystemGallery()) {
- if (isCallingPackageLegacyWrite()) {
- // We bypass db operations for legacy system galleries with W_E_S (see b/167307393).
- // Tracking a longer term solution in b/168784136.
- return true;
- } else if (isCallingPackageRequestingLegacy()) {
- // If requesting legacy, app should have W_E_S along with SystemGallery appops.
- return false;
- } else if (!SdkLevel.isAtLeastS()) {
- // We don't parse manifest flags for SdkLevel<=R yet. Hence, we don't bypass
- // database updates for SystemGallery targeting R or above on R OS.
- return false;
- }
- return mCallingIdentity.get().shouldBypassDatabase(true /*isSystemGallery*/);
- }
- return false;
- }
-
private static int getFileMediaType(String path) {
final File file = new File(path);
final String mimeType = MimeUtils.resolveMimeType(file);
@@ -7958,16 +6862,26 @@
return false;
}
+ final String relativePath = extractRelativePath(path);
// Android/media is not considered private, because it contains media that is explicitly
// scanned and shared by other apps
- if (isExternalMediaDirectory(path)) {
+ if (relativePath.startsWith("Android/media")) {
return false;
}
return !isUidAllowedAccessToDataOrObbPathForFuse(mCallingIdentity.get().uid, path);
}
private boolean shouldBypassDatabaseAndSetDirtyForFuse(int uid, String path) {
- if (shouldBypassDatabase(uid)) {
+ boolean shouldBypass = false;
+ if (uid != android.os.Process.SHELL_UID && isCallingPackageManager()) {
+ shouldBypass = true;
+ } else if (isCallingPackageLegacyWrite() && isCallingPackageSystemGallery()) {
+ // We bypass db operations for legacy system galleries with W_E_S (see b/167307393).
+ // Tracking a longer term solution in b/168784136.
+ shouldBypass = true;
+ }
+
+ if (shouldBypass) {
synchronized (mNonHiddenPaths) {
File file = new File(path);
String key = file.getParent();
@@ -7982,9 +6896,8 @@
}
}
}
- return true;
}
- return false;
+ return shouldBypass;
}
/**
@@ -8059,26 +6972,9 @@
}
}
- private static final class PendingOpenInfo {
- public final int uid;
- public final int mediaCapabilitiesUid;
- public final boolean shouldRedact;
- public final int transcodeReason;
-
- public PendingOpenInfo(int uid, int mediaCapabilitiesUid, boolean shouldRedact,
- int transcodeReason) {
- this.uid = uid;
- this.mediaCapabilitiesUid = mediaCapabilitiesUid;
- this.shouldRedact = shouldRedact;
- this.transcodeReason = transcodeReason;
- }
- }
-
/**
* 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
@@ -8086,62 +6982,51 @@
* @return Ranges that should be redacted.
*
* @throws IOException if an error occurs while calculating the redaction ranges
+ *
+ * Called from JNI in jni/MediaProviderWrapper.cpp
*/
+ @Keep
@NonNull
- private long[] getRedactionRangesForFuse(String path, String ioPath, int original_uid, int uid,
- int tid, boolean forceRedaction) throws IOException {
- // |ioPath| might refer to a transcoded file path (which is not indexed in the db)
- // |path| will always refer to a valid _data column
- // We use |ioPath| for the filesystem access because in the case of transcoding,
- // we want to get redaction ranges from the transcoded file and *not* the original file
- final File file = new File(ioPath);
+ public long[] getRedactionRangesForFuse(String path, int uid, int tid) throws IOException {
+ final File file = new File(path);
- if (forceRedaction) {
- return getRedactionRanges(file).redactionRanges;
- }
-
- // When calculating redaction ranges initiated from MediaProvider, the redaction policy
- // is slightly different from the FUSE initiated opens redaction policy. targetSdk=29 from
- // MediaProvider requires redaction, but targetSdk=29 apps from FUSE don't require redaction
- // Hence, we check the mPendingOpenInfo object (populated when opens are initiated from
- // MediaProvider) if there's a pending open from MediaProvider with matching tid and uid and
- // use the shouldRedact decision there if there's one.
- synchronized (mPendingOpenInfo) {
- PendingOpenInfo info = mPendingOpenInfo.get(tid);
- if (info != null && info.uid == original_uid) {
- boolean shouldRedact = info.shouldRedact;
- if (shouldRedact) {
- return getRedactionRanges(file).redactionRanges;
- } else {
- return new long[0];
- }
+ // When we're calculating redaction ranges for MediaProvider, it means we're actually
+ // calculating redaction ranges for another app that called to MediaProvider through Binder.
+ // If the tid is in mShouldRedactThreadIds, we should redact, otherwise, we don't redact
+ if (uid == android.os.Process.myUid()) {
+ boolean shouldRedact = false;
+ synchronized (mShouldRedactThreadIds) {
+ shouldRedact = mShouldRedactThreadIds.indexOf(tid) != -1;
+ }
+ if (shouldRedact) {
+ return getRedactionRanges(file).redactionRanges;
+ } else {
+ return new long[0];
}
}
final LocalCallingIdentity token =
clearLocalCallingIdentity(getCachedCallingIdentityForFuse(uid));
+
+ long[] res = new long[0];
try {
if (!isRedactionNeeded()
|| shouldBypassFuseRestrictions(/*forWrite*/ false, path)) {
- return new long[0];
+ return res;
}
final Uri contentUri = FileUtils.getContentUriForPath(path);
final String[] projection = new String[]{
- MediaColumns.OWNER_PACKAGE_NAME, MediaColumns._ID , FileColumns.MEDIA_TYPE};
+ MediaColumns.OWNER_PACKAGE_NAME, MediaColumns._ID };
final String selection = MediaColumns.DATA + "=?";
- final String[] selectionArgs = new String[]{path};
+ final String[] selectionArgs = new String[] { path };
final String ownerPackageName;
- 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)) {
+ final Uri item;
+ try (final Cursor c = queryForSingleItem(contentUri, projection, selection,
+ selectionArgs, null)) {
c.moveToFirst();
ownerPackageName = c.getString(0);
- id = c.getInt(1);
- mediaType = c.getInt(2);
+ item = ContentUris.withAppendedId(contentUri, /*item id*/ c.getInt(1));
} 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
@@ -8153,31 +7038,17 @@
final boolean callerIsOwner = Objects.equals(getCallingPackageOrSelf(),
ownerPackageName);
-
- // Do not redact if the caller is the owner
- if (callerIsOwner) {
- return new long[0];
- }
-
- // 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,
+ final boolean callerHasUriPermission = getContext().checkUriPermission(
+ item, mCallingIdentity.get().pid, mCallingIdentity.get().uid,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == PERMISSION_GRANTED;
- 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];
- }
- return getRedactionRanges(file).redactionRanges;
+ if (!callerIsOwner && !callerHasUriPermission) {
+ res = getRedactionRanges(file).redactionRanges;
+ }
} finally {
restoreLocalCallingIdentity(token);
}
+ return res;
}
/**
@@ -8251,76 +7122,30 @@
* @param path the path of the file to be opened
* @param uid UID of the app requesting to open the file
* @param forWrite specifies if the file is to be opened for write
- * @return {@link FileOpenResult} with {@code status} {@code 0} upon success and
- * {@link FileOpenResult} with {@code status} {@link OsConstants#EACCES} if the operation is
- * illegal or not permitted for the given {@code uid} or if the calling package is a legacy app
- * that doesn't have right storage permission.
+ * @return 0 upon success. {@link OsConstants#EACCES} if the operation is illegal or not
+ * permitted for the given {@code uid} or if the calling package is a legacy app that doesn't
+ * have right storage permission.
*
* Called from JNI in jni/MediaProviderWrapper.cpp
*/
@Keep
- public FileOpenResult onFileOpenForFuse(String path, String ioPath, int uid, int tid,
- int transformsReason, boolean forWrite, boolean redact, boolean logTransformsMetrics) {
+ public int isOpenAllowedForFuse(String path, int uid, boolean forWrite) {
final LocalCallingIdentity token =
clearLocalCallingIdentity(getCachedCallingIdentityForFuse(uid));
- PulledMetrics.logFileAccessViaFuse(getCallingUidOrSelf(), path);
-
- boolean isSuccess = false;
-
- final int originalUid = getBinderUidForFuse(uid, tid);
- int mediaCapabilitiesUid = 0;
- final PendingOpenInfo pendingOpenInfo;
- synchronized (mPendingOpenInfo) {
- pendingOpenInfo = mPendingOpenInfo.get(tid);
- }
-
- if (pendingOpenInfo != null && pendingOpenInfo.uid == originalUid) {
- mediaCapabilitiesUid = pendingOpenInfo.mediaCapabilitiesUid;
- }
-
try {
- boolean forceRedaction = false;
- String redactedUriId = null;
- if (isSyntheticFilePathForRedactedUri(path, uid)) {
- if (forWrite) {
- // Redacted URIs are not allowed to update EXIF headers.
- return new FileOpenResult(OsConstants.EACCES /* status */, originalUid,
- mediaCapabilitiesUid, new long[0]);
- }
-
- 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;
-
- // 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]);
- }
-
if (isPrivatePackagePathNotAccessibleByCaller(path)) {
Log.e(TAG, "Can't open a file in another app's external directory!");
- return new FileOpenResult(OsConstants.ENOENT, originalUid, mediaCapabilitiesUid,
- new long[0]);
+ return OsConstants.ENOENT;
}
if (shouldBypassFuseRestrictions(forWrite, path)) {
- isSuccess = true;
- return new FileOpenResult(0 /* status */, originalUid, mediaCapabilitiesUid,
- redact ? getRedactionRangesForFuse(path, ioPath, originalUid, uid, tid,
- forceRedaction) : new long[0]);
+ 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 new FileOpenResult(OsConstants.EACCES /* status */, originalUid,
- mediaCapabilitiesUid, new long[0]);
+ return OsConstants.EACCES;
}
final Uri contentUri = FileUtils.getContentUriForPath(path);
@@ -8328,15 +7153,12 @@
MediaColumns._ID,
MediaColumns.OWNER_PACKAGE_NAME,
MediaColumns.IS_PENDING,
- FileColumns.MEDIA_TYPE,
- MediaColumns.IS_TRASHED
- };
+ FileColumns.MEDIA_TYPE};
final String selection = MediaColumns.DATA + "=?";
- final String[] selectionArgs = new String[]{path};
+ final String[] selectionArgs = new String[]{ path };
final long id;
final int mediaType;
final boolean isPending;
- final boolean isTrashed;
String ownerPackageName = null;
try (final Cursor c = queryForSingleItemAsMediaProvider(contentUri, projection,
selection,
@@ -8345,12 +7167,11 @@
ownerPackageName = c.getString(1);
isPending = c.getInt(2) != 0;
mediaType = c.getInt(3);
- isTrashed = c.getInt(4) != 0;
}
final File file = new File(path);
- Uri fileUri = MediaStore.Files.getContentUri(extractVolumeName(path), id);
+ final Uri fileUri = MediaStore.Files.getContentUri(extractVolumeName(path), id);
// We don't check ownership for files with IS_PENDING set by FUSE
- if (isTrashed || (isPending && !isPendingFromFuse(new File(path)))) {
+ if (isPending && !isPendingFromFuse(new File(path))) {
requireOwnershipForItem(ownerPackageName, fileUri);
}
@@ -8362,91 +7183,63 @@
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) {
+ if (!(isFilePathSupportForMediaUris() &&
+ hasOtherUriGrants(path, mediaType, id, forWrite))) {
throw e;
}
}
- isSuccess = true;
- return new FileOpenResult(0 /* status */, originalUid, mediaCapabilitiesUid,
- redact ? getRedactionRangesForFuse(path, ioPath, originalUid, uid, tid,
- forceRedaction) : new long[0]);
- } catch (IOException e) {
- // We are here because
- // * There is no db row corresponding to the requested path, which is more unlikely.
- // * getRedactionRangesForFuse couldn't fetch the redaction info correctly
- // In all of these cases, it means that app doesn't have access permission to the file.
- Log.e(TAG, "Couldn't find file: " + path, e);
- return new FileOpenResult(OsConstants.EACCES /* status */, originalUid,
- mediaCapabilitiesUid, new long[0]);
+ return 0;
+ } catch (FileNotFoundException e) {
+ // We are here because there is no db row corresponding to the requested path.
+ Log.e(TAG, "Couldn't find file: " + path);
+ return OsConstants.EACCES;
} catch (IllegalStateException | SecurityException e) {
Log.e(TAG, "Permission to access file: " + path + " is denied");
- return new FileOpenResult(OsConstants.EACCES /* status */, originalUid,
- mediaCapabilitiesUid, new long[0]);
+ return OsConstants.EACCES;
} finally {
- if (isSuccess && logTransformsMetrics) {
- notifyTranscodeHelperOnFileOpen(path, ioPath, originalUid, transformsReason);
- }
restoreLocalCallingIdentity(token);
}
}
- private @Nullable Uri getOtherUriGrantsForPath(String path, boolean forWrite) {
- final Uri contentUri = FileUtils.getContentUriForPath(path);
- final String[] projection = new String[]{
- MediaColumns._ID,
- FileColumns.MEDIA_TYPE};
- final String selection = MediaColumns.DATA + "=?";
- final String[] selectionArgs = new String[]{path};
- final String id;
- final int mediaType;
- try (final Cursor c = queryForSingleItemAsMediaProvider(contentUri, projection, selection,
- selectionArgs, null)) {
- id = c.getString(0);
- mediaType = c.getInt(1);
- return getOtherUriGrantsForPath(path, mediaType, id, forWrite);
- } catch (FileNotFoundException ignored) {
- }
- return null;
- }
-
- @Nullable
- private Uri getOtherUriGrantsForPath(String path, int mediaType, String id, boolean forWrite) {
- List<Uri> otherUris = new ArrayList<Uri>();
+ private boolean hasOtherUriGrants(String path, int mediaType, long id, boolean forWrite) {
+ Set<Uri> otherUris = new ArraySet<Uri>();
final Uri mediaUri = getMediaUriForFuse(extractVolumeName(path), mediaType, id);
otherUris.add(mediaUri);
final Uri externalMediaUri = getMediaUriForFuse(MediaStore.VOLUME_EXTERNAL, mediaType, id);
otherUris.add(externalMediaUri);
- return getPermissionGrantedUri(otherUris, forWrite);
+ return bulkCheckUriPermissions(otherUris, forWrite);
}
- @NonNull
- private Uri getMediaUriForFuse(@NonNull String volumeName, int mediaType, String id) {
- Uri uri = MediaStore.Files.getContentUri(volumeName);
+ private @NonNull Uri getMediaUriForFuse(@NonNull String volumeName, int mediaType, long id) {
switch (mediaType) {
case FileColumns.MEDIA_TYPE_IMAGE:
- uri = MediaStore.Images.Media.getContentUri(volumeName);
- break;
+ return MediaStore.Images.Media.getContentUri(volumeName, id);
case FileColumns.MEDIA_TYPE_VIDEO:
- uri = MediaStore.Video.Media.getContentUri(volumeName);
- break;
+ return MediaStore.Video.Media.getContentUri(volumeName, id);
case FileColumns.MEDIA_TYPE_AUDIO:
- uri = MediaStore.Audio.Media.getContentUri(volumeName);
- break;
+ return MediaStore.Audio.Media.getContentUri(volumeName, id);
case FileColumns.MEDIA_TYPE_PLAYLIST:
- uri = MediaStore.Audio.Playlists.getContentUri(volumeName);
- break;
+ return ContentUris.withAppendedId(
+ MediaStore.Audio.Playlists.getContentUri(volumeName), id);
+ default:
+ // return files URIs
+ return MediaStore.Files.getContentUri(volumeName, id);
}
+ }
- return uri.buildUpon().appendPath(id).build();
+ /**
+ * Feature flag to support File APIs for different formats of media-store URI grants like:
+ * * content://media/external_primary/images/media/123
+ * * content://media/external/images/media/123
+ *
+ * Default value: false
+ */
+ private boolean isFilePathSupportForMediaUris() {
+ return SystemProperties.getBoolean("sys.filepathsupport.mediauri", false);
}
/**
@@ -8491,7 +7284,6 @@
case DIRECTORY_ALARMS_LOWER_CASE:
case DIRECTORY_NOTIFICATIONS_LOWER_CASE:
case DIRECTORY_AUDIOBOOKS_LOWER_CASE:
- case DIRECTORY_RECORDINGS_LOWER_CASE:
uri = Audio.Media.getContentUri(volName);
break;
case DIRECTORY_MUSIC_LOWER_CASE:
@@ -8555,6 +7347,14 @@
}
}
+ private boolean isExternalMediaDirectory(@NonNull String path) {
+ final String relativePath = extractRelativePath(path);
+ if (relativePath != null) {
+ return relativePath.startsWith("Android/media");
+ }
+ return false;
+ }
+
private Uri insertFileForFuse(@NonNull String path, @NonNull Uri uri, @NonNull String mimeType,
boolean useData) {
ContentValues values = new ContentValues();
@@ -8574,7 +7374,7 @@
/**
* Enforces file creation restrictions (see return values) for the given file on behalf of the
- * app with the given {@code uid}. If the file is added to the shared storage, creates a
+ * app with the given {@code uid}. If the file is is added to the shared storage, creates a
* database entry for it.
* <p> Does NOT create file.
*
@@ -8598,7 +7398,6 @@
public int insertFileIfNecessaryForFuse(@NonNull String path, int uid) {
final LocalCallingIdentity token =
clearLocalCallingIdentity(getCachedCallingIdentityForFuse(uid));
- PulledMetrics.logFileAccessViaFuse(getCallingUidOrSelf(), path);
try {
if (isPrivatePackagePathNotAccessibleByCaller(path)) {
@@ -8724,8 +7523,6 @@
public int deleteFileForFuse(@NonNull String path, int uid) throws IOException {
final LocalCallingIdentity token =
clearLocalCallingIdentity(getCachedCallingIdentityForFuse(uid));
- PulledMetrics.logFileAccessViaFuse(getCallingUidOrSelf(), path);
-
try {
if (isPrivatePackagePathNotAccessibleByCaller(path)) {
Log.e(TAG, "Can't delete a file in another app's external directory!");
@@ -8788,7 +7585,6 @@
@NonNull String path, int uid, boolean forCreate) {
final LocalCallingIdentity token =
clearLocalCallingIdentity(getCachedCallingIdentityForFuse(uid));
- PulledMetrics.logFileAccessViaFuse(getCallingUidOrSelf(), path);
try {
// App dirs are not indexed, so we don't create an entry for the file.
@@ -8842,7 +7638,6 @@
public int isOpendirAllowedForFuse(@NonNull String path, int uid, boolean forWrite) {
final LocalCallingIdentity token =
clearLocalCallingIdentity(getCachedCallingIdentityForFuse(uid));
- PulledMetrics.logFileAccessViaFuse(getCallingUidOrSelf(), path);
try {
if ("/storage/emulated".equals(path)) {
return OsConstants.EPERM;
@@ -8944,30 +7739,6 @@
return mCallingIdentity.get().hasPermission(APPOP_REQUEST_INSTALL_PACKAGES_FOR_SHARED_UID);
}
- private String getExternalStorageProviderAuthority() {
- if (SdkLevel.isAtLeastS()) {
- return getExternalStorageProviderAuthorityFromDocumentsContract();
- }
- return MediaStore.EXTERNAL_STORAGE_PROVIDER_AUTHORITY;
- }
-
- @RequiresApi(Build.VERSION_CODES.S)
- private String getExternalStorageProviderAuthorityFromDocumentsContract() {
- return DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY;
- }
-
- private String getDownloadsProviderAuthority() {
- if (SdkLevel.isAtLeastS()) {
- return getDownloadsProviderAuthorityFromDocumentsContract();
- }
- return DOWNLOADS_PROVIDER_AUTHORITY;
- }
-
- @RequiresApi(Build.VERSION_CODES.S)
- private String getDownloadsProviderAuthorityFromDocumentsContract() {
- return DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY;
- }
-
private boolean isCallingIdentityDownloadProvider(int uid) {
return uid == mDownloadsAuthorityAppId;
}
@@ -8984,9 +7755,9 @@
* The following apps have access to all private-app directories on secondary volumes:
* * ExternalStorageProvider
* * DownloadProvider
- * * Signature apps with ACCESS_MTP permission granted
- * (Note: For Android R we also allow privileged apps with ACCESS_MTP to access all
- * private-app directories, this additional access is removed for Android S+).
+ * * Signature/privileged apps with ACCESS_MTP permission granted
+ * (TODO(b/175796984): Allow *only* signature apps with ACCESS_MTP to access all
+ * private-app directories).
*
* Installer apps can only access private-app directories on Android/obb.
*
@@ -8997,43 +7768,16 @@
final LocalCallingIdentity token =
clearLocalCallingIdentity(getCachedCallingIdentityForFuse(uid));
try {
- if (SdkLevel.isAtLeastS()) {
- return isMountModeAllowedPrivatePathAccess(uid, getCallingPackage(), path);
- } else {
- if (isCallingIdentityDownloadProvider(uid) ||
- isCallingIdentityExternalStorageProvider(uid) || isCallingIdentityMtp(
- uid)) {
- return true;
- }
- return (isObbOrChildPath(path) && isCallingIdentityAllowedInstallerAccess(uid));
+ if (isCallingIdentityDownloadProvider(uid) ||
+ isCallingIdentityExternalStorageProvider(uid) || isCallingIdentityMtp(uid)) {
+ return true;
}
+ return (isObbOrChildPath(path) && isCallingIdentityAllowedInstallerAccess(uid));
} finally {
restoreLocalCallingIdentity(token);
}
}
- @RequiresApi(Build.VERSION_CODES.S)
- private boolean isMountModeAllowedPrivatePathAccess(int uid, String packageName, String path) {
- // This is required as only MediaProvider (package with WRITE_MEDIA_STORAGE) can access
- // mount modes.
- final CallingIdentity token = clearCallingIdentity();
- try {
- final int mountMode = mStorageManager.getExternalStorageMountMode(uid, packageName);
- switch (mountMode) {
- case StorageManager.MOUNT_MODE_EXTERNAL_ANDROID_WRITABLE:
- case StorageManager.MOUNT_MODE_EXTERNAL_PASS_THROUGH:
- return true;
- case StorageManager.MOUNT_MODE_EXTERNAL_INSTALLER:
- return isObbOrChildPath(path);
- }
- } catch (Exception e) {
- Log.w(TAG, "Caller does not have the permissions to access mount modes: ", e);
- } finally {
- restoreCallingIdentity(token);
- }
- return false;
- }
-
private boolean checkCallingPermissionGlobal(Uri uri, boolean forWrite) {
// System internals can work with all media
if (isCallingPackageSelf() || isCallingPackageShell()) {
@@ -9084,24 +7828,20 @@
return isUriPermissionGranted(uri, forWrite);
}
- /**
- * Returns any uri that is granted from the set of Uris passed.
- */
- private @Nullable Uri getPermissionGrantedUri(@NonNull List<Uri> uris, boolean forWrite) {
+ private boolean bulkCheckUriPermissions(Set<Uri> uris, boolean forWrite) {
for (Uri uri : uris) {
if (isUriPermissionGranted(uri, forWrite)) {
- return uri;
+ return true;
}
}
- return null;
+ return false;
}
private boolean isUriPermissionGranted(Uri uri, boolean forWrite) {
- final int modeFlags = forWrite
- ? Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- : Intent.FLAG_GRANT_READ_URI_PERMISSION;
int uriPermission = getContext().checkUriPermission(uri, mCallingIdentity.get().pid,
- mCallingIdentity.get().uid, modeFlags);
+ mCallingIdentity.get().uid, forWrite
+ ? Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ : Intent.FLAG_GRANT_READ_URI_PERMISSION);
return uriPermission == PERMISSION_GRANTED;
}
@@ -9110,72 +7850,6 @@
return FuseDaemon.native_is_fuse_thread();
}
- @VisibleForTesting
- public boolean getBooleanDeviceConfig(String key, boolean defaultValue) {
- if (!canReadDeviceConfig(key, defaultValue)) {
- return defaultValue;
- }
-
- final long token = Binder.clearCallingIdentity();
- try {
- return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, key,
- defaultValue);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @VisibleForTesting
- public int getIntDeviceConfig(String key, int defaultValue) {
- if (!canReadDeviceConfig(key, defaultValue)) {
- return defaultValue;
- }
-
- final long token = Binder.clearCallingIdentity();
- try {
- return DeviceConfig.getInt(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, key,
- defaultValue);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @VisibleForTesting
- public String getStringDeviceConfig(String key, String defaultValue) {
- if (!canReadDeviceConfig(key, defaultValue)) {
- return defaultValue;
- }
-
- final long token = Binder.clearCallingIdentity();
- try {
- return DeviceConfig.getString(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, key,
- defaultValue);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- private static <T> boolean canReadDeviceConfig(String key, T defaultValue) {
- if (SdkLevel.isAtLeastS()) {
- return true;
- }
-
- Log.w(TAG, "Cannot read device config before Android S. Returning defaultValue: "
- + defaultValue + " for key: " + key);
- return false;
- }
-
- @VisibleForTesting
- public void addOnPropertiesChangedListener(OnPropertiesChangedListener listener) {
- if (!SdkLevel.isAtLeastS()) {
- Log.w(TAG, "Cannot add device config changed listener before Android S");
- return;
- }
-
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
- BackgroundThread.getExecutor(), listener);
- }
-
@Deprecated
private boolean checkCallingPermissionAudio(boolean forWrite, String callingPackage) {
if (forWrite) {
@@ -9224,12 +7898,6 @@
}
}
- private void enforceCallingPermission(@NonNull Collection<Uri> uris, boolean forWrite) {
- for (Uri uri : uris) {
- enforceCallingPermission(uri, Bundle.EMPTY, forWrite);
- }
- }
-
private void enforceCallingPermissionInternal(@NonNull Uri uri, @NonNull Bundle extras,
boolean forWrite) {
Objects.requireNonNull(uri);
@@ -9242,16 +7910,6 @@
return;
}
- // For redacted URI proceed with its corresponding URI as query builder doesn't support
- // redacted URIs for fetching a database row
- // NOTE: The grants (if any) must have been on redacted URI hence global check requires
- // redacted URI
- Uri redactedUri = null;
- if (isRedactedUri(uri)) {
- redactedUri = uri;
- uri = getUriForRedactedUri(uri);
- }
-
final DatabaseHelper helper;
try {
helper = getDatabaseForUri(uri);
@@ -9317,7 +7975,6 @@
}
}
- if (redactedUri != null) uri = redactedUri;
throw new SecurityException(getCallingPackageOrSelf() + " has no access to " + uri);
}
@@ -9341,7 +7998,7 @@
// First, does caller have the needed row-level access?
enforceCallingPermission(uri, extras, isWrite);
- // Second, does the path look consistent?
+ // Second, does the path look sane?
if (!FileUtils.contains(Environment.getStorageDirectory(), file)) {
checkWorldReadAccess(file.getAbsolutePath());
}
@@ -9478,34 +8135,10 @@
}
}
- public List<String> getSupportedTranscodingRelativePaths() {
- return mTranscodeHelper.getSupportedRelativePaths();
- }
-
- /**
- * Creating a new method for Transcoding to avoid any merge conflicts.
- * TODO(b/170465810): Remove this when the code is refactored.
- */
- @NonNull DatabaseHelper getDatabaseForUriForTranscoding(Uri uri)
- throws VolumeNotFoundException {
- return getDatabaseForUri(uri);
- }
-
private @NonNull DatabaseHelper getDatabaseForUri(Uri uri) throws VolumeNotFoundException {
final String volumeName = resolveVolumeName(uri);
- synchronized (mAttachedVolumes) {
- boolean volumeAttached = false;
- UserHandle user = mCallingIdentity.get().getUser();
- for (MediaVolume vol : mAttachedVolumes) {
- if (vol.getName().equals(volumeName) && vol.isVisibleToUser(user)) {
- volumeAttached = true;
- break;
- }
- }
- if (!volumeAttached) {
- // Dump some more debug info
- Log.e(TAG, "Volume " + volumeName + " not found, calling identity: "
- + user + ", attached volumes: " + mAttachedVolumes);
+ synchronized (mAttachedVolumeNames) {
+ if (!mAttachedVolumeNames.contains(volumeName)) {
throw new VolumeNotFoundException(volumeName);
}
}
@@ -9529,47 +8162,53 @@
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();
}
- public Uri attachVolume(MediaVolume volume, boolean validate) {
+ public Uri attachVolume(String volume, boolean validate) {
if (mCallingIdentity.get().pid != android.os.Process.myPid()) {
throw new SecurityException(
"Opening and closing databases not allowed.");
}
- final String volumeName = volume.getName();
+ // Quick sanity check for shady volume names
+ MediaStore.checkArgumentVolumeName(volume);
- // Quick check for shady volume names
- MediaStore.checkArgumentVolumeName(volumeName);
-
- // Quick check that volume actually exists
- if (!MediaStore.VOLUME_INTERNAL.equals(volumeName) && validate) {
+ // Quick sanity check that volume actually exists
+ if (!MediaStore.VOLUME_INTERNAL.equals(volume) && validate) {
try {
- getVolumePath(volumeName);
+ getVolumePath(volume);
} catch (IOException e) {
throw new IllegalArgumentException(
"Volume " + volume + " currently unavailable", e);
}
}
- synchronized (mAttachedVolumes) {
- mAttachedVolumes.add(volume);
+ synchronized (mAttachedVolumeNames) {
+ mAttachedVolumeNames.add(volume);
}
final ContentResolver resolver = getContext().getContentResolver();
- final Uri uri = getBaseContentUri(volumeName);
- // TODO(b/182396009) we probably also want to notify clone profile (and vice versa)
- resolver.notifyChange(getBaseContentUri(volumeName), null);
+ final Uri uri = getBaseContentUri(volume);
+ resolver.notifyChange(getBaseContentUri(volume), null);
if (LOGV) Log.v(TAG, "Attached volume: " + volume);
- if (!MediaStore.VOLUME_INTERNAL.equals(volumeName)) {
+ if (!MediaStore.VOLUME_INTERNAL.equals(volume)) {
// Also notify on synthetic view of all devices
resolver.notifyChange(getBaseContentUri(MediaStore.VOLUME_EXTERNAL), null);
ForegroundThread.getExecutor().execute(() -> {
- mExternalDatabase.runWithTransaction((db) -> {
+ final DatabaseHelper helper = MediaStore.VOLUME_INTERNAL.equals(volume)
+ ? mInternalDatabase : mExternalDatabase;
+ helper.runWithTransaction((db) -> {
ensureDefaultFolders(volume, db);
ensureThumbnailsValid(volume, db);
return null;
@@ -9578,39 +8217,26 @@
// 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());
+ MediaDocumentsProvider.onMediaStoreReady(getContext(), volume);
});
}
return uri;
}
private void detachVolume(Uri uri) {
- final String volumeName = MediaStore.getVolumeName(uri);
- try {
- detachVolume(getVolume(volumeName));
- } catch (FileNotFoundException e) {
- Log.e(TAG, "Couldn't find volume for URI " + uri, e) ;
- }
+ detachVolume(MediaStore.getVolumeName(uri));
}
- public boolean isVolumeAttached(MediaVolume volume) {
- synchronized (mAttachedVolumes) {
- return mAttachedVolumes.contains(volume);
- }
- }
-
- public void detachVolume(MediaVolume volume) {
+ public void detachVolume(String volume) {
if (mCallingIdentity.get().pid != android.os.Process.myPid()) {
throw new SecurityException(
"Opening and closing databases not allowed.");
}
- final String volumeName = volume.getName();
+ // Quick sanity check for shady volume names
+ MediaStore.checkArgumentVolumeName(volume);
- // Quick check for shady volume names
- MediaStore.checkArgumentVolumeName(volumeName);
-
- if (MediaStore.VOLUME_INTERNAL.equals(volumeName)) {
+ if (MediaStore.VOLUME_INTERNAL.equals(volume)) {
throw new UnsupportedOperationException(
"Deleting the internal volume is not allowed");
}
@@ -9618,24 +8244,24 @@
// Signal any scanning to shut down
mMediaScanner.onDetachVolume(volume);
- synchronized (mAttachedVolumes) {
- mAttachedVolumes.remove(volume);
+ synchronized (mAttachedVolumeNames) {
+ mAttachedVolumeNames.remove(volume);
}
final ContentResolver resolver = getContext().getContentResolver();
- final Uri uri = getBaseContentUri(volumeName);
- resolver.notifyChange(getBaseContentUri(volumeName), null);
+ final Uri uri = getBaseContentUri(volume);
+ resolver.notifyChange(getBaseContentUri(volume), null);
- if (!MediaStore.VOLUME_INTERNAL.equals(volumeName)) {
+ if (!MediaStore.VOLUME_INTERNAL.equals(volume)) {
// Also notify on synthetic view of all devices
resolver.notifyChange(getBaseContentUri(MediaStore.VOLUME_EXTERNAL), null);
}
- if (LOGV) Log.v(TAG, "Detached volume: " + volumeName);
+ if (LOGV) Log.v(TAG, "Detached volume: " + volume);
}
- @GuardedBy("mAttachedVolumes")
- private final ArraySet<MediaVolume> mAttachedVolumes = new ArraySet<>();
+ @GuardedBy("mAttachedVolumeNames")
+ private final ArraySet<String> mAttachedVolumeNames = new ArraySet<>();
@GuardedBy("mCustomCollators")
private final ArraySet<String> mCustomCollators = new ArraySet<>();
@@ -9643,10 +8269,6 @@
private DatabaseHelper mInternalDatabase;
private DatabaseHelper mExternalDatabase;
- private PickerDbFacade mPickerDbFacade;
- private ExternalDbFacade mExternalDbFacade;
- private PickerSyncController mPickerSyncController;
- private TranscodeHelper mTranscodeHelper;
// name of the volume currently being scanned by the media scanner (or null)
private String mMediaScannerVolume;
@@ -9704,14 +8326,6 @@
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 = 902;
- static final int PICKER_UNRELIABLE_VOLUME = 903;
-
- 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));
-
private LocalUriMatcher mUriMatcher;
private static final String[] PATH_PROJECTION = new String[] {
@@ -9748,19 +8362,6 @@
}
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);
- // content://media/picker/<user-id>/<media-id>
- mPublic.addURI(auth, "picker/#/#", 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);
@@ -9805,7 +8406,6 @@
// NOTE: technically hidden, since Uri is never exposed
mPublic.addURI(auth, "*/version", VERSION);
- mHidden.addURI(auth, "picker_internal", PICKER_INTERNAL);
mHidden.addURI(auth, "*", VOLUMES_ID);
mHidden.addURI(auth, null, VOLUMES);
@@ -9934,22 +8534,10 @@
return builder.build();
}
- public ExternalDbFacade getExternalDbFacade() {
- return mExternalDbFacade;
- }
-
- public PickerSyncController getPickerSyncController() {
- return mPickerSyncController;
- }
-
private boolean isCallingPackageSystemGallery() {
return mCallingIdentity.get().hasPermission(PERMISSION_IS_SYSTEM_GALLERY);
}
- private int getCallingUidOrSelf() {
- return mCallingIdentity.get().uid;
- }
-
@Deprecated
private String getCallingPackageOrSelf() {
return mCallingIdentity.get().getPackageName();
@@ -9999,20 +8587,11 @@
@Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
writer.println("mThumbSize=" + mThumbSize);
- synchronized (mAttachedVolumes) {
- writer.println("mAttachedVolumes=" + mAttachedVolumes);
+ synchronized (mAttachedVolumeNames) {
+ writer.println("mAttachedVolumeNames=" + mAttachedVolumeNames);
}
writer.println();
- mVolumeCache.dump(writer);
- writer.println();
-
- mUserCache.dump(writer);
- writer.println();
-
- mTranscodeHelper.dump(writer);
- writer.println();
-
Logging.dumpPersistent(writer);
}
}
diff --git a/src/com/android/providers/media/MediaService.java b/src/com/android/providers/media/MediaService.java
index 4178aa4..d7c6bab 100644
--- a/src/com/android/providers/media/MediaService.java
+++ b/src/com/android/providers/media/MediaService.java
@@ -27,10 +27,7 @@
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;
import android.provider.MediaStore;
import android.util.Log;
@@ -44,21 +41,6 @@
public class MediaService extends JobIntentService {
private static final int JOB_ID = -300;
- private static final String ACTION_SCAN_VOLUME
- = "com.android.providers.media.action.SCAN_VOLUME";
-
- private static final String EXTRA_MEDIAVOLUME = "MediaVolume";
-
- private static final String EXTRA_SCAN_REASON = "scan_reason";
-
-
- public static void queueVolumeScan(Context context, MediaVolume volume, int reason) {
- Intent intent = new Intent(ACTION_SCAN_VOLUME);
- intent.putExtra(EXTRA_MEDIAVOLUME, volume) ;
- intent.putExtra(EXTRA_SCAN_REASON, reason);
- enqueueWork(context, intent);
- }
-
public static void enqueueWork(Context context, Intent work) {
enqueueWork(context, MediaService.class, JOB_ID, work);
}
@@ -86,13 +68,7 @@
break;
}
case Intent.ACTION_MEDIA_MOUNTED: {
- onMediaMountedBroadcast(this, intent);
- break;
- }
- case ACTION_SCAN_VOLUME: {
- final MediaVolume volume = intent.getParcelableExtra(EXTRA_MEDIAVOLUME);
- int reason = intent.getIntExtra(EXTRA_SCAN_REASON, REASON_DEMAND);
- onScanVolume(this, volume, reason);
+ onScanVolume(this, intent.getData(), REASON_MOUNTED);
break;
}
default: {
@@ -124,42 +100,21 @@
}
}
- private static void onMediaMountedBroadcast(Context context, Intent intent)
+ private static void onScanVolume(Context context, Uri uri, int reason)
throws IOException {
- final StorageVolume volume = intent.getParcelableExtra(StorageVolume.EXTRA_STORAGE_VOLUME);
- if (volume != null) {
- MediaVolume mediaVolume = MediaVolume.fromStorageVolume(volume);
- try (ContentProviderClient cpc = context.getContentResolver()
- .acquireContentProviderClient(MediaStore.AUTHORITY)) {
- if (!((MediaProvider)cpc.getLocalContentProvider()).isVolumeAttached(mediaVolume)) {
- // This can happen on some legacy app clone implementations, where the
- // framework is modified to send MEDIA_MOUNTED broadcasts for clone volumes
- // to u0 MediaProvider; these volumes are not reported through the usual
- // volume attach events, so we need to scan them here if they weren't
- // attached previously
- onScanVolume(context, mediaVolume, REASON_MOUNTED);
- } else {
- Log.i(TAG, "Volume " + mediaVolume + " already attached");
- }
- }
- } else {
- Log.e(TAG, "Couldn't retrieve StorageVolume from intent");
- }
+ final File file = new File(uri.getPath()).getCanonicalFile();
+ final String volumeName = FileUtils.getVolumeName(context, file);
+
+ onScanVolume(context, volumeName, reason);
}
- public static void onScanVolume(Context context, MediaVolume volume, int reason)
+ public static void onScanVolume(Context context, String volumeName, int reason)
throws IOException {
- final String volumeName = volume.getName();
- UserHandle owner = volume.getUser();
- if (owner == null) {
- // Can happen for the internal volume
- owner = context.getUser();
- }
// If we're about to scan any external storage, scan internal first
// to ensure that we have ringtones ready to roll before a possibly very
// long external storage scan
if (!MediaStore.VOLUME_INTERNAL.equals(volumeName)) {
- onScanVolume(context, MediaVolume.fromInternal(), reason);
+ onScanVolume(context, MediaStore.VOLUME_INTERNAL, reason);
RingtoneManager.ensureDefaultRingtones(context);
}
@@ -168,7 +123,7 @@
// in the situation where a volume is ejected mid-scan
final Uri broadcastUri;
if (!MediaStore.VOLUME_INTERNAL.equals(volumeName)) {
- broadcastUri = Uri.fromFile(volume.getPath());
+ broadcastUri = Uri.fromFile(FileUtils.getVolumePath(context, volumeName));
} else {
broadcastUri = null;
}
@@ -176,7 +131,7 @@
try (ContentProviderClient cpc = context.getContentResolver()
.acquireContentProviderClient(MediaStore.AUTHORITY)) {
final MediaProvider provider = ((MediaProvider) cpc.getLocalContentProvider());
- provider.attachVolume(volume, /* validate */ true);
+ provider.attachVolume(volumeName, /* validate */ true);
final ContentResolver resolver = ContentResolver.wrap(cpc.getLocalContentProvider());
@@ -185,24 +140,20 @@
Uri scanUri = resolver.insert(MediaStore.getMediaScannerUri(), values);
if (broadcastUri != null) {
- context.sendBroadcastAsUser(
- new Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, broadcastUri), owner);
+ context.sendBroadcast(
+ new Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, broadcastUri));
}
- if (MediaStore.VOLUME_INTERNAL.equals(volumeName)) {
- for (File dir : FileUtils.getVolumeScanPaths(context, volumeName)) {
- provider.scanDirectory(dir, reason);
- }
- } else {
- provider.scanDirectory(volume.getPath(), reason);
+ for (File dir : FileUtils.getVolumeScanPaths(context, volumeName)) {
+ provider.scanDirectory(dir, reason);
}
resolver.delete(scanUri, null, null);
} finally {
if (broadcastUri != null) {
- context.sendBroadcastAsUser(
- new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, broadcastUri), owner);
+ context.sendBroadcast(
+ new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, broadcastUri));
}
}
}
diff --git a/src/com/android/providers/media/MediaUpgradeReceiver.java b/src/com/android/providers/media/MediaUpgradeReceiver.java
index ea72330..abb326a 100644
--- a/src/com/android/providers/media/MediaUpgradeReceiver.java
+++ b/src/com/android/providers/media/MediaUpgradeReceiver.java
@@ -73,8 +73,9 @@
long startTime = System.currentTimeMillis();
Log.i(TAG, "---> Start upgrade of media database " + file);
try {
- DatabaseHelper helper = new DatabaseHelper(context, file, false, false,
- Column.class, Metrics::logSchemaChange, null,
+ DatabaseHelper helper = new DatabaseHelper(
+ context, file, MediaProvider.isInternalMediaDatabaseName(file),
+ false, false, Column.class, Metrics::logSchemaChange, null,
MediaProvider.MIGRATION_LISTENER, null);
helper.runWithTransaction((db) -> {
// Perform just enough to force database upgrade
diff --git a/src/com/android/providers/media/MediaVolume.java b/src/com/android/providers/media/MediaVolume.java
deleted file mode 100644
index bceeaeb..0000000
--- a/src/com/android/providers/media/MediaVolume.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.UserHandle;
-import android.os.storage.StorageVolume;
-import android.provider.MediaStore;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import java.io.File;
-import java.util.Objects;
-
-/**
- * MediaVolume is a MediaProvider-internal representation of a storage volume.
- *
- * Before MediaVolume, volumes inside MediaProvider were represented by their name;
- * but now that MediaProvider handles volumes on behalf on multiple users, the name of a volume
- * might no longer be unique. So MediaVolume holds both a name and a user. The user may be
- * null on volumes without an owner (eg public volumes).
- *
- * In addition to that, we keep the path and ID of the volume cached in here as well
- * for easy access.
- */
-public final class MediaVolume implements Parcelable {
- /**
- * Name of the volume.
- */
- private final @NonNull String mName;
-
- /**
- * User to which the volume belongs to; might be null in case of public volumes.
- */
- private final @Nullable UserHandle mUser;
-
- /**
- * Path on which the volume is mounted.
- */
- private final @Nullable File mPath;
-
- /**
- * Unique ID of the volume; eg "external;0"
- */
- private final @Nullable String mId;
-
- public @NonNull String getName() {
- return mName;
- }
-
- public @Nullable UserHandle getUser() {
- return mUser;
- }
-
- public @Nullable File getPath() {
- return mPath;
- }
-
- public @Nullable String getId() {
- return mId;
- }
-
- private MediaVolume (@NonNull String name, UserHandle user, File path, String id) {
- this.mName = name;
- this.mUser = user;
- this.mPath = path;
- this.mId = id;
- }
-
- private MediaVolume (Parcel in) {
- this.mName = in.readString();
- this.mUser = in.readParcelable(null);
- this.mPath = new File(in.readString());
- this.mId = in.readString();
- }
-
- @Override
- public boolean equals(Object obj) {
- 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(mId, that.mId);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mName, mUser, mId);
- }
-
- public boolean isVisibleToUser(UserHandle user) {
- return mUser == null || user.equals(mUser);
- }
-
- @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);
- }
-
- public static MediaVolume fromInternal() {
- String name = MediaStore.VOLUME_INTERNAL;
-
- return new MediaVolume(name, null, null, null);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mName);
- dest.writeParcelable(mUser, flags);
- dest.writeString(mPath.toString());
- dest.writeString(mId);
- }
-
- @Override
- public String toString() {
- return "MediaVolume name: [" + mName + "] id: [" + mId + "] user: [" + mUser + "] path: ["
- + mPath + "]";
- }
-
- public static final @android.annotation.NonNull Creator<MediaVolume> CREATOR
- = new Creator<MediaVolume>() {
- @Override
- public MediaVolume createFromParcel(Parcel in) {
- return new MediaVolume(in);
- }
-
- @Override
- public MediaVolume[] newArray(int size) {
- return new MediaVolume[size];
- }
- };
-}
diff --git a/src/com/android/providers/media/PermissionActivity.java b/src/com/android/providers/media/PermissionActivity.java
index ee37448..c088aec 100644
--- a/src/com/android/providers/media/PermissionActivity.java
+++ b/src/com/android/providers/media/PermissionActivity.java
@@ -22,14 +22,10 @@
import static com.android.providers.media.MediaProvider.collectUris;
import static com.android.providers.media.util.DatabaseUtils.getAsBoolean;
import static com.android.providers.media.util.Logging.TAG;
-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.checkPermissionReadStorage;
import android.app.Activity;
import android.app.AlertDialog;
-import android.app.Dialog;
+import android.app.ProgressDialog;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -54,6 +50,7 @@
import android.provider.MediaStore;
import android.provider.MediaStore.MediaColumns;
import android.text.TextUtils;
+import android.text.format.DateUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Size;
@@ -63,12 +60,10 @@
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.widget.ImageView;
-import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
import com.android.providers.media.MediaProvider.LocalUriMatcher;
import com.android.providers.media.util.Metrics;
@@ -108,28 +103,16 @@
private AlertDialog actionDialog;
private AsyncTask<Void, Void, Void> positiveActionTask;
- private Dialog progressDialog;
+ private ProgressDialog progressDialog;
private TextView titleView;
- private Handler mHandler;
- private Runnable mShowProgressDialogRunnable = () -> {
- // We will show the progress dialog, add the dim effect back.
- getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
- progressDialog.show();
- };
private static final Long LEAST_SHOW_PROGRESS_TIME_MS = 300L;
- private static final Long BEFORE_SHOW_PROGRESS_TIME_MS = 300L;
- @VisibleForTesting
- static final String VERB_WRITE = "write";
- @VisibleForTesting
- static final String VERB_TRASH = "trash";
- @VisibleForTesting
- static final String VERB_FAVORITE = "favorite";
- @VisibleForTesting
- static final String VERB_UNFAVORITE = "unfavorite";
-
+ private static final String VERB_WRITE = "write";
+ private static final String VERB_TRASH = "trash";
private static final String VERB_UNTRASH = "untrash";
+ private static final String VERB_FAVORITE = "favorite";
+ private static final String VERB_UNFAVORITE = "unfavorite";
private static final String VERB_DELETE = "delete";
private static final String DATA_AUDIO = "audio";
@@ -153,12 +136,6 @@
getWindow().addSystemFlags(
WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
setFinishOnTouchOutside(false);
- // remove the dim effect
- // We may not show the progress dialog, if we don't remove the dim effect,
- // it may have flicker.
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
- getWindow().setDimAmount(0.0f);
-
// All untrusted input values here were validated when generating the
// original PendingIntent
@@ -177,21 +154,31 @@
return;
}
- mHandler = new Handler(getMainLooper());
- // Create Progress dialog
- createProgressDialog();
+ progressDialog = new ProgressDialog(this);
+ progressDialog.setCancelable(false);
- if (!shouldShowActionDialog(this, -1 /* pid */, appInfo.uid, getCallingPackage(),
- null /* attributionTag */, verb)) {
- onPositiveAction(null, 0);
- return;
+ // 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
+ switch (verb) {
+ case VERB_FAVORITE:
+ case VERB_UNFAVORITE: {
+ onPositiveAction(null, 0);
+ return;
+ }
}
-
// Kick off async loading of description to show in dialog
final View bodyView = getLayoutInflater().inflate(R.layout.permission_body, null, false);
handleImageViewVisibility(bodyView, uris);
new DescriptionTask(bodyView).execute(uris);
+ final CharSequence message = resolveMessageText();
+ if (!TextUtils.isEmpty(message)) {
+ final TextView messageView = bodyView.requireViewById(R.id.message);
+ messageView.setVisibility(View.VISIBLE);
+ messageView.setText(message);
+ }
+
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
// We set the title in message so that the text doesn't get truncated
builder.setMessage(resolveTitleText());
@@ -224,23 +211,9 @@
});
}
- private void createProgressDialog() {
- final ProgressBar progressBar = new ProgressBar(this);
- final int padding = getResources().getDimensionPixelOffset(R.dimen.dialog_space);
-
- progressBar.setIndeterminate(true);
- progressBar.setPadding(0, padding / 2, 0, padding);
- progressDialog = new AlertDialog.Builder(this)
- .setTitle(resolveProgressMessageText())
- .setView(progressBar)
- .setCancelable(false)
- .create();
- }
-
@Override
public void onDestroy() {
super.onDestroy();
- 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:
@@ -266,10 +239,8 @@
((AlertDialog) dialog).getButton(AlertDialog.BUTTON_NEGATIVE).setEnabled(false);
}
+ progressDialog.show();
final long startTime = System.currentTimeMillis();
-
- mHandler.postDelayed(mShowProgressDialogRunnable, BEFORE_SHOW_PROGRESS_TIME_MS);
-
positiveActionTask = new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
@@ -320,23 +291,17 @@
@Override
protected void onPostExecute(Void result) {
setResult(Activity.RESULT_OK);
- mHandler.removeCallbacks(mShowProgressDialogRunnable);
-
- if (!progressDialog.isShowing()) {
+ // Don't dismiss the progress dialog too quick, it will cause bad UX.
+ final long duration = System.currentTimeMillis() - startTime;
+ if (duration > LEAST_SHOW_PROGRESS_TIME_MS) {
+ progressDialog.dismiss();
finish();
} else {
- // Don't dismiss the progress dialog too quick, it will cause bad UX.
- final long duration =
- System.currentTimeMillis() - startTime - BEFORE_SHOW_PROGRESS_TIME_MS;
- if (duration > LEAST_SHOW_PROGRESS_TIME_MS) {
+ Handler handler = new Handler(getMainLooper());
+ handler.postDelayed(() -> {
progressDialog.dismiss();
finish();
- } else {
- mHandler.postDelayed(() -> {
- progressDialog.dismiss();
- finish();
- }, LEAST_SHOW_PROGRESS_TIME_MS - duration);
- }
+ }, LEAST_SHOW_PROGRESS_TIME_MS - duration);
}
}
}.execute();
@@ -372,37 +337,6 @@
return keyCode == KeyEvent.KEYCODE_BACK;
}
- @VisibleForTesting
- static boolean shouldShowActionDialog(@NonNull Context context, int pid, int uid,
- @NonNull String packageName, @Nullable String attributionTag, @NonNull String verb) {
- // 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
- if (TextUtils.equals(VERB_FAVORITE, verb) || TextUtils.equals(VERB_UNFAVORITE, verb)) {
- 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;
- }
- // check MANAGE_MEDIA permission
- if (!checkPermissionManageMedia(context, pid, uid, packageName, attributionTag)) {
- Log.d(TAG, "No permission MANAGE_MEDIA");
- return true;
- }
-
- // if verb is write, check ACCESS_MEDIA_LOCATION permission
- if (TextUtils.equals(verb, VERB_WRITE) && !checkPermissionAccessMediaLocation(context, pid,
- uid, packageName, attributionTag)) {
- Log.d(TAG, "No permission ACCESS_MEDIA_LOCATION");
- return true;
- }
- return false;
- }
-
private void handleImageViewVisibility(View bodyView, List<Uri> uris) {
if (uris.isEmpty()) {
return;
@@ -517,24 +451,43 @@
}
/**
- * Resolve the progress message string to be displayed to the user. All
- * arguments have been bound and this string is ready to be displayed.
+ * Resolve the dialog message string to be displayed to the user, if any.
+ * All arguments have been bound and this string is ready to be displayed.
*/
- private @Nullable CharSequence resolveProgressMessageText() {
- final String resName = "permission_progress_" + verb + "_" + data;
+ private @Nullable CharSequence resolveMessageText() {
+ final String resName = "permission_" + verb + "_" + data + "_info";
final int resId = getResources().getIdentifier(resName, "plurals",
getResources().getResourcePackageName(R.string.app_label));
if (resId != 0) {
final int count = uris.size();
+ final long durationMillis = (values.getAsLong(MediaColumns.DATE_EXPIRES) * 1000)
+ - System.currentTimeMillis();
+ final long durationDays = (durationMillis + DateUtils.DAY_IN_MILLIS)
+ / DateUtils.DAY_IN_MILLIS;
final CharSequence text = getResources().getQuantityText(resId, count);
- return TextUtils.expandTemplate(text, String.valueOf(count));
+ return TextUtils.expandTemplate(text, label, String.valueOf(count),
+ String.valueOf(durationDays));
} else {
- // Only some actions have a progress message string; it's okay if
+ // Only some actions have a secondary message string; it's okay if
// there isn't one defined
return null;
}
}
+ private @NonNull CharSequence resolvePositiveText() {
+ final String resName = "permission_" + verb + "_grant";
+ final int resId = getResources().getIdentifier(resName, "string",
+ getResources().getResourcePackageName(R.string.app_label));
+ return getResources().getText(resId);
+ }
+
+ private @NonNull CharSequence resolveNegativeText() {
+ final String resName = "permission_" + verb + "_deny";
+ final int resId = getResources().getIdentifier(resName, "string",
+ getResources().getResourcePackageName(R.string.app_label));
+ return getResources().getText(resId);
+ }
+
/**
* Recursively walk the given view hierarchy looking for the first
* {@link View} which matches the given predicate.
diff --git a/src/com/android/providers/media/PickerUriResolver.java b/src/com/android/providers/media/PickerUriResolver.java
deleted file mode 100644
index e71bc67..0000000
--- a/src/com/android/providers/media/PickerUriResolver.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media;
-
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.AssetFileDescriptor;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Build;
-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 androidx.annotation.NonNull;
-import androidx.annotation.RequiresApi;
-
-import com.android.modules.utils.build.SdkLevel;
-import com.android.providers.media.photopicker.data.model.UserId;
-
-import java.io.FileNotFoundException;
-
-/**
- * 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 Context mContext;
-
- /** 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").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").build();
-
- PickerUriResolver(Context context) {
- mContext = context;
- }
-
- 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 = getContentResolverForUserId(uri);
- final long token = Binder.clearCallingIdentity();
- try {
- return resolver.openFile(getRedactedFileUriFromPickerUri(uri, resolver), "r", signal);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- public AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts,
- CancellationSignal signal, int callingPid, int callingUid)
- throws FileNotFoundException{
- checkUriPermission(uri, callingPid, callingUid);
-
- final ContentResolver resolver = getContentResolverForUserId(uri);
- final long token = Binder.clearCallingIdentity();
- try {
- return resolver.openTypedAssetFile(getRedactedFileUriFromPickerUri(uri, resolver),
- mimeTypeFilter, opts, signal);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- public Cursor query(Uri uri, String[] projection, Bundle queryArgs, CancellationSignal signal,
- int callingPid, int callingUid) {
- checkUriPermission(uri, callingPid, callingUid);
-
- return queryInternal(uri, projection, queryArgs, signal);
- }
-
- public String getType(@NonNull Uri uri) {
- try (Cursor cursor = queryInternal(uri, new String[]{MediaStore.MediaColumns.MIME_TYPE},
- /* queryArgs */ null, /* signal */ null)) {
- if (cursor != null && cursor.getCount() == 1 && cursor.moveToFirst()) {
- return cursor.getString(0);
- }
- }
- 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 getMediaInfoUri(String authority) {
- return Uri.parse("content://" + authority + "/"
- + CloudMediaProviderContract.URI_PATH_MEDIA_INFO);
- }
-
- private Cursor queryInternal(Uri uri, String[] projection, Bundle queryArgs,
- CancellationSignal signal) {
- final ContentResolver resolver = getContentResolverForUserId(uri);
- final long token = Binder.clearCallingIdentity();
- try {
- // Support query similar to as we support for redacted mediastore file uris.
- // TODO(b/191362529): Restrict projection values when we start querying picker db. Add
- // PickerColumns and add checks for projection.
- return resolver.query(getRedactedFileUriFromPickerUri(uri, resolver), projection,
- queryArgs, signal);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * @return {@link MediaStore.Files} Uri that always redacts sensitive data
- */
- private static Uri getRedactedFileUriFromPickerUri(Uri uri, ContentResolver contentResolver) {
- // content://media/picker/<user-id>/<media-id>
- final long id = Long.parseLong(uri.getPathSegments().get(2));
- final Uri res = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL, id);
- return getRedactedUri(contentResolver, res);
- }
-
- private static Uri getRedactedUri(ContentResolver contentResolver, Uri uri) {
- if (SdkLevel.isAtLeastS()) {
- return getRedactedUriFromMediaStoreAPI(contentResolver, uri);
- } else {
- // TODO (b/168783994): directly call redacted uri code logic or explore other solution.
- // Devices running on Android R cannot call getRedacted() as the API is added in
- // Android S.
- return uri;
- }
- }
-
- @RequiresApi(Build.VERSION_CODES.S)
- private static Uri getRedactedUriFromMediaStoreAPI(ContentResolver contentResolver, Uri uri) {
- return MediaStore.getRedactedUri(contentResolver, uri);
- }
-
- private static UserId getUserId(Uri uri) {
- // content://media/picker/<user-id>/<media-id>
- final int user = Integer.parseInt(uri.getPathSegments().get(1));
- return UserId.of(UserHandle.of(user));
- }
-
- private void checkUriPermission(Uri uri, int pid, int uid) {
- if (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 ContentResolver getContentResolverForUserId(Uri uri) {
- final UserId userId = getUserId(uri);
- return userId.getContentResolver(mContext);
- }
-}
diff --git a/src/com/android/providers/media/TranscodeHelper.java b/src/com/android/providers/media/TranscodeHelper.java
deleted file mode 100644
index 5e2b13b..0000000
--- a/src/com/android/providers/media/TranscodeHelper.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media;
-
-import android.net.Uri;
-import android.os.Bundle;
-import java.io.PrintWriter;
-import java.util.List;
-
-/** Interface over MediaTranscodeManager access */
-public interface TranscodeHelper {
- public void freeCache(long bytes);
-
- public void onAnrDelayStarted(String packageName, int uid, int tid, int reason);
-
- public boolean transcode(String src, String dst, int uid, int reason);
-
- public String getIoPath(String path, int uid);
-
- public int shouldTranscode(String path, int uid, Bundle bundle);
-
- public boolean supportsTranscode(String path);
-
- public void onUriPublished(Uri uri);
-
- public void onFileOpen(String path, String ioPath, int uid, int transformsReason);
-
- public boolean isTranscodeFileCached(String path, String transcodePath);
-
- 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
deleted file mode 100644
index a3e2731..0000000
--- a/src/com/android/providers/media/TranscodeHelperImpl.java
+++ /dev/null
@@ -1,1904 +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;
-
-import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
-import static android.provider.MediaStore.Files.FileColumns.TRANSCODE_COMPLETE;
-import static android.provider.MediaStore.Files.FileColumns.TRANSCODE_EMPTY;
-import static android.provider.MediaStore.MATCH_EXCLUDE;
-import static android.provider.MediaStore.QUERY_ARG_MATCH_PENDING;
-import static android.provider.MediaStore.QUERY_ARG_MATCH_TRASHED;
-
-import static com.android.providers.media.MediaProvider.VolumeNotFoundException;
-import static com.android.providers.media.MediaProviderStatsLog.TRANSCODING_DATA;
-import static com.android.providers.media.MediaProviderStatsLog.TRANSCODING_DATA__FAILURE_CAUSE__CAUSE_UNKNOWN;
-import static com.android.providers.media.MediaProviderStatsLog.TRANSCODING_DATA__FAILURE_CAUSE__TRANSCODING_CLIENT_TIMEOUT;
-import static com.android.providers.media.MediaProviderStatsLog.TRANSCODING_DATA__FAILURE_CAUSE__TRANSCODING_SERVICE_ERROR;
-import static com.android.providers.media.MediaProviderStatsLog.TRANSCODING_DATA__FAILURE_CAUSE__TRANSCODING_SESSION_CANCELED;
-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 android.annotation.IntRange;
-import android.annotation.LongDef;
-import android.app.ActivityManager;
-import android.app.ActivityManager.OnUidImportanceListener;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.compat.CompatChanges;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.Disabled;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.InstallSourceInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.Property;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
-import android.content.res.XmlResourceParser;
-import android.database.Cursor;
-import android.media.ApplicationMediaCapabilities;
-import android.media.MediaFeature;
-import android.media.MediaFormat;
-import android.media.MediaTranscodingManager;
-import android.media.MediaTranscodingManager.VideoTranscodingRequest;
-import android.media.MediaTranscodingManager.TranscodingRequest.VideoFormatResolver;
-import android.media.MediaTranscodingManager.TranscodingSession;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.ParcelFileDescriptor;
-import android.os.Process;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.os.storage.StorageManager;
-import android.os.storage.StorageVolume;
-import android.provider.MediaStore;
-import android.provider.MediaStore.Files.FileColumns;
-import android.provider.MediaStore.MediaColumns;
-import android.provider.MediaStore.Video.VideoColumns;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
-import android.util.Pair;
-import android.util.SparseArray;
-import android.widget.Toast;
-
-import androidx.annotation.GuardedBy;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
-import androidx.core.app.NotificationCompat;
-import androidx.core.app.NotificationManagerCompat;
-
-import com.android.internal.annotations.VisibleForTesting;
-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 java.io.BufferedReader;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-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.Arrays;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.Optional;
-import java.util.UUID;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-@RequiresApi(Build.VERSION_CODES.S)
-public class TranscodeHelperImpl implements TranscodeHelper {
- private static final String TAG = "TranscodeHelper";
- private static final boolean DEBUG = SystemProperties.getBoolean("persist.sys.fuse.log", false);
- private static final float MAX_APP_NAME_SIZE_PX = 500f;
-
- // Notice the pairing of the keys.When you change a DEVICE_CONFIG key, then please also change
- // the corresponding SYS_PROP key too; and vice-versa.
- // Keeping the whole strings separate for the ease of text search.
- private static final String TRANSCODE_ENABLED_SYS_PROP_KEY =
- "persist.sys.fuse.transcode_enabled";
- private static final String TRANSCODE_ENABLED_DEVICE_CONFIG_KEY = "transcode_enabled";
- private static final String TRANSCODE_DEFAULT_SYS_PROP_KEY =
- "persist.sys.fuse.transcode_default";
- private static final String TRANSCODE_DEFAULT_DEVICE_CONFIG_KEY = "transcode_default";
- private static final String TRANSCODE_USER_CONTROL_SYS_PROP_KEY =
- "persist.sys.fuse.transcode_user_control";
- private static final String TRANSCODE_COMPAT_MANIFEST_KEY = "transcode_compat_manifest";
- private static final String TRANSCODE_COMPAT_STALE_KEY = "transcode_compat_stale";
- private static final String TRANSCODE_MAX_DURATION_MS_KEY = "transcode_max_duration_ms";
-
- private static final int MY_UID = android.os.Process.myUid();
- private static final int MAX_TRANSCODE_DURATION_MS = (int) TimeUnit.MINUTES.toMillis(1);
-
- /**
- * Force enable an app to support the HEVC media capability
- *
- * Apps should declare their supported media capabilities in their manifest but this flag can be
- * used to force an app into supporting HEVC, hence avoiding transcoding while accessing media
- * encoded in HEVC.
- *
- * Setting this flag will override any OS level defaults for apps. It is disabled by default,
- * meaning that the OS defaults would take precedence.
- *
- * Setting this flag and {@code FORCE_DISABLE_HEVC_SUPPORT} is an undefined
- * state and will result in the OS ignoring both flags.
- */
- @ChangeId
- @Disabled
- private static final long FORCE_ENABLE_HEVC_SUPPORT = 174228127L;
-
- /**
- * Force disable an app from supporting the HEVC media capability
- *
- * Apps should declare their supported media capabilities in their manifest but this flag can be
- * used to force an app into not supporting HEVC, hence forcing transcoding while accessing
- * media encoded in HEVC.
- *
- * Setting this flag will override any OS level defaults for apps. It is disabled by default,
- * meaning that the OS defaults would take precedence.
- *
- * Setting this flag and {@code FORCE_ENABLE_HEVC_SUPPORT} is an undefined state
- * and will result in the OS ignoring both flags.
- */
- @ChangeId
- @Disabled
- private static final long FORCE_DISABLE_HEVC_SUPPORT = 174227820L;
-
- @VisibleForTesting
- static final int FLAG_HEVC = 1 << 0;
- @VisibleForTesting
- static final int FLAG_SLOW_MOTION = 1 << 1;
- private static final int FLAG_HDR_10 = 1 << 2;
- private static final int FLAG_HDR_10_PLUS = 1 << 3;
- private static final int FLAG_HDR_HLG = 1 << 4;
- private static final int FLAG_HDR_DOLBY_VISION = 1 << 5;
- private static final int MEDIA_FORMAT_FLAG_MASK = FLAG_HEVC | FLAG_SLOW_MOTION
- | FLAG_HDR_10 | FLAG_HDR_10_PLUS | FLAG_HDR_HLG | FLAG_HDR_DOLBY_VISION;
-
- @LongDef({
- FLAG_HEVC,
- FLAG_SLOW_MOTION,
- FLAG_HDR_10,
- FLAG_HDR_10_PLUS,
- FLAG_HDR_HLG,
- FLAG_HDR_DOLBY_VISION
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ApplicationMediaCapabilitiesFlags {
- }
-
- /** Coefficient to 'guess' how long a transcoding session might take */
- private static final double TRANSCODING_TIMEOUT_COEFFICIENT = 2;
- /** Coefficient to 'guess' how large a transcoded file might be */
- private static final double TRANSCODING_SIZE_COEFFICIENT = 2;
-
- /**
- * Copied from MediaProvider.java
- * TODO(b/170465810): Remove this when getQueryBuilder code is refactored.
- */
- private static final int TYPE_QUERY = 0;
- private static final int TYPE_UPDATE = 2;
-
- private static final int MAX_FINISHED_TRANSCODING_SESSION_STORE_COUNT = 16;
- private static final String DIRECTORY_CAMERA = "Camera";
-
- private static final boolean IS_TRANSCODING_SUPPORTED = SdkLevel.isAtLeastS();
-
- private final Object mLock = new Object();
- private final Context mContext;
- private final MediaProvider mMediaProvider;
- private final PackageManager mPackageManager;
- private final StorageManager mStorageManager;
- private final ActivityManager mActivityManager;
- private final File mTranscodeDirectory;
- private final List<String> mSupportedRelativePaths;
- @GuardedBy("mLock")
- private UUID mTranscodeVolumeUuid;
-
- @GuardedBy("mLock")
- private final Map<String, StorageTranscodingSession> mStorageTranscodingSessions =
- new ArrayMap<>();
-
- // These are for dumping purpose only.
- // We keep these separately because the probability of getting cancelled and error'ed sessions
- // is pretty low, and we are limiting the count of what we keep. So, we don't wanna miss out
- // on dumping the cancelled and error'ed sessions.
- @GuardedBy("mLock")
- private final Map<StorageTranscodingSession, Boolean> mSuccessfulTranscodeSessions =
- createFinishedTranscodingSessionMap();
- @GuardedBy("mLock")
- private final Map<StorageTranscodingSession, Boolean> mCancelledTranscodeSessions =
- createFinishedTranscodingSessionMap();
- @GuardedBy("mLock")
- private final Map<StorageTranscodingSession, Boolean> mErroredTranscodeSessions =
- createFinishedTranscodingSessionMap();
-
- private final TranscodeUiNotifier mTranscodingUiNotifier;
- private final TranscodeDenialController mTranscodeDenialController;
- private final SessionTiming mSessionTiming;
- @GuardedBy("mLock")
- private final Map<String, Integer> mAppCompatMediaCapabilities = new ArrayMap<>();
- @GuardedBy("mLock")
- private boolean mIsTranscodeEnabled;
-
- private static final String[] TRANSCODE_CACHE_INFO_PROJECTION =
- {FileColumns._ID, FileColumns._TRANSCODE_STATUS};
- private static final String TRANSCODE_WHERE_CLAUSE =
- FileColumns.DATA + "=?" + " and mime_type not like 'null'";
-
- public TranscodeHelperImpl(Context context, MediaProvider mediaProvider) {
- mContext = context;
- mPackageManager = context.getPackageManager();
- mStorageManager = context.getSystemService(StorageManager.class);
- mActivityManager = context.getSystemService(ActivityManager.class);
- mMediaProvider = mediaProvider;
- mTranscodeDirectory = new File("/storage/emulated/" + UserHandle.myUserId(),
- DIRECTORY_TRANSCODE);
- mTranscodeDirectory.mkdirs();
- mSessionTiming = new SessionTiming();
- mTranscodingUiNotifier = new TranscodeUiNotifier(context, mSessionTiming);
- mIsTranscodeEnabled = isTranscodeEnabled();
- int maxTranscodeDurationMs =
- mMediaProvider.getIntDeviceConfig(TRANSCODE_MAX_DURATION_MS_KEY,
- MAX_TRANSCODE_DURATION_MS);
- mTranscodeDenialController = new TranscodeDenialController(mActivityManager,
- mTranscodingUiNotifier, maxTranscodeDurationMs);
- mSupportedRelativePaths = verifySupportedRelativePaths(getStringArrayConfig(
- R.array.config_supported_transcoding_relative_paths));
-
- parseTranscodeCompatManifest();
- // The storage namespace is a boot namespace so we actually don't expect this to be changed
- // after boot, but it is useful for tests
- mMediaProvider.addOnPropertiesChangedListener(properties -> parseTranscodeCompatManifest());
- }
-
- /**
- * Regex that matches path of transcode file. The regex only
- * matches emulated volume, for files in other volumes we don't
- * seamlessly transcode.
- */
- private static final Pattern PATTERN_TRANSCODE_PATH = Pattern.compile(
- "(?i)^/storage/emulated/(?:[0-9]+)/\\.transforms/transcode/(?:\\d+)$");
- private static final String DIRECTORY_TRANSCODE = ".transforms/transcode";
- /**
- * @return true if the file path matches transcode file path.
- */
- private static boolean isTranscodeFile(@NonNull String path) {
- final Matcher matcher = PATTERN_TRANSCODE_PATH.matcher(path);
- return matcher.matches();
- }
-
- public void freeCache(long bytes) {
- File[] files = mTranscodeDirectory.listFiles();
- for (File file : files) {
- if (bytes <= 0) {
- return;
- }
- if (file.exists() && file.isFile()) {
- long size = file.length();
- boolean deleted = file.delete();
- if (deleted) {
- bytes -= size;
- }
- }
- }
- }
-
- private UUID getTranscodeVolumeUuid() {
- synchronized (mLock) {
- if (mTranscodeVolumeUuid != null) {
- return mTranscodeVolumeUuid;
- }
- }
-
- StorageVolume vol = mStorageManager.getStorageVolume(mTranscodeDirectory);
- if (vol != null) {
- synchronized (mLock) {
- mTranscodeVolumeUuid = vol.getStorageUuid();
- return mTranscodeVolumeUuid;
- }
- } else {
- Log.w(TAG, "Failed to get storage volume UUID for: " + mTranscodeDirectory);
- return null;
- }
- }
-
- /**
- * @return transcode file's path for given {@code rowId}
- */
- @NonNull
- private String getTranscodePath(long rowId) {
- return new File(mTranscodeDirectory, String.valueOf(rowId)).getAbsolutePath();
- }
-
- public void onAnrDelayStarted(String packageName, int uid, int tid, int reason) {
- if (!isTranscodeEnabled()) {
- return;
- }
-
- if (uid == MY_UID) {
- Log.w(TAG, "Skipping ANR delay handling for MediaProvider");
- return;
- }
-
- logVerbose("Checking transcode status during ANR of " + packageName);
-
- Set<StorageTranscodingSession> sessions = new ArraySet<>();
- synchronized (mLock) {
- sessions.addAll(mStorageTranscodingSessions.values());
- }
-
- for (StorageTranscodingSession session: sessions) {
- if (session.isUidBlocked(uid)) {
- session.setAnr();
- Log.i(TAG, "Package: " + packageName + " with uid: " + uid
- + " and tid: " + tid + " is blocked on transcoding: " + session);
- // TODO(b/170973510): Show UI
- }
- }
- }
-
- // TODO(b/170974147): This should probably use a cache so we don't need to ask the
- // package manager every time for the package name or installer name
- private String getMetricsSafeNameForUid(int uid) {
- String name = mPackageManager.getNameForUid(uid);
- if (name == null) {
- Log.w(TAG, "null package name received from getNameForUid for uid " + uid
- + ", logging uid instead.");
- return Integer.toString(uid);
- } else if (name.isEmpty()) {
- Log.w(TAG, "empty package name received from getNameForUid for uid " + uid
- + ", logging uid instead");
- return ":empty_package_name:" + uid;
- } else {
- try {
- InstallSourceInfo installInfo = mPackageManager.getInstallSourceInfo(name);
- ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(name, 0);
- if (installInfo.getInstallingPackageName() == null
- && ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0)) {
- // For privacy reasons, we don't log metrics for side-loaded packages that
- // are not system packages
- return ":installer_adb:" + uid;
- }
- return name;
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Unable to check installer for uid: " + uid, e);
- return ":name_not_found:" + uid;
- }
- }
- }
-
- private void reportTranscodingResult(int uid, boolean success, int errorCode, int failureReason,
- long transcodingDurationMs,
- int transcodingReason, String src, String dst, boolean hasAnr) {
- BackgroundThread.getExecutor().execute(() -> {
- try (Cursor c = queryFileForTranscode(src,
- new String[]{MediaColumns.DURATION, MediaColumns.CAPTURE_FRAMERATE,
- MediaColumns.WIDTH, MediaColumns.HEIGHT})) {
- if (c != null && c.moveToNext()) {
- MediaProviderStatsLog.write(
- TRANSCODING_DATA,
- getMetricsSafeNameForUid(uid),
- MediaProviderStatsLog.TRANSCODING_DATA__ACCESS_TYPE__READ_TRANSCODE,
- success ? new File(dst).length() : -1,
- success ? TRANSCODING_DATA__TRANSCODE_RESULT__SUCCESS :
- TRANSCODING_DATA__TRANSCODE_RESULT__FAIL,
- transcodingDurationMs,
- c.getLong(0) /* video_duration */,
- c.getLong(1) /* capture_framerate */,
- transcodingReason,
- c.getLong(2) /* width */,
- c.getLong(3) /* height */,
- hasAnr,
- failureReason,
- errorCode);
- }
- }
- });
- }
-
- public boolean transcode(String src, String dst, int uid, int reason) {
- // This can only happen when we are in a version that supports transcoding.
- // So, no need to check for the SDK version here.
-
- StorageTranscodingSession storageSession = null;
- TranscodingSession transcodingSession = null;
- CountDownLatch latch = null;
- long startTime = SystemClock.elapsedRealtime();
- boolean result = false;
- int errorCode = TranscodingSession.ERROR_SERVICE_DIED;
- int failureReason = TRANSCODING_DATA__FAILURE_CAUSE__TRANSCODING_SERVICE_ERROR;
-
- try {
- synchronized (mLock) {
- storageSession = mStorageTranscodingSessions.get(src);
- if (storageSession == null) {
- latch = new CountDownLatch(1);
- try {
- transcodingSession = enqueueTranscodingSession(src, dst, uid, latch);
- if (transcodingSession == null) {
- Log.e(TAG, "Failed to enqueue request due to Service unavailable");
- throw new IllegalStateException("Failed to enqueue request");
- }
- } catch (UnsupportedOperationException | IOException e) {
- throw new IllegalStateException(e);
- }
- storageSession = new StorageTranscodingSession(transcodingSession, latch,
- src, dst);
- mStorageTranscodingSessions.put(src, storageSession);
- } else {
- latch = storageSession.latch;
- transcodingSession = storageSession.session;
- if (latch == null || transcodingSession == null) {
- throw new IllegalStateException("Uninitialised TranscodingSession for uid: "
- + uid + ". Path: " + src);
- }
- }
- storageSession.addBlockedUid(uid);
- }
-
- failureReason = waitTranscodingResult(uid, src, transcodingSession, latch);
- errorCode = transcodingSession.getErrorCode();
- result = failureReason == TRANSCODING_DATA__FAILURE_CAUSE__CAUSE_UNKNOWN;
-
- if (result) {
- updateTranscodeStatus(src, TRANSCODE_COMPLETE);
- } else {
- logEvent("Transcoding failed for " + src + ". session: ", transcodingSession);
- // Attempt to workaround potential media transcoding deadlock
- // Cancelling a deadlocked session seems to unblock the transcoder
- transcodingSession.cancel();
- }
- } finally {
- if (storageSession == null) {
- Log.w(TAG, "Failed to create a StorageTranscodingSession");
- // We were unable to even queue the request. Which means the media service is
- // in a very bad state
- reportTranscodingResult(uid, result, errorCode, failureReason,
- SystemClock.elapsedRealtime() - startTime, reason,
- src, dst, false /* hasAnr */);
- return false;
- }
-
- storageSession.notifyFinished(failureReason, errorCode);
- if (errorCode == TranscodingSession.ERROR_DROPPED_BY_SERVICE) {
- // If the transcoding service drops a request for a uid the uid will be denied
- // transcoding access until the next boot, notify the denial controller which may
- // also show a denial UI
- mTranscodeDenialController.onTranscodingDropped(uid);
- }
- reportTranscodingResult(uid, result, errorCode, failureReason,
- SystemClock.elapsedRealtime() - startTime, reason,
- src, dst, storageSession.hasAnr());
- }
- return result;
- }
-
- /**
- * Returns IO path for a {@code path} and {@code uid}
- *
- * IO path is the actual path to be used on the lower fs for IO via FUSE. For some file
- * transforms, this path might be different from the path the app is requesting IO on.
- *
- * @param path file path to get an IO path for
- * @param uid app requesting IO
- *
- */
- public String getIoPath(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.
-
- Pair<Long, Integer> cacheInfo = getTranscodeCacheInfoFromDB(path);
- final long rowId = cacheInfo.first;
- if (rowId == -1) {
- // No database row found, The file is pending/trashed or not added to database yet.
- // Assuming that no transcoding needed.
- return path;
- }
-
- int transcodeStatus = cacheInfo.second;
- final String transcodePath = getTranscodePath(rowId);
- final File transcodeFile = new File(transcodePath);
-
- if (transcodeFile.exists()) {
- return transcodePath;
- }
-
- if (transcodeStatus == TRANSCODE_COMPLETE) {
- // The transcode file doesn't exist but db row is marked as TRANSCODE_COMPLETE,
- // update db row to TRANSCODE_EMPTY so that cache state remains valid.
- 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();
- return transcodePath;
- }
-
- return transcodePath;
- }
-
- private static int getMediaCapabilitiesUid(int uid, Bundle bundle) {
- if (bundle == null || !bundle.containsKey(MediaStore.EXTRA_MEDIA_CAPABILITIES_UID)) {
- return uid;
- }
-
- int mediaCapabilitiesUid = bundle.getInt(MediaStore.EXTRA_MEDIA_CAPABILITIES_UID);
- if (mediaCapabilitiesUid >= Process.FIRST_APPLICATION_UID) {
- logVerbose(
- "Media capabilities uid " + mediaCapabilitiesUid + ", passed for uid " + uid);
- return mediaCapabilitiesUid;
- }
- Log.w(TAG, "Ignoring invalid media capabilities uid " + mediaCapabilitiesUid
- + " for uid: " + uid);
- return uid;
- }
-
- // TODO(b/173491972): Generalize to consider other file/app media capabilities beyond hevc
- /**
- * @return 0 or >0 representing whether we should transcode or not.
- * 0 means we should not transcode, otherwise we should transcode and the value is the
- * reason that will be logged to statsd as a transcode reason. Possible values are:
- * <ul>
- * <li>MediaProviderStatsLog.TRANSCODING_DATA__ACCESS_REASON__SYSTEM_DEFAULT=1
- * <li>MediaProviderStatsLog.TRANSCODING_DATA__ACCESS_REASON__SYSTEM_CONFIG=2
- * <li>MediaProviderStatsLog.TRANSCODING_DATA__ACCESS_REASON__APP_MANIFEST=3
- * <li>MediaProviderStatsLog.TRANSCODING_DATA__ACCESS_REASON__APP_COMPAT=4
- * <li>MediaProviderStatsLog.TRANSCODING_DATA__ACCESS_REASON__APP_EXTRA=5
- * </ul>
- *
- */
- public int shouldTranscode(String path, int uid, Bundle bundle) {
- boolean isTranscodeEnabled = isTranscodeEnabled();
- updateConfigs(isTranscodeEnabled);
-
- if (!isTranscodeEnabled) {
- logVerbose("Transcode not enabled");
- return 0;
- }
-
- uid = getMediaCapabilitiesUid(uid, bundle);
- logVerbose("Checking shouldTranscode for: " + path + ". Uid: " + uid);
-
- if (!supportsTranscode(path) || uid < Process.FIRST_APPLICATION_UID || uid == MY_UID) {
- logVerbose("Transcode not supported");
- // Never transcode in any of these conditions
- // 1. Path doesn't support transcode
- // 2. Uid is from native process on device
- // 3. Uid is ourselves, which can happen when we are opening a file via FUSE for
- // redaction on behalf of another app via ContentResolver
- return 0;
- }
-
- // Transcode only if file needs transcoding
- Pair<Integer, Long> result = getFileFlagsAndDurationMs(path);
- int fileFlags = result.first;
- long durationMs = result.second;
-
- if (fileFlags == 0) {
- // Nothing to transcode
- logVerbose("File is not HEVC");
- return 0;
- }
-
- int accessReason = doesAppNeedTranscoding(uid, bundle, fileFlags, durationMs);
- if (accessReason != 0 && mTranscodeDenialController.checkFileAccess(uid, durationMs)) {
- logVerbose("Transcoding denied");
- return 0;
- }
- return accessReason;
- }
-
- @VisibleForTesting
- int doesAppNeedTranscoding(int uid, Bundle bundle, int fileFlags, long durationMs) {
- // Check explicit Bundle provided
- if (bundle != null) {
- if (bundle.getBoolean(MediaStore.EXTRA_ACCEPT_ORIGINAL_MEDIA_FORMAT, false)) {
- logVerbose("Original format requested");
- return 0;
- }
-
- ApplicationMediaCapabilities capabilities =
- bundle.getParcelable(MediaStore.EXTRA_MEDIA_CAPABILITIES);
- if (capabilities != null) {
- Pair<Integer, Integer> flags = capabilitiesToMediaFormatFlags(capabilities);
- Optional<Boolean> appExtraResult = checkAppMediaSupport(flags.first, flags.second,
- fileFlags, "app_extra");
- if (appExtraResult.isPresent()) {
- if (appExtraResult.get()) {
- return MediaProviderStatsLog.TRANSCODING_DATA__ACCESS_REASON__APP_EXTRA;
- }
- return 0;
- }
- // Bundle didn't have enough information to make decision, continue
- }
- }
-
- // Check app compat support
- Optional<Boolean> appCompatResult = checkAppCompatSupport(uid, fileFlags);
- if (appCompatResult.isPresent()) {
- if (appCompatResult.get()) {
- return MediaProviderStatsLog.TRANSCODING_DATA__ACCESS_REASON__APP_COMPAT;
- }
- return 0;
- }
- // App compat didn't have enough information to make decision, continue
-
- // If we are here then the file supports HEVC, so we only check if the package is in the
- // mAppCompatCapabilities. If it's there, we will respect that value.
- LocalCallingIdentity identity = mMediaProvider.getCachedCallingIdentityForTranscoding(uid);
- final String[] callingPackages = identity.getSharedPackageNames();
-
- // Check app manifest support
- for (String callingPackage : callingPackages) {
- Optional<Boolean> appManifestResult = checkManifestSupport(callingPackage, identity,
- fileFlags);
- if (appManifestResult.isPresent()) {
- if (appManifestResult.get()) {
- return MediaProviderStatsLog.TRANSCODING_DATA__ACCESS_REASON__APP_MANIFEST;
- }
- return 0;
- }
- // App manifest didn't have enough information to make decision, continue
-
- // TODO(b/169327180): We should also check app's targetSDK version to verify if app
- // still qualifies to be on these lists.
- // Check config compat manifest
- synchronized (mLock) {
- if (mAppCompatMediaCapabilities.containsKey(callingPackage)) {
- int configCompatFlags = mAppCompatMediaCapabilities.get(callingPackage);
- int supportedFlags = configCompatFlags;
- int unsupportedFlags = ~configCompatFlags & MEDIA_FORMAT_FLAG_MASK;
-
- Optional<Boolean> systemConfigResult = checkAppMediaSupport(supportedFlags,
- unsupportedFlags, fileFlags, "system_config");
- if (systemConfigResult.isPresent()) {
- if (systemConfigResult.get()) {
- return MediaProviderStatsLog.TRANSCODING_DATA__ACCESS_REASON__SYSTEM_CONFIG;
- }
- return 0;
- }
- // Should never get here because the supported & unsupported flags should span
- // the entire universe of file flags
- }
- }
- }
-
- // TODO: Need to add transcode_default as flags
- if (shouldTranscodeDefault()) {
- logVerbose("Default behavior should transcode");
- return MediaProviderStatsLog.TRANSCODING_DATA__ACCESS_REASON__SYSTEM_DEFAULT;
- } else {
- logVerbose("Default behavior should not transcode");
- return 0;
- }
- }
-
- /**
- * Checks if transcode is required for the given app media capabilities and file media formats
- *
- * @param appSupportedMediaFormatFlags bit mask of media capabilites explicitly supported by an
- * app, e.g 001 indicating HEVC support
- * @param appUnsupportedMediaFormatFlags bit mask of media capabilites explicitly not supported
- * by an app, e.g 10 indicating HDR_10 is not supportted
- * @param fileMediaFormatFlags bit mask of media capabilites contained in a file e.g 101
- * indicating HEVC and HDR_10 media file
- *
- * @return {@code Optional} containing {@code boolean}. {@code true} means transcode is
- * required, {@code false} means transcode is not required and {@code empty} means a decision
- * could not be made.
- */
- private Optional<Boolean> checkAppMediaSupport(int appSupportedMediaFormatFlags,
- int appUnsupportedMediaFormatFlags, int fileMediaFormatFlags, String type) {
- if ((appSupportedMediaFormatFlags & appUnsupportedMediaFormatFlags) != 0) {
- Log.w(TAG, "Ignoring app media capabilities for type: [" + type
- + "]. Supported and unsupported capapbilities are not mutually exclusive");
- return Optional.empty();
- }
-
- // As an example:
- // 1. appSupportedMediaFormatFlags=001 # App supports HEVC
- // 2. appUnsupportedMediaFormatFlags=100 # App does not support HDR_10
- // 3. fileSupportedMediaFormatFlags=101 # File contains HEVC and HDR_10
-
- // File contains HDR_10 but app explicitly doesn't support it
- int fileMediaFormatsUnsupportedByApp =
- fileMediaFormatFlags & appUnsupportedMediaFormatFlags;
- if (fileMediaFormatsUnsupportedByApp != 0) {
- // If *any* file media formats are unsupported by the app we need to transcode
- logVerbose("App media capability check for type: [" + type + "]" + ". transcode=true");
- return Optional.of(true);
- }
-
- // fileMediaFormatsSupportedByApp=001 # File contains HEVC but app explicitly supports HEVC
- int fileMediaFormatsSupportedByApp = appSupportedMediaFormatFlags & fileMediaFormatFlags;
- // fileMediaFormatsNotSupportedByApp=100 # File contains HDR_10 but app doesn't support it
- int fileMediaFormatsNotSupportedByApp =
- fileMediaFormatsSupportedByApp ^ fileMediaFormatFlags;
- if (fileMediaFormatsNotSupportedByApp == 0) {
- logVerbose("App media capability check for type: [" + type + "]" + ". transcode=false");
- // If *all* file media formats are supported by the app, we don't need to transcode
- return Optional.of(false);
- }
-
- // If there are some file media formats that are neither supported nor unsupported by the
- // app we can't make a decision yet
- return Optional.empty();
- }
-
- private Pair<Integer, Long> getFileFlagsAndDurationMs(String path) {
- final String[] projection = new String[] {
- FileColumns._VIDEO_CODEC_TYPE,
- VideoColumns.COLOR_STANDARD,
- VideoColumns.COLOR_TRANSFER,
- MediaColumns.DURATION
- };
-
- try (Cursor cursor = queryFileForTranscode(path, projection)) {
- if (cursor == null || !cursor.moveToNext()) {
- logVerbose("Couldn't find database row");
- return Pair.create(0, 0L);
- }
-
- int result = 0;
- if (isHevc(cursor.getString(0))) {
- result |= FLAG_HEVC;
- }
- if (isHdr10Plus(cursor.getInt(1), cursor.getInt(2))) {
- result |= FLAG_HDR_10_PLUS;
- }
- return Pair.create(result, cursor.getLong(3));
- }
- }
-
- private static boolean isHevc(String mimeType) {
- return MediaFormat.MIMETYPE_VIDEO_HEVC.equalsIgnoreCase(mimeType);
- }
-
- private static boolean isHdr10Plus(int colorStandard, int colorTransfer) {
- return (colorStandard == MediaFormat.COLOR_STANDARD_BT2020) &&
- (colorTransfer == MediaFormat.COLOR_TRANSFER_ST2084
- || colorTransfer == MediaFormat.COLOR_TRANSFER_HLG);
- }
-
- private static boolean isModernFormat(String mimeType, int colorStandard, int colorTransfer) {
- return isHevc(mimeType) || isHdr10Plus(colorStandard, colorTransfer);
- }
-
- public boolean supportsTranscode(String path) {
- final File file = new File(path);
- final String name = file.getName();
- final String relativePath = 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 List<String> getStringArrayConfig(int resId) {
- final Resources res = mContext.getResources();
- try {
- final String[] configValue = res.getStringArray(resId);
- return Arrays.asList(configValue);
- } catch (NotFoundException e) {
- return new ArrayList<String>();
- }
- }
-
- private Optional<Boolean> checkAppCompatSupport(int uid, int fileFlags) {
- int supportedFlags = 0;
- int unsupportedFlags = 0;
- boolean hevcSupportEnabled = CompatChanges.isChangeEnabled(FORCE_ENABLE_HEVC_SUPPORT, uid);
- boolean hevcSupportDisabled = CompatChanges.isChangeEnabled(FORCE_DISABLE_HEVC_SUPPORT,
- uid);
- if (hevcSupportEnabled) {
- supportedFlags = FLAG_HEVC;
- logVerbose("App compat hevc support enabled");
- }
-
- if (hevcSupportDisabled) {
- unsupportedFlags = FLAG_HEVC;
- logVerbose("App compat hevc support disabled");
- }
- return checkAppMediaSupport(supportedFlags, unsupportedFlags, fileFlags, "app_compat");
- }
-
- /**
- * @return {@code true} if HEVC is explicitly supported by the manifest of {@code packageName},
- * {@code false} otherwise.
- */
- private Optional<Boolean> checkManifestSupport(String packageName,
- LocalCallingIdentity identity, int fileFlags) {
- // TODO(b/169327180):
- // 1. Support beyond HEVC
- // 2. Shared package names policy:
- // If appA and appB share the same uid. And appA supports HEVC but appB doesn't.
- // Should we assume entire uid supports or doesn't?
- // For now, we assume uid supports, but this might change in future
- int supportedFlags = identity.getApplicationMediaCapabilitiesSupportedFlags();
- int unsupportedFlags = identity.getApplicationMediaCapabilitiesUnsupportedFlags();
- if (supportedFlags != -1 && unsupportedFlags != -1) {
- return checkAppMediaSupport(supportedFlags, unsupportedFlags, fileFlags,
- "cached_app_manifest");
- }
-
- try {
- Property mediaCapProperty = mPackageManager.getProperty(
- PackageManager.PROPERTY_MEDIA_CAPABILITIES, packageName);
- XmlResourceParser parser = mPackageManager.getResourcesForApplication(packageName)
- .getXml(mediaCapProperty.getResourceId());
- ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml(
- parser);
- Pair<Integer, Integer> flags = capabilitiesToMediaFormatFlags(capability);
- supportedFlags = flags.first;
- unsupportedFlags = flags.second;
- identity.setApplicationMediaCapabilitiesFlags(supportedFlags, unsupportedFlags);
-
- return checkAppMediaSupport(supportedFlags, unsupportedFlags, fileFlags,
- "app_manifest");
- } catch (PackageManager.NameNotFoundException | UnsupportedOperationException e) {
- return Optional.empty();
- }
- }
-
- @ApplicationMediaCapabilitiesFlags
- private Pair<Integer, Integer> capabilitiesToMediaFormatFlags(
- ApplicationMediaCapabilities capability) {
- int supportedFlags = 0;
- int unsupportedFlags = 0;
-
- // MimeType
- if (capability.isFormatSpecified(MediaFormat.MIMETYPE_VIDEO_HEVC)) {
- if (capability.isVideoMimeTypeSupported(MediaFormat.MIMETYPE_VIDEO_HEVC)) {
- supportedFlags |= FLAG_HEVC;
- } else {
- unsupportedFlags |= FLAG_HEVC;
- }
- }
-
- // HdrType
- if (capability.isFormatSpecified(MediaFeature.HdrType.HDR10)) {
- if (capability.isHdrTypeSupported(MediaFeature.HdrType.HDR10)) {
- supportedFlags |= FLAG_HDR_10;
- } else {
- unsupportedFlags |= FLAG_HDR_10;
- }
- }
-
- if (capability.isFormatSpecified(MediaFeature.HdrType.HDR10_PLUS)) {
- if (capability.isHdrTypeSupported(MediaFeature.HdrType.HDR10_PLUS)) {
- supportedFlags |= FLAG_HDR_10_PLUS;
- } else {
- unsupportedFlags |= FLAG_HDR_10_PLUS;
- }
- }
-
- if (capability.isFormatSpecified(MediaFeature.HdrType.HLG)) {
- if (capability.isHdrTypeSupported(MediaFeature.HdrType.HLG)) {
- supportedFlags |= FLAG_HDR_HLG;
- } else {
- unsupportedFlags |= FLAG_HDR_HLG;
- }
- }
-
- if (capability.isFormatSpecified(MediaFeature.HdrType.DOLBY_VISION)) {
- if (capability.isHdrTypeSupported(MediaFeature.HdrType.DOLBY_VISION)) {
- supportedFlags |= FLAG_HDR_DOLBY_VISION;
- } else {
- unsupportedFlags |= FLAG_HDR_DOLBY_VISION;
- }
- }
-
- return Pair.create(supportedFlags, unsupportedFlags);
- }
-
- private boolean getBooleanProperty(String sysPropKey, String deviceConfigKey,
- boolean defaultValue) {
- // If the user wants to override the default, respect that; otherwise use the DeviceConfig
- // which is filled with the values sent from server.
- if (SystemProperties.getBoolean(TRANSCODE_USER_CONTROL_SYS_PROP_KEY, false)) {
- return SystemProperties.getBoolean(sysPropKey, defaultValue);
- }
-
- return mMediaProvider.getBooleanDeviceConfig(deviceConfigKey, defaultValue);
- }
-
- private Pair<Long, Integer> getTranscodeCacheInfoFromDB(String path) {
- try (Cursor cursor = queryFileForTranscode(path, TRANSCODE_CACHE_INFO_PROJECTION)) {
- if (cursor != null && cursor.moveToNext()) {
- return Pair.create(cursor.getLong(0), cursor.getInt(1));
- }
- }
- return Pair.create((long) -1, TRANSCODE_EMPTY);
- }
-
- // called from MediaProvider
- public void onUriPublished(Uri uri) {
- if (!isTranscodeEnabled()) {
- return;
- }
-
- try (Cursor c = mMediaProvider.queryForSingleItem(uri,
- new String[]{
- FileColumns._VIDEO_CODEC_TYPE,
- FileColumns.SIZE,
- FileColumns.OWNER_PACKAGE_NAME,
- FileColumns.DATA,
- MediaColumns.DURATION,
- MediaColumns.CAPTURE_FRAMERATE,
- MediaColumns.WIDTH,
- MediaColumns.HEIGHT
- },
- null, null, null)) {
- if (supportsTranscode(c.getString(3))) {
- if (isHevc(c.getString(0))) {
- MediaProviderStatsLog.write(
- TRANSCODING_DATA,
- c.getString(2) /* owner_package_name */,
- MediaProviderStatsLog.TRANSCODING_DATA__ACCESS_TYPE__HEVC_WRITE,
- c.getLong(1) /* file size */,
- TRANSCODING_DATA__TRANSCODE_RESULT__UNDEFINED,
- -1 /* transcoding_duration */,
- c.getLong(4) /* video_duration */,
- c.getLong(5) /* capture_framerate */,
- -1 /* transcode_reason */,
- c.getLong(6) /* width */,
- c.getLong(7) /* height */,
- false /* hit_anr */,
- TRANSCODING_DATA__FAILURE_CAUSE__CAUSE_UNKNOWN,
- TranscodingSession.ERROR_NONE);
-
- } else {
- MediaProviderStatsLog.write(
- TRANSCODING_DATA,
- c.getString(2) /* owner_package_name */,
- MediaProviderStatsLog.TRANSCODING_DATA__ACCESS_TYPE__AVC_WRITE,
- c.getLong(1) /* file size */,
- TRANSCODING_DATA__TRANSCODE_RESULT__UNDEFINED,
- -1 /* transcoding_duration */,
- c.getLong(4) /* video_duration */,
- c.getLong(5) /* capture_framerate */,
- -1 /* transcode_reason */,
- c.getLong(6) /* width */,
- c.getLong(7) /* height */,
- false /* hit_anr */,
- TRANSCODING_DATA__FAILURE_CAUSE__CAUSE_UNKNOWN,
- TranscodingSession.ERROR_NONE);
- }
- }
- } catch (Exception e) {
- Log.w(TAG, "Couldn't get cursor for scanned file", e);
- }
- }
-
- public void onFileOpen(String path, String ioPath, int uid, int transformsReason) {
- if (!isTranscodeEnabled()) {
- return;
- }
-
- String[] resolverInfoProjection = new String[] {
- FileColumns._VIDEO_CODEC_TYPE,
- FileColumns.SIZE,
- MediaColumns.DURATION,
- MediaColumns.CAPTURE_FRAMERATE,
- MediaColumns.WIDTH,
- MediaColumns.HEIGHT,
- VideoColumns.COLOR_STANDARD,
- VideoColumns.COLOR_TRANSFER
- };
-
- try (Cursor c = queryFileForTranscode(path, resolverInfoProjection)) {
- if (c != null && c.moveToNext()) {
- if (supportsTranscode(path)
- && isModernFormat(c.getString(0), c.getInt(6), c.getInt(7))) {
- if (transformsReason == 0) {
- MediaProviderStatsLog.write(
- TRANSCODING_DATA,
- getMetricsSafeNameForUid(uid) /* owner_package_name */,
- MediaProviderStatsLog.TRANSCODING_DATA__ACCESS_TYPE__READ_DIRECT,
- c.getLong(1) /* file size */,
- TRANSCODING_DATA__TRANSCODE_RESULT__UNDEFINED,
- -1 /* transcoding_duration */,
- c.getLong(2) /* video_duration */,
- c.getLong(3) /* capture_framerate */,
- -1 /* transcode_reason */,
- c.getLong(4) /* width */,
- c.getLong(5) /* height */,
- false /*hit_anr*/,
- TRANSCODING_DATA__FAILURE_CAUSE__CAUSE_UNKNOWN,
- TranscodingSession.ERROR_NONE);
- } else if (isTranscodeFileCached(path, ioPath)) {
- MediaProviderStatsLog.write(
- TRANSCODING_DATA,
- getMetricsSafeNameForUid(uid) /* owner_package_name */,
- MediaProviderStatsLog.TRANSCODING_DATA__ACCESS_TYPE__READ_CACHE,
- c.getLong(1) /* file size */,
- TRANSCODING_DATA__TRANSCODE_RESULT__UNDEFINED,
- -1 /* transcoding_duration */,
- c.getLong(2) /* video_duration */,
- c.getLong(3) /* capture_framerate */,
- transformsReason /* transcode_reason */,
- c.getLong(4) /* width */,
- c.getLong(5) /* height */,
- false /*hit_anr*/,
- TRANSCODING_DATA__FAILURE_CAUSE__CAUSE_UNKNOWN,
- TranscodingSession.ERROR_NONE);
- } // else if file is not in cache, we'll log at read(2) when we transcode
- }
- }
- } catch (IllegalStateException e) {
- Log.w(TAG, "Unable to log metrics on file open", e);
- }
- }
-
- public boolean isTranscodeFileCached(String path, String transcodePath) {
- // This can only happen when we are in a version that supports transcoding.
- // So, no need to check for the SDK version here.
-
- if (SystemProperties.getBoolean("persist.sys.fuse.disable_transcode_cache", false)) {
- // Caching is disabled. Hence, delete the cached transcode file.
- return false;
- }
-
- Pair<Long, Integer> cacheInfo = getTranscodeCacheInfoFromDB(path);
- final long rowId = cacheInfo.first;
- if (rowId != -1) {
- final int transcodeStatus = cacheInfo.second;
- boolean result = transcodePath.equalsIgnoreCase(getTranscodePath(rowId)) &&
- transcodeStatus == TRANSCODE_COMPLETE &&
- new File(transcodePath).exists();
- if (result) {
- logEvent("Transcode cache hit: " + path, null /* session */);
- }
- return result;
- }
- return false;
- }
-
- @Nullable
- private MediaFormat getVideoTrackFormat(String path) {
- String[] resolverInfoProjection = new String[]{
- FileColumns._VIDEO_CODEC_TYPE,
- MediaStore.MediaColumns.WIDTH,
- MediaStore.MediaColumns.HEIGHT,
- MediaStore.MediaColumns.BITRATE,
- MediaStore.MediaColumns.CAPTURE_FRAMERATE
- };
- try (Cursor c = queryFileForTranscode(path, resolverInfoProjection)) {
- if (c != null && c.moveToNext()) {
- String codecType = c.getString(0);
- int width = c.getInt(1);
- int height = c.getInt(2);
- int bitRate = c.getInt(3);
- float framerate = c.getFloat(4);
-
- // TODO(b/169849854): Get this info from Manifest, for now if app got here it
- // definitely doesn't support hevc
- ApplicationMediaCapabilities capability =
- new ApplicationMediaCapabilities.Builder().build();
- MediaFormat sourceFormat = MediaFormat.createVideoFormat(
- codecType, width, height);
- if (framerate > 0) {
- sourceFormat.setFloat(MediaFormat.KEY_FRAME_RATE, framerate);
- }
- VideoFormatResolver resolver = new VideoFormatResolver(capability, sourceFormat);
- MediaFormat resolvedFormat = resolver.resolveVideoFormat();
- resolvedFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
-
- return resolvedFormat;
- }
- }
- throw new IllegalStateException("Couldn't get video format info from database for " + path);
- }
-
- private TranscodingSession enqueueTranscodingSession(String src, String dst, int uid,
- final CountDownLatch latch) throws UnsupportedOperationException, IOException {
- // Fetch the service lazily to improve memory usage
- final MediaTranscodingManager mediaTranscodeManager =
- mContext.getSystemService(MediaTranscodingManager.class);
- File file = new File(src);
- File transcodeFile = new File(dst);
-
- // These are file URIs (effectively file paths) and even if the |transcodeFile| is
- // inaccesible via FUSE, it works because the transcoding service calls into the
- // MediaProvider to open them and within the MediaProvider, it is opened directly on
- // the lower fs.
- Uri uri = Uri.fromFile(file);
- Uri transcodeUri = Uri.fromFile(transcodeFile);
-
- ParcelFileDescriptor srcPfd = ParcelFileDescriptor.open(file,
- ParcelFileDescriptor.MODE_READ_ONLY);
- ParcelFileDescriptor dstPfd = ParcelFileDescriptor.open(transcodeFile,
- ParcelFileDescriptor.MODE_READ_WRITE);
-
- MediaFormat format = getVideoTrackFormat(src);
-
- VideoTranscodingRequest request =
- new VideoTranscodingRequest.Builder(uri, transcodeUri, format)
- .setClientUid(uid)
- .setSourceFileDescriptor(srcPfd)
- .setDestinationFileDescriptor(dstPfd)
- .build();
- TranscodingSession session = mediaTranscodeManager.enqueueRequest(request,
- ForegroundThread.getExecutor(),
- s -> {
- mTranscodingUiNotifier.stop(s, src);
- finishTranscodingResult(uid, src, s, latch);
- mSessionTiming.logSessionEnd(s);
- });
- session.setOnProgressUpdateListener(ForegroundThread.getExecutor(),
- (s, progress) -> mTranscodingUiNotifier.setProgress(s, src, progress));
-
- mSessionTiming.logSessionStart(session);
- mTranscodingUiNotifier.start(session, src);
- logEvent("Transcoding start: " + src + ". Uid: " + uid, session);
- return session;
- }
-
- /**
- * Returns an {@link Integer} indicating whether the transcoding {@code session} was successful
- * or not.
- *
- * @return {@link TRANSCODING_DATA__FAILURE_CAUSE__CAUSE_UNKNOWN} on success,
- * otherwise indicates failure.
- */
- private int waitTranscodingResult(int uid, String src, TranscodingSession session,
- CountDownLatch latch) {
- UUID uuid = getTranscodeVolumeUuid();
- try {
- if (uuid != null) {
- // tid is 0 since we can't really get the apps tid over binder
- mStorageManager.notifyAppIoBlocked(uuid, uid, 0 /* tid */,
- StorageManager.APP_IO_BLOCKED_REASON_TRANSCODING);
- }
-
- int timeout = getTranscodeTimeoutSeconds(src);
-
- String waitStartLog = "Transcoding wait start: " + src + ". Uid: " + uid + ". Timeout: "
- + timeout + "s";
- logEvent(waitStartLog, session);
-
- boolean latchResult = latch.await(timeout, TimeUnit.SECONDS);
- int sessionResult = session.getResult();
- boolean transcodeResult = sessionResult == TranscodingSession.RESULT_SUCCESS;
-
- String waitEndLog = "Transcoding wait end: " + src + ". Uid: " + uid + ". Timeout: "
- + !latchResult + ". Success: " + transcodeResult;
- logEvent(waitEndLog, session);
-
- if (sessionResult == TranscodingSession.RESULT_SUCCESS) {
- return TRANSCODING_DATA__FAILURE_CAUSE__CAUSE_UNKNOWN;
- } else if (sessionResult == TranscodingSession.RESULT_CANCELED) {
- return TRANSCODING_DATA__FAILURE_CAUSE__TRANSCODING_SESSION_CANCELED;
- } else if (!latchResult) {
- return TRANSCODING_DATA__FAILURE_CAUSE__TRANSCODING_CLIENT_TIMEOUT;
- } else {
- return TRANSCODING_DATA__FAILURE_CAUSE__TRANSCODING_SERVICE_ERROR;
- }
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- Log.w(TAG, "Transcoding latch interrupted." + session);
- return TRANSCODING_DATA__FAILURE_CAUSE__TRANSCODING_CLIENT_TIMEOUT;
- } finally {
- if (uuid != null) {
- // tid is 0 since we can't really get the apps tid over binder
- mStorageManager.notifyAppIoResumed(uuid, uid, 0 /* tid */,
- StorageManager.APP_IO_BLOCKED_REASON_TRANSCODING);
- }
- }
- }
-
- private int getTranscodeTimeoutSeconds(String file) {
- double sizeMb = (new File(file).length() / (1024 * 1024));
- // Ensure size is at least 1MB so transcoding timeout is at least the timeout coefficient
- sizeMb = Math.max(sizeMb, 1);
- return (int) (sizeMb * TRANSCODING_TIMEOUT_COEFFICIENT);
- }
-
- private void finishTranscodingResult(int uid, String src, TranscodingSession session,
- CountDownLatch latch) {
- final StorageTranscodingSession finishedSession;
-
- synchronized (mLock) {
- latch.countDown();
- session.cancel();
-
- finishedSession = mStorageTranscodingSessions.remove(src);
-
- switch (session.getResult()) {
- case TranscodingSession.RESULT_SUCCESS:
- mSuccessfulTranscodeSessions.put(finishedSession, false /* placeholder */);
- break;
- case TranscodingSession.RESULT_CANCELED:
- mCancelledTranscodeSessions.put(finishedSession, false /* placeholder */);
- break;
- case TranscodingSession.RESULT_ERROR:
- mErroredTranscodeSessions.put(finishedSession, false /* placeholder */);
- break;
- default:
- Log.w(TAG, "TranscodingSession.RESULT_NONE received for a finished session");
- }
- }
-
- logEvent("Transcoding end: " + src + ". Uid: " + uid, session);
- }
-
- private boolean updateTranscodeStatus(String path, int transcodeStatus) {
- final Uri uri = FileUtils.getContentUriForPath(path);
- // TODO(b/170465810): Replace this with matchUri when the code is refactored.
- final int match = MediaProvider.FILES;
- final SQLiteQueryBuilder qb = mMediaProvider.getQueryBuilderForTranscoding(TYPE_UPDATE,
- match, uri, Bundle.EMPTY, null);
- final String[] selectionArgs = new String[]{path};
-
- ContentValues values = new ContentValues();
- values.put(FileColumns._TRANSCODE_STATUS, transcodeStatus);
- final boolean success = qb.update(getDatabaseHelperForUri(uri), values,
- TRANSCODE_WHERE_CLAUSE, selectionArgs) == 1;
- if (!success) {
- Log.w(TAG, "Transcoding status update to: " + transcodeStatus + " failed for " + path);
- }
- return success;
- }
-
- public boolean deleteCachedTranscodeFile(long rowId) {
- return new File(mTranscodeDirectory, String.valueOf(rowId)).delete();
- }
-
- private DatabaseHelper getDatabaseHelperForUri(Uri uri) {
- final DatabaseHelper helper;
- try {
- return mMediaProvider.getDatabaseForUriForTranscoding(uri);
- } catch (VolumeNotFoundException e) {
- throw new IllegalStateException("Volume not found while querying transcode path", e);
- }
- }
-
- /**
- * @return given {@code projection} columns from database for given {@code path}.
- * Note that cursor might be empty if there is no database row or file is pending or trashed.
- * TODO(b/170465810): Optimize these queries by bypassing getQueryBuilder(). These queries are
- * always on Files table and doesn't have any dependency on calling package. i.e., query is
- * always called with callingPackage=self.
- */
- @Nullable
- private Cursor queryFileForTranscode(String path, String[] projection) {
- final Uri uri = FileUtils.getContentUriForPath(path);
- // TODO(b/170465810): Replace this with matchUri when the code is refactored.
- final int match = MediaProvider.FILES;
- final SQLiteQueryBuilder qb = mMediaProvider.getQueryBuilderForTranscoding(TYPE_QUERY,
- match, uri, Bundle.EMPTY, null);
- final String[] selectionArgs = new String[]{path};
-
- Bundle extras = new Bundle();
- extras.putInt(QUERY_ARG_MATCH_PENDING, MATCH_EXCLUDE);
- extras.putInt(QUERY_ARG_MATCH_TRASHED, MATCH_EXCLUDE);
- extras.putString(ContentResolver.QUERY_ARG_SQL_SELECTION, TRANSCODE_WHERE_CLAUSE);
- extras.putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS, selectionArgs);
- return qb.query(getDatabaseHelperForUri(uri), projection, extras, null);
- }
-
- private boolean isTranscodeEnabled() {
- return IS_TRANSCODING_SUPPORTED && getBooleanProperty(TRANSCODE_ENABLED_SYS_PROP_KEY,
- TRANSCODE_ENABLED_DEVICE_CONFIG_KEY, true /* defaultValue */);
- }
-
- private boolean shouldTranscodeDefault() {
- return getBooleanProperty(TRANSCODE_DEFAULT_SYS_PROP_KEY,
- TRANSCODE_DEFAULT_DEVICE_CONFIG_KEY, false /* defaultValue */);
- }
-
- private void updateConfigs(boolean transcodeEnabled) {
- synchronized (mLock) {
- boolean isTranscodeEnabledChanged = transcodeEnabled != mIsTranscodeEnabled;
-
- if (isTranscodeEnabledChanged) {
- Log.i(TAG, "Reloading transcode configs. transcodeEnabled: " + transcodeEnabled
- + ". lastTranscodeEnabled: " + mIsTranscodeEnabled);
-
- mIsTranscodeEnabled = transcodeEnabled;
- parseTranscodeCompatManifest();
- }
- }
- }
-
- private void parseTranscodeCompatManifest() {
- synchronized (mLock) {
- // Clear the transcode_compat manifest before parsing. If transcode is disabled,
- // nothing will be parsed, effectively leaving the compat manifest empty.
- mAppCompatMediaCapabilities.clear();
- if (!mIsTranscodeEnabled) {
- return;
- }
-
- Set<String> stalePackages = getTranscodeCompatStale();
- parseTranscodeCompatManifestFromResourceLocked(stalePackages);
- parseTranscodeCompatManifestFromDeviceConfigLocked();
- }
- }
-
- /** @return {@code true} if the manifest was parsed successfully, {@code false} otherwise */
- private boolean parseTranscodeCompatManifestFromDeviceConfigLocked() {
- final String[] manifest = mMediaProvider.getStringDeviceConfig(
- TRANSCODE_COMPAT_MANIFEST_KEY, "").split(",");
-
- if (manifest.length == 0 || manifest[0].isEmpty()) {
- Log.i(TAG, "Empty device config transcode compat manifest");
- return false;
- }
- if ((manifest.length % 2) != 0) {
- Log.w(TAG, "Uneven number of items in device config transcode compat manifest");
- return false;
- }
-
- String packageName = "";
- int packageCompatValue;
- int i = 0;
- int count = 0;
- while (i < manifest.length - 1) {
- try {
- packageName = manifest[i++];
- packageCompatValue = Integer.parseInt(manifest[i++]);
- synchronized (mLock) {
- // Lock is already held, explicitly hold again to make error prone happy
- mAppCompatMediaCapabilities.put(packageName, packageCompatValue);
- count++;
- }
- } catch (NumberFormatException e) {
- Log.w(TAG, "Failed to parse media capability from device config for package: "
- + packageName, e);
- }
- }
-
- Log.i(TAG, "Parsed " + count + " packages from device config");
- return count != 0;
- }
-
- /** @return {@code true} if the manifest was parsed successfully, {@code false} otherwise */
- private boolean parseTranscodeCompatManifestFromResourceLocked(Set<String> stalePackages) {
- InputStream inputStream = mContext.getResources().openRawResource(
- R.raw.transcode_compat_manifest);
- BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
- int count = 0;
- try {
- while (reader.ready()) {
- String line = reader.readLine();
- String packageName = "";
- int packageCompatValue;
-
- if (line == null) {
- Log.w(TAG, "Unexpected null line while parsing transcode compat manifest");
- continue;
- }
-
- String[] lineValues = line.split(",");
- if (lineValues.length != 2) {
- Log.w(TAG, "Failed to read line while parsing transcode compat manifest");
- continue;
- }
- try {
- packageName = lineValues[0];
- packageCompatValue = Integer.parseInt(lineValues[1]);
-
- if (stalePackages.contains(packageName)) {
- Log.i(TAG, "Skipping stale package in transcode compat manifest: "
- + packageName);
- continue;
- }
-
- synchronized (mLock) {
- // Lock is already held, explicitly hold again to make error prone happy
- mAppCompatMediaCapabilities.put(packageName, packageCompatValue);
- count++;
- }
- } catch (NumberFormatException e) {
- Log.w(TAG, "Failed to parse media capability from resource for package: "
- + packageName, e);
- }
- }
- } catch (IOException e) {
- Log.w(TAG, "Failed to read transcode compat manifest", e);
- }
-
- Log.i(TAG, "Parsed " + count + " packages from resource");
- return count != 0;
- }
-
- private Set<String> getTranscodeCompatStale() {
- Set<String> stalePackages = new ArraySet<>();
- final String[] staleConfig = mMediaProvider.getStringDeviceConfig(
- TRANSCODE_COMPAT_STALE_KEY, "").split(",");
-
- if (staleConfig.length == 0 || staleConfig[0].isEmpty()) {
- Log.i(TAG, "Empty transcode compat stale");
- return stalePackages;
- }
-
- for (String stalePackage : staleConfig) {
- stalePackages.add(stalePackage);
- }
-
- int size = stalePackages.size();
- Log.i(TAG, "Parsed " + size + " stale packages from device config");
- return stalePackages;
- }
-
- public void dump(PrintWriter writer) {
- writer.println("isTranscodeEnabled=" + isTranscodeEnabled());
- writer.println("shouldTranscodeDefault=" + shouldTranscodeDefault());
-
- synchronized (mLock) {
- writer.println("mAppCompatMediaCapabilities=" + mAppCompatMediaCapabilities);
- writer.println("mStorageTranscodingSessions=" + mStorageTranscodingSessions);
- writer.println("mSupportedTranscodingRelativePaths=" + mSupportedRelativePaths);
-
- dumpFinishedSessions(writer);
- }
- }
-
- public List<String> getSupportedRelativePaths() {
- return mSupportedRelativePaths;
- }
-
- private void dumpFinishedSessions(PrintWriter writer) {
- synchronized (mLock) {
- writer.println("mSuccessfulTranscodeSessions=" + mSuccessfulTranscodeSessions.keySet());
-
- writer.println("mCancelledTranscodeSessions=" + mCancelledTranscodeSessions.keySet());
-
- writer.println("mErroredTranscodeSessions=" + mErroredTranscodeSessions.keySet());
- }
- }
-
- private static void logEvent(String event, @Nullable TranscodingSession session) {
- Log.d(TAG, event + (session == null ? "" : session));
- }
-
- private static void logVerbose(String message) {
- if (DEBUG) {
- Log.v(TAG, message);
- }
- }
-
- // We want to keep track of only the most recent [MAX_FINISHED_TRANSCODING_SESSION_STORE_COUNT]
- // finished transcoding sessions.
- private static LinkedHashMap createFinishedTranscodingSessionMap() {
- return new LinkedHashMap<StorageTranscodingSession, Boolean>() {
- @Override
- protected boolean removeEldestEntry(Entry eldest) {
- return size() > MAX_FINISHED_TRANSCODING_SESSION_STORE_COUNT;
- }
- };
- }
-
- @VisibleForTesting
- static int getMyUid() {
- return MY_UID;
- }
-
- private static class StorageTranscodingSession {
- private static final DateTimeFormatter DATE_FORMAT =
- DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
-
- public final TranscodingSession session;
- public final CountDownLatch latch;
- private final String mSrcPath;
- private final String mDstPath;
- @GuardedBy("latch")
- private final Set<Integer> mBlockedUids = new ArraySet<>();
- private final LocalDateTime mStartTime;
- @GuardedBy("latch")
- private LocalDateTime mFinishTime;
- @GuardedBy("latch")
- private boolean mHasAnr;
- @GuardedBy("latch")
- private int mFailureReason;
- @GuardedBy("latch")
- private int mErrorCode;
-
- public StorageTranscodingSession(TranscodingSession session, CountDownLatch latch,
- String srcPath, String dstPath) {
- this.session = session;
- this.latch = latch;
- this.mSrcPath = srcPath;
- this.mDstPath = dstPath;
- this.mStartTime = LocalDateTime.now();
- mErrorCode = TranscodingSession.ERROR_NONE;
- mFailureReason = TRANSCODING_DATA__FAILURE_CAUSE__CAUSE_UNKNOWN;
- }
-
- public void addBlockedUid(int uid) {
- session.addClientUid(uid);
- }
-
- public boolean isUidBlocked(int uid) {
- return session.getClientUids().contains(uid);
- }
-
- public void setAnr() {
- synchronized (latch) {
- mHasAnr = true;
- }
- }
-
- public boolean hasAnr() {
- synchronized (latch) {
- return mHasAnr;
- }
- }
-
- public void notifyFinished(int failureReason, int errorCode) {
- synchronized (latch) {
- mFinishTime = LocalDateTime.now();
- mFailureReason = failureReason;
- mErrorCode = errorCode;
- }
- }
-
- @Override
- public String toString() {
- String startTime = mStartTime.format(DATE_FORMAT);
- String finishTime = "NONE";
- String durationMs = "NONE";
- boolean hasAnr;
- int failureReason;
- int errorCode;
-
- synchronized (latch) {
- if (mFinishTime != null) {
- finishTime = mFinishTime.format(DATE_FORMAT);
- durationMs = String.valueOf(mStartTime.until(mFinishTime, ChronoUnit.MILLIS));
- }
- hasAnr = mHasAnr;
- failureReason = mFailureReason;
- errorCode = mErrorCode;
- }
-
- return String.format("<%s. Src: %s. Dst: %s. BlockedUids: %s. DurationMs: %sms"
- + ". Start: %s. Finish: %sms. HasAnr: %b. FailureReason: %d. ErrorCode: %d>",
- session.toString(), mSrcPath, mDstPath, session.getClientUids(), durationMs,
- startTime, finishTime, hasAnr, failureReason, errorCode);
- }
- }
-
- private static class TranscodeUiNotifier {
- private static final int PROGRESS_MAX = 100;
- private static final int ALERT_DISMISS_DELAY_MS = 1000;
- private static final int SHOW_PROGRESS_THRESHOLD_TIME_MS = 1000;
- private static final String TRANSCODE_ALERT_CHANNEL_ID = "native_transcode_alert_channel";
- private static final String TRANSCODE_ALERT_CHANNEL_NAME = "Native Transcode Alerts";
- private static final String TRANSCODE_PROGRESS_CHANNEL_ID =
- "native_transcode_progress_channel";
- private static final String TRANSCODE_PROGRESS_CHANNEL_NAME = "Native Transcode Progress";
-
- // Related to notification settings
- private static final String TRANSCODE_NOTIFICATION_SYS_PROP_KEY =
- "persist.sys.fuse.transcode_notification";
- private static final boolean NOTIFICATION_ALLOWED_DEFAULT_VALUE = false;
-
- private final Context mContext;
- private final NotificationManagerCompat mNotificationManager;
- private final PackageManager mPackageManager;
- // Builder for creating alert notifications.
- private final NotificationCompat.Builder mAlertBuilder;
- // Builder for creating progress notifications.
- private final NotificationCompat.Builder mProgressBuilder;
- private final SessionTiming mSessionTiming;
-
- TranscodeUiNotifier(Context context, SessionTiming sessionTiming) {
- mContext = context;
- mNotificationManager = NotificationManagerCompat.from(context);
- mPackageManager = context.getPackageManager();
- createAlertNotificationChannel(context);
- createProgressNotificationChannel(context);
- mAlertBuilder = createAlertNotificationBuilder(context);
- mProgressBuilder = createProgressNotificationBuilder(context);
- mSessionTiming = sessionTiming;
- }
-
- void start(TranscodingSession session, String filePath) {
- if (!notificationEnabled()) {
- return;
- }
- ForegroundThread.getHandler().post(() -> {
- mAlertBuilder.setContentTitle(getString(mContext,
- R.string.transcode_processing_started));
- mAlertBuilder.setContentText(FileUtils.extractDisplayName(filePath));
- final int notificationId = session.getSessionId();
- mNotificationManager.notify(notificationId, mAlertBuilder.build());
- });
- }
-
- void stop(TranscodingSession session, String filePath) {
- if (!notificationEnabled()) {
- return;
- }
- endSessionWithMessage(session, filePath, getResultMessageForSession(mContext, session));
- }
-
- void denied(int uid) {
- String appName = getAppName(uid);
- if (appName == null) {
- Log.w(TAG, "Not showing denial, no app name ");
- return;
- }
-
- final Handler handler = ForegroundThread.getHandler();
- handler.post(() -> {
- Toast.makeText(mContext,
- mContext.getResources().getString(R.string.transcode_denied, appName),
- Toast.LENGTH_LONG).show();
- });
- }
-
- void setProgress(TranscodingSession session, String filePath,
- @IntRange(from = 0, to = PROGRESS_MAX) int progress) {
- if (!notificationEnabled()) {
- return;
- }
- if (shouldShowProgress(session)) {
- mProgressBuilder.setContentText(FileUtils.extractDisplayName(filePath));
- mProgressBuilder.setProgress(PROGRESS_MAX, progress, /* indeterminate= */ false);
- final int notificationId = session.getSessionId();
- mNotificationManager.notify(notificationId, mProgressBuilder.build());
- }
- }
-
- private boolean shouldShowProgress(TranscodingSession session) {
- return (System.currentTimeMillis() - mSessionTiming.getSessionStartTime(session))
- > SHOW_PROGRESS_THRESHOLD_TIME_MS;
- }
-
- private void endSessionWithMessage(TranscodingSession session, String filePath,
- String message) {
- final Handler handler = ForegroundThread.getHandler();
- handler.post(() -> {
- mAlertBuilder.setContentTitle(message);
- mAlertBuilder.setContentText(FileUtils.extractDisplayName(filePath));
- final int notificationId = session.getSessionId();
- mNotificationManager.notify(notificationId, mAlertBuilder.build());
- // Auto-dismiss after a delay.
- handler.postDelayed(() -> mNotificationManager.cancel(notificationId),
- ALERT_DISMISS_DELAY_MS);
- });
- }
-
- private String getAppName(int uid) {
- String name = mPackageManager.getNameForUid(uid);
- if (name == null) {
- Log.w(TAG, "Couldn't find name");
- return null;
- }
-
- final ApplicationInfo aInfo;
- try {
- aInfo = mPackageManager.getApplicationInfo(name, 0);
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "unable to look up package name", e);
- return null;
- }
-
- // If the label contains new line characters it may push the security
- // message below the fold of the dialog. Labels shouldn't have new line
- // characters anyways, so we just delete all of the newlines (if there are any).
- return aInfo.loadSafeLabel(mPackageManager, MAX_APP_NAME_SIZE_PX,
- TextUtils.SAFE_STRING_FLAG_SINGLE_LINE).toString();
- }
-
- private static String getString(Context context, int resourceId) {
- return context.getResources().getString(resourceId);
- }
-
- private static void createAlertNotificationChannel(Context context) {
- NotificationChannel channel = new NotificationChannel(TRANSCODE_ALERT_CHANNEL_ID,
- TRANSCODE_ALERT_CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH);
- NotificationManager notificationManager = context.getSystemService(
- NotificationManager.class);
- notificationManager.createNotificationChannel(channel);
- }
-
- private static void createProgressNotificationChannel(Context context) {
- NotificationChannel channel = new NotificationChannel(TRANSCODE_PROGRESS_CHANNEL_ID,
- TRANSCODE_PROGRESS_CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW);
- NotificationManager notificationManager = context.getSystemService(
- NotificationManager.class);
- notificationManager.createNotificationChannel(channel);
- }
-
- private static NotificationCompat.Builder createAlertNotificationBuilder(Context context) {
- NotificationCompat.Builder builder = new NotificationCompat.Builder(context,
- TRANSCODE_ALERT_CHANNEL_ID);
- builder.setAutoCancel(false)
- .setOngoing(true)
- .setSmallIcon(R.drawable.thumb_clip);
- return builder;
- }
-
- private static NotificationCompat.Builder createProgressNotificationBuilder(
- Context context) {
- NotificationCompat.Builder builder = new NotificationCompat.Builder(context,
- TRANSCODE_PROGRESS_CHANNEL_ID);
- builder.setAutoCancel(false)
- .setOngoing(true)
- .setContentTitle(getString(context, R.string.transcode_processing))
- .setSmallIcon(R.drawable.thumb_clip);
- return builder;
- }
-
- private static String getResultMessageForSession(Context context,
- TranscodingSession session) {
- switch (session.getResult()) {
- case TranscodingSession.RESULT_CANCELED:
- return getString(context, R.string.transcode_processing_cancelled);
- case TranscodingSession.RESULT_ERROR:
- return getString(context, R.string.transcode_processing_error);
- case TranscodingSession.RESULT_SUCCESS:
- return getString(context, R.string.transcode_processing_success);
- default:
- return getString(context, R.string.transcode_processing_error);
- }
- }
-
- private static boolean notificationEnabled() {
- return SystemProperties.getBoolean(TRANSCODE_NOTIFICATION_SYS_PROP_KEY,
- NOTIFICATION_ALLOWED_DEFAULT_VALUE);
- }
- }
-
- private static class TranscodeDenialController implements OnUidImportanceListener {
- private final int mMaxDurationMs;
- private final ActivityManager mActivityManager;
- private final TranscodeUiNotifier mUiNotifier;
- private final Object mLock = new Object();
- @GuardedBy("mLock")
- private final Set<Integer> mActiveDeniedUids = new ArraySet<>();
- @GuardedBy("mLock")
- private final Set<Integer> mDroppedUids = new ArraySet<>();
-
- TranscodeDenialController(ActivityManager activityManager, TranscodeUiNotifier uiNotifier,
- int maxDurationMs) {
- mActivityManager = activityManager;
- mUiNotifier = uiNotifier;
- mMaxDurationMs = maxDurationMs;
- }
-
- @Override
- public void onUidImportance(int uid, int importance) {
- if (importance != IMPORTANCE_FOREGROUND) {
- synchronized (mLock) {
- if (mActiveDeniedUids.remove(uid) && mActiveDeniedUids.isEmpty()) {
- // Stop the uid listener if this is the last uid triggering a denial UI
- mActivityManager.removeOnUidImportanceListener(this);
- }
- }
- }
- }
-
- /** @return {@code true} if file access should be denied, {@code false} otherwise */
- boolean checkFileAccess(int uid, long durationMs) {
- boolean shouldDeny = false;
- synchronized (mLock) {
- shouldDeny = durationMs > mMaxDurationMs || mDroppedUids.contains(uid);
- }
-
- if (!shouldDeny) {
- // Nothing to do
- return false;
- }
-
- synchronized (mLock) {
- if (!mActiveDeniedUids.contains(uid)
- && mActivityManager.getUidImportance(uid) == IMPORTANCE_FOREGROUND) {
- // Show UI for the first denial while foreground
- mUiNotifier.denied(uid);
-
- if (mActiveDeniedUids.isEmpty()) {
- // Start a uid listener if this is the first uid triggering a denial UI
- mActivityManager.addOnUidImportanceListener(this, IMPORTANCE_FOREGROUND);
- }
- mActiveDeniedUids.add(uid);
- }
- }
- return true;
- }
-
- void onTranscodingDropped(int uid) {
- synchronized (mLock) {
- mDroppedUids.add(uid);
- }
- // Notify about file access, so we might show a denial UI
- checkFileAccess(uid, 0 /* duration */);
- }
- }
-
- private static final class SessionTiming {
- // This should be accessed only in foreground thread.
- private final SparseArray<Long> mSessionStartTimes = new SparseArray<>();
-
- // Call this only in foreground thread.
- private long getSessionStartTime(MediaTranscodingManager.TranscodingSession session) {
- return mSessionStartTimes.get(session.getSessionId());
- }
-
- private void logSessionStart(MediaTranscodingManager.TranscodingSession session) {
- ForegroundThread.getHandler().post(
- () -> mSessionStartTimes.append(session.getSessionId(),
- System.currentTimeMillis()));
- }
-
- private void logSessionEnd(MediaTranscodingManager.TranscodingSession session) {
- ForegroundThread.getHandler().post(
- () -> mSessionStartTimes.remove(session.getSessionId()));
- }
- }
-}
diff --git a/src/com/android/providers/media/TranscodeHelperNoOp.java b/src/com/android/providers/media/TranscodeHelperNoOp.java
deleted file mode 100644
index ee4549b..0000000
--- a/src/com/android/providers/media/TranscodeHelperNoOp.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media;
-
-import android.net.Uri;
-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
- */
-public class TranscodeHelperNoOp implements TranscodeHelper {
- public void freeCache(long bytes) {}
-
- public void onAnrDelayStarted(String packageName, int uid, int tid, int reason) {}
-
- public boolean transcode(String src, String dst, int uid, int reason) {
- return false;
- }
-
- public String getIoPath(String path, int uid) {
- return null;
- }
-
- public int shouldTranscode(String path, int uid, Bundle bundle) {
- return 0;
- }
-
- public boolean supportsTranscode(String path) {
- return false;
- }
-
- public void onUriPublished(Uri uri) {}
-
- public void onFileOpen(String path, String ioPath, int uid, int transformsReason) {}
-
- public boolean isTranscodeFileCached(String path, String transcodePath) {
- return false;
- }
-
- public boolean deleteCachedTranscodeFile(long rowId) {
- return false;
- }
-
- 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
deleted file mode 100644
index 90f7d3b..0000000
--- a/src/com/android/providers/media/VolumeCache.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media;
-
-import static com.android.providers.media.util.Logging.TAG;
-
-import android.content.Context;
-import android.net.Uri;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.os.storage.StorageManager;
-import android.os.storage.StorageVolume;
-import android.provider.MediaStore;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
-
-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;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * The VolumeCache class keeps track of all the volumes that are available,
- * as well as their scan paths.
- */
-public class VolumeCache {
- private final Context mContext;
-
- private final Object mLock = new Object();
-
- private final UserManager mUserManager;
- private final UserCache mUserCache;
-
- @GuardedBy("mLock")
- private final ArrayList<MediaVolume> mExternalVolumes = new ArrayList<>();
-
- @GuardedBy("mLock")
- private final Map<MediaVolume, Collection<File>> mCachedVolumeScanPaths = new ArrayMap<>();
-
- @GuardedBy("mLock")
- private Collection<File> mCachedInternalScanPaths;
-
- public VolumeCache(Context context, UserCache userCache) {
- mContext = context;
- mUserManager = context.getSystemService(UserManager.class);
- mUserCache = userCache;
- }
-
- public @NonNull List<MediaVolume> getExternalVolumes() {
- synchronized(mLock) {
- return new ArrayList<>(mExternalVolumes);
- }
- }
-
- public @NonNull Set<String> getExternalVolumeNames() {
- synchronized (mLock) {
- ArraySet<String> volNames = new ArraySet<String>();
- for (MediaVolume vol : mExternalVolumes) {
- volNames.add(vol.getName());
- }
- return volNames;
- }
- }
-
- /**
- * @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) {
- for (MediaVolume vol : mExternalVolumes) {
- if (vol.getName().equals(volumeName) && vol.isVisibleToUser(user)) {
- return vol;
- }
- }
- }
-
- throw new FileNotFoundException("Couldn't find volume with name " + volumeName);
- }
-
- public @NonNull File getVolumePath(@NonNull String volumeName, @NonNull UserHandle user)
- throws FileNotFoundException {
- synchronized (mLock) {
- try {
- MediaVolume volume = findVolume(volumeName, user);
- return volume.getPath();
- } catch (FileNotFoundException e) {
- Log.w(TAG, "getVolumePath for unknown volume: " + volumeName);
- // Try again by using FileUtils below
- }
-
- final Context userContext = mUserCache.getContextForUser(user);
- return FileUtils.getVolumePath(userContext, volumeName);
- }
- }
-
- public @NonNull Collection<File> getVolumeScanPaths(@NonNull String volumeName,
- @NonNull UserHandle user) throws FileNotFoundException {
- synchronized (mLock) {
- if (MediaStore.VOLUME_INTERNAL.equals(volumeName)) {
- // Internal is shared by all users
- return mCachedInternalScanPaths;
- }
- try {
- MediaVolume volume = findVolume(volumeName, user);
- if (mCachedVolumeScanPaths.containsKey(volume)) {
- return mCachedVolumeScanPaths.get(volume);
- }
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Didn't find cached volume scan paths for " + volumeName);
- }
-
- // Nothing found above; let's ask directly
- final Context userContext = mUserCache.getContextForUser(user);
- final Collection<File> res = FileUtils.getVolumeScanPaths(userContext, volumeName);
-
- return res;
- }
- }
-
- public @NonNull MediaVolume findVolumeForFile(@NonNull File file) throws FileNotFoundException {
- synchronized (mLock) {
- for (MediaVolume volume : mExternalVolumes) {
- if (FileUtils.contains(volume.getPath(), file)) {
- return volume;
- }
- }
- }
-
- Log.w(TAG, "Didn't find any volume for getVolume(" + file.getPath() + ")");
- // Nothing found above; let's ask directly
- final StorageManager sm = mContext.getSystemService(StorageManager.class);
- final StorageVolume volume = sm.getStorageVolume(file);
- if (volume == null) {
- throw new FileNotFoundException("Missing volume for " + file);
- }
-
- return MediaVolume.fromStorageVolume(volume);
- }
-
- public @NonNull String getVolumeId(@NonNull File file) throws FileNotFoundException {
- MediaVolume volume = findVolumeForFile(file);
-
- return volume.getId();
- }
-
- @GuardedBy("mLock")
- private void updateExternalVolumesForUserLocked(Context userContext) {
- final StorageManager sm = userContext.getSystemService(StorageManager.class);
- for (String volumeName : MediaStore.getExternalVolumeNames(userContext)) {
- try {
- final Uri uri = MediaStore.Files.getContentUri(volumeName);
- final StorageVolume storageVolume = sm.getStorageVolume(uri);
- MediaVolume volume = MediaVolume.fromStorageVolume(storageVolume);
- mExternalVolumes.add(volume);
- mCachedVolumeScanPaths.put(volume, FileUtils.getVolumeScanPaths(userContext,
- volume.getName()));
- } catch (IllegalStateException | FileNotFoundException e) {
- Log.wtf(TAG, "Failed to update volume " + volumeName, e);
- }
- }
- }
-
- public void update() {
- synchronized (mLock) {
- mCachedVolumeScanPaths.clear();
- try {
- mCachedInternalScanPaths = FileUtils.getVolumeScanPaths(mContext,
- MediaStore.VOLUME_INTERNAL);
- } catch (FileNotFoundException e) {
- Log.wtf(TAG, "Failed to update volume " + MediaStore.VOLUME_INTERNAL,e );
- }
- mExternalVolumes.clear();
- List<UserHandle> users = mUserCache.updateAndGetUsers();
- for (UserHandle user : users) {
- Context userContext = mUserCache.getContextForUser(user);
- updateExternalVolumesForUserLocked(userContext);
- }
- }
- }
-
- public void dump(PrintWriter writer) {
- writer.println("Volume cache state:");
- synchronized (mLock) {
- for (MediaVolume volume : mExternalVolumes) {
- writer.println(" " + volume.toString());
- }
- }
- }
-}
diff --git a/src/com/android/providers/media/fuse/ExternalStorageServiceImpl.java b/src/com/android/providers/media/fuse/ExternalStorageServiceImpl.java
index 0afdc48..0c1cb94 100644
--- a/src/com/android/providers/media/fuse/ExternalStorageServiceImpl.java
+++ b/src/com/android/providers/media/fuse/ExternalStorageServiceImpl.java
@@ -18,7 +18,6 @@
import static com.android.providers.media.scan.MediaScanner.REASON_MOUNTED;
-import android.annotation.BytesLong;
import android.content.ContentProviderClient;
import android.os.Environment;
import android.os.OperationCanceledException;
@@ -33,16 +32,11 @@
import com.android.providers.media.MediaProvider;
import com.android.providers.media.MediaService;
-import com.android.providers.media.MediaVolume;
-
-import com.android.providers.media.util.BackgroundThread;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
-import java.util.Objects;
-import java.util.UUID;
/**
* Handles filesystem I/O from other apps.
@@ -54,14 +48,9 @@
private static final Map<String, FuseDaemon> sFuseDaemons = new HashMap<>();
@Override
- public void onStartSession(@NonNull String sessionId, /* @SessionFlag */ int flag,
+ public void onStartSession(String sessionId, /* @SessionFlag */ int flag,
@NonNull ParcelFileDescriptor deviceFd, @NonNull File upperFileSystemPath,
@NonNull File lowerFileSystemPath) {
- Objects.requireNonNull(sessionId);
- Objects.requireNonNull(deviceFd);
- Objects.requireNonNull(upperFileSystemPath);
- Objects.requireNonNull(lowerFileSystemPath);
-
MediaProvider mediaProvider = getMediaProvider();
synchronized (sLock) {
@@ -72,10 +61,8 @@
// 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]);
FuseDaemon daemon = new FuseDaemon(mediaProvider, this, deviceFd, sessionId,
- upperFileSystemPath.getPath(), supportedTranscodingRelativePaths);
+ upperFileSystemPath.getPath());
daemon.start();
sFuseDaemons.put(sessionId, daemon);
}
@@ -83,27 +70,22 @@
}
@Override
- public void onVolumeStateChanged(@NonNull StorageVolume vol) throws IOException {
- Objects.requireNonNull(vol);
-
+ public void onVolumeStateChanged(StorageVolume vol) throws IOException {
MediaProvider mediaProvider = getMediaProvider();
+ String volumeName = vol.getMediaStoreVolumeName();
switch(vol.getState()) {
case Environment.MEDIA_MOUNTED:
- MediaVolume volume = MediaVolume.fromStorageVolume(vol);
- mediaProvider.attachVolume(volume, /* validate */ false);
- MediaService.queueVolumeScan(mediaProvider.getContext(), volume, REASON_MOUNTED);
- BackgroundThread.getExecutor().execute(() ->
- mediaProvider.getPickerSyncController().syncPicker());
+ mediaProvider.attachVolume(volumeName, /* validate */ false);
break;
case Environment.MEDIA_UNMOUNTED:
case Environment.MEDIA_EJECTING:
case Environment.MEDIA_REMOVED:
case Environment.MEDIA_BAD_REMOVAL:
- mediaProvider.detachVolume(MediaVolume.fromStorageVolume(vol));
+ mediaProvider.detachVolume(volumeName);
break;
default:
- Log.i(TAG, "Ignoring volume state for vol:" + vol.getMediaStoreVolumeName()
+ Log.i(TAG, "Ignoring volume state for vol:" + volumeName
+ ". State: " + vol.getState());
}
// Check for invalidation of cached volumes
@@ -111,9 +93,7 @@
}
@Override
- public void onEndSession(@NonNull String sessionId) {
- Objects.requireNonNull(sessionId);
-
+ public void onEndSession(String sessionId) {
FuseDaemon daemon = onExitSession(sessionId);
if (daemon == null) {
@@ -128,24 +108,7 @@
}
}
- @Override
- public void onFreeCache(@NonNull UUID volumeUuid, @BytesLong long bytes) throws IOException {
- Objects.requireNonNull(volumeUuid);
-
- Log.i(TAG, "Free cache requested for " + bytes + " bytes");
- getMediaProvider().freeCache(bytes);
- }
-
- @Override
- public void onAnrDelayStarted(@NonNull String packageName, int uid, int tid, int reason) {
- Objects.requireNonNull(packageName);
-
- getMediaProvider().onAnrDelayStarted(packageName, uid, tid, reason);
- }
-
- public FuseDaemon onExitSession(@NonNull String sessionId) {
- Objects.requireNonNull(sessionId);
-
+ public FuseDaemon onExitSession(String sessionId) {
Log.i(TAG, "Exiting session for id: " + sessionId);
synchronized (sLock) {
return sFuseDaemons.remove(sessionId);
diff --git a/src/com/android/providers/media/fuse/FuseDaemon.java b/src/com/android/providers/media/fuse/FuseDaemon.java
index 0c9ca85..9a433c9 100644
--- a/src/com/android/providers/media/fuse/FuseDaemon.java
+++ b/src/com/android/providers/media/fuse/FuseDaemon.java
@@ -24,8 +24,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.providers.media.MediaProvider;
-import java.io.File;
-import java.io.IOException;
import java.util.Objects;
/**
@@ -40,22 +38,18 @@
private final MediaProvider mMediaProvider;
private final int mFuseDeviceFd;
private final String mPath;
- private final String[] mSupportedTranscodingRelativePaths;
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,
- String[] supportedTranscodingRelativePaths) {
+ @NonNull String sessionId, @NonNull String path) {
mMediaProvider = Objects.requireNonNull(mediaProvider);
mService = Objects.requireNonNull(service);
setName(Objects.requireNonNull(sessionId));
mFuseDeviceFd = Objects.requireNonNull(fd).detachFd();
mPath = Objects.requireNonNull(path);
- mSupportedTranscodingRelativePaths
- = Objects.requireNonNull(supportedTranscodingRelativePaths);
}
/** Starts a FUSE session. Does not return until the lower filesystem is unmounted. */
@@ -71,7 +65,7 @@
}
Log.i(TAG, "Starting thread for " + getName() + " ...");
- native_start(ptr, mFuseDeviceFd, mPath, mSupportedTranscodingRelativePaths); // Blocks
+ native_start(ptr, mFuseDeviceFd, mPath); // Blocks
Log.i(TAG, "Exiting thread for " + getName() + " ...");
synchronized (mLock) {
@@ -88,20 +82,6 @@
// Wait for native_start
waitForStart();
-
- // Initialize device id
- initializeDeviceId();
- }
-
- private void initializeDeviceId() {
- synchronized (mLock) {
- if (mPtr == 0) {
- Log.e(TAG, "initializeDeviceId failed, FUSE daemon unavailable");
- return;
- }
- String path = mMediaProvider.getFuseFile(new File(mPath)).getAbsolutePath();
- native_initialize_device_id(mPtr, path);
- }
}
private void waitForStart() {
@@ -171,28 +151,15 @@
}
}
- public String getOriginalMediaFormatFilePath(ParcelFileDescriptor fileDescriptor)
- throws IOException {
- synchronized (mLock) {
- if (mPtr == 0) {
- throw new IOException("FUSE daemon unavailable");
- }
- return native_get_original_media_format_file_path(mPtr, fileDescriptor.getFd());
- }
- }
-
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,
- String[] supportedTranscodingRelativePaths);
+ private native void native_start(long daemon, int deviceFd, String path);
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 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 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/PulledMetrics.java b/src/com/android/providers/media/metrics/PulledMetrics.java
deleted file mode 100644
index f9c5fac..0000000
--- a/src/com/android/providers/media/metrics/PulledMetrics.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.metrics;
-
-import static com.android.providers.media.MediaProviderStatsLog.GENERAL_EXTERNAL_STORAGE_ACCESS_STATS;
-import static com.android.providers.media.MediaProviderStatsLog.TRANSCODING_DATA;
-
-import android.app.StatsManager;
-import android.content.Context;
-import android.util.Log;
-import android.util.StatsEvent;
-
-import androidx.annotation.NonNull;
-
-import com.android.providers.media.fuse.FuseDaemon;
-import com.android.providers.media.util.BackgroundThread;
-
-import java.util.List;
-
-/** A class to initialise and log metrics pulled by statsd. */
-public class PulledMetrics {
- private static final String TAG = "PulledMetrics";
-
- private static final StatsPullCallbackHandler STATS_PULL_CALLBACK_HANDLER =
- new StatsPullCallbackHandler();
-
- private static final StorageAccessMetrics storageAccessMetrics = new StorageAccessMetrics();
-
- private static boolean isInitialized = false;
-
- public static void initialize(Context context) {
- if (isInitialized) {
- return;
- }
-
- final StatsManager statsManager = context.getSystemService(StatsManager.class);
- if (statsManager == null) {
- Log.e(TAG, "Error retrieving StatsManager. Cannot initialize PulledMetrics.");
- } else {
- Log.d(TAG, "Registering callback with StatsManager");
-
- try {
- // use the same callback handler for registering for all the tags.
- statsManager.setPullAtomCallback(TRANSCODING_DATA, null /* metadata */,
- BackgroundThread.getExecutor(),
- STATS_PULL_CALLBACK_HANDLER);
- statsManager.setPullAtomCallback(
- GENERAL_EXTERNAL_STORAGE_ACCESS_STATS,
- /*metadata*/null,
- BackgroundThread.getExecutor(),
- STATS_PULL_CALLBACK_HANDLER);
- isInitialized = true;
- } catch (NullPointerException e) {
- Log.w(TAG, "Pulled metrics not supported. Could not register.", e);
- }
- }
- }
-
- // Storage Access Metrics log functions
-
- /**
- * Logs the mime type that was accessed by the given {@code uid}.
- * Does nothing if the stats puller is not initialized.
- */
- public static void logMimeTypeAccess(int uid, @NonNull String mimeType) {
- if (!isInitialized) {
- return;
- }
-
- 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.
- */
- public static void logFileAccessViaFuse(int uid, @NonNull String file) {
- if (!isInitialized) {
- return;
- }
-
- storageAccessMetrics.logAccessViaFuse(uid, file);
- }
-
- /**
- * Logs the storage access and attributes it to the given {@code uid}.
- *
- * <p>This is a no-op if it's called on a FUSE thread.
- */
- public static void logVolumeAccessViaMediaProvider(int uid, @NonNull String volumeName) {
- if (!isInitialized) {
- return;
- }
-
- // We don't log if it's a FUSE thread because logAccessViaFuse should handle that.
- if (FuseDaemon.native_is_fuse_thread()) {
- return;
- }
- storageAccessMetrics.logAccessViaMediaProvider(uid, volumeName);
- }
-
- private static class StatsPullCallbackHandler implements StatsManager.StatsPullAtomCallback {
- @Override
- public int onPullAtom(int atomTag, List<StatsEvent> data) {
- // handle the tags appropriately.
- List<StatsEvent> events = pullEvents(atomTag);
- if (events == null) {
- return StatsManager.PULL_SKIP;
- }
-
- data.addAll(events);
- return StatsManager.PULL_SUCCESS;
- }
-
- private List<StatsEvent> pullEvents(int atomTag) {
- switch (atomTag) {
- case TRANSCODING_DATA:
- return TranscodeMetrics.pullStatsEvents();
- case GENERAL_EXTERNAL_STORAGE_ACCESS_STATS:
- return storageAccessMetrics.pullStatsEvents();
- default:
- return null;
- }
- }
- }
-}
diff --git a/src/com/android/providers/media/metrics/StatsdPuller.java b/src/com/android/providers/media/metrics/StatsdPuller.java
new file mode 100644
index 0000000..606d058
--- /dev/null
+++ b/src/com/android/providers/media/metrics/StatsdPuller.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.metrics;
+
+import android.app.StatsManager;
+import android.content.Context;
+import android.os.Build;
+import android.util.Log;
+import android.util.StatsEvent;
+
+import java.util.List;
+
+/**
+ * A "static" class providing the boilerplate for handling the pulling of metrics by statsd.
+ * All individuals using this should accumulate their metrics per their policies.
+ */
+public class StatsdPuller {
+ private static final String TAG = "StatsdPuller";
+
+ private static final StatsPullCallbackHandler STATS_PULL_CALLBACK_HANDLER =
+ new StatsPullCallbackHandler();
+
+ private static boolean isInitialized = false;
+
+ public static void initialize(Context context) {
+ if (isInitialized) {
+ return;
+ }
+
+ final StatsManager statsManager = context.getSystemService(StatsManager.class);
+ if (statsManager == null) {
+ Log.e(TAG, "Error retrieving StatsManager. Cannot initialize StatsdPuller.");
+ } else {
+ // use the same callback handler for registering for all the tags.
+ isInitialized = true;
+ }
+ }
+
+ public static boolean isInitialized() {
+ return isInitialized;
+ }
+
+ private static class StatsPullCallbackHandler implements StatsManager.StatsPullAtomCallback {
+ @Override
+ public int onPullAtom(int atomTag, List<StatsEvent> data) {
+ // handle the tags appropriately.
+ return StatsManager.PULL_SUCCESS;
+ }
+ }
+}
diff --git a/src/com/android/providers/media/metrics/StorageAccessMetrics.java b/src/com/android/providers/media/metrics/StorageAccessMetrics.java
deleted file mode 100644
index ce54e3f..0000000
--- a/src/com/android/providers/media/metrics/StorageAccessMetrics.java
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.metrics;
-
-import static com.android.providers.media.MediaProviderStatsLog.GENERAL_EXTERNAL_STORAGE_ACCESS_STATS;
-
-import static java.util.stream.Collectors.toList;
-
-import android.os.Process;
-import android.os.SystemClock;
-import android.provider.MediaStore;
-import android.util.ArraySet;
-import android.util.Log;
-import android.util.SparseArray;
-import android.util.StatsEvent;
-import android.util.proto.ProtoOutputStream;
-
-import androidx.annotation.GuardedBy;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.providers.media.MediaProviderStatsLog;
-import com.android.providers.media.util.FileUtils;
-import com.android.providers.media.util.MimeUtils;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Metrics for {@link MediaProviderStatsLog#GENERAL_EXTERNAL_STORAGE_ACCESS_STATS}. This class
- * gathers stats separately for each UID that accesses external storage.
- */
-class StorageAccessMetrics {
-
- private static final String TAG = "StorageAccessMetrics";
-
- @VisibleForTesting
- static final int UID_SAMPLES_COUNT_LIMIT = 50;
-
- private final int mMyUid = Process.myUid();
-
- @GuardedBy("mLock")
- private final SparseArray<PackageStorageAccessStats> mAccessStatsPerPackage =
- new SparseArray<>();
- @GuardedBy("mLock")
- private long mStartTimeMillis = SystemClock.uptimeMillis();
-
- private final Object mLock = new Object();
-
-
- /**
- * Logs the mime type that was accessed by the given {@code uid}.
- */
- void logMimeType(int uid, @NonNull String mimeType) {
- if (mimeType == null) {
- Log.w(TAG, "Attempted to log null mime type access");
- return;
- }
-
- synchronized (mLock) {
- getOrGeneratePackageStatsObjectLocked(uid).mMimeTypes.add(mimeType);
- }
- }
-
- /**
- * Logs the storage access and attributes it to the given {@code uid}.
- *
- * <p>Should only be called from a FUSE thread.
- */
- void logAccessViaFuse(int uid, @NonNull String file) {
- // We don't log the access if it's MediaProvider accessing.
- if (mMyUid == uid) {
- return;
- }
-
- incrementFilePathAccesses(uid);
- final String volumeName = MediaStore.getVolumeName(
- FileUtils.getContentUriForPath(file));
- logGeneralExternalStorageAccess(uid, volumeName);
- logMimeTypeFromFile(uid, file);
- }
-
-
- /**
- * Logs the storage access and attributes it to the given {@code uid}.
- */
- void logAccessViaMediaProvider(int uid, @NonNull String volumeName) {
- // We also don't log the access if it's MediaProvider accessing.
- if (mMyUid == uid) {
- return;
- }
-
- logGeneralExternalStorageAccess(uid, volumeName);
- }
-
- /**
- * Use this to log whenever a package accesses external storage via ContentResolver or FUSE.
- * The given volume name helps us determine whether this was an access on primary or secondary
- * storage.
- */
- private void logGeneralExternalStorageAccess(int uid, @NonNull String volumeName) {
- switch (volumeName) {
- case MediaStore.VOLUME_EXTERNAL:
- case MediaStore.VOLUME_EXTERNAL_PRIMARY:
- incrementTotalAccesses(uid);
- break;
- case MediaStore.VOLUME_INTERNAL:
- case MediaStore.VOLUME_DEMO:
- case MediaStore.MEDIA_SCANNER_VOLUME:
- break;
- default:
- // Secondary external storage
- incrementTotalAccesses(uid);
- incrementSecondaryStorageAccesses(uid);
- }
- }
-
- /**
- * Logs that the mime type of the given {@param file} was accessed by the given {@param uid}.
- */
- private void logMimeTypeFromFile(int uid, @Nullable String file) {
- logMimeType(uid, MimeUtils.resolveMimeType(new File(file)));
- }
-
- private void incrementTotalAccesses(int uid) {
- synchronized (mLock) {
- getOrGeneratePackageStatsObjectLocked(uid).mTotalAccesses += 1;
- }
- }
-
- private void incrementFilePathAccesses(int uid) {
- synchronized (mLock) {
- getOrGeneratePackageStatsObjectLocked(uid).mFilePathAccesses += 1;
- }
- }
-
- private void incrementSecondaryStorageAccesses(int uid) {
- synchronized (mLock) {
- getOrGeneratePackageStatsObjectLocked(uid).mSecondaryStorageAccesses += 1;
- }
- }
-
- @GuardedBy("mLock")
- private PackageStorageAccessStats getOrGeneratePackageStatsObjectLocked(int uid) {
- PackageStorageAccessStats stats = mAccessStatsPerPackage.get(uid);
- if (stats == null) {
- stats = new PackageStorageAccessStats(uid);
- mAccessStatsPerPackage.put(uid, stats);
- }
- return stats;
- }
-
- /**
- * Returns the list of {@link StatsEvent} since latest reset, for a random subset of tracked
- * uids if there are more than {@link #UID_SAMPLES_COUNT_LIMIT} in total. Returns {@code null}
- * when the time since reset is non-positive.
- */
- @Nullable
- List<StatsEvent> pullStatsEvents() {
- synchronized (mLock) {
- final long timeInterval = SystemClock.uptimeMillis() - mStartTimeMillis;
- List<PackageStorageAccessStats> stats = getSampleStats();
- resetStats();
- return stats
- .stream()
- .map(s -> s.toNormalizedStats(timeInterval).toStatsEvent())
- .collect(toList());
- }
- }
-
- @VisibleForTesting
- List<PackageStorageAccessStats> getSampleStats() {
- synchronized (mLock) {
- List<PackageStorageAccessStats> result = new ArrayList<>();
-
- List<Integer> sampledUids = new ArrayList<>();
- for (int i = 0; i < mAccessStatsPerPackage.size(); i++) {
- sampledUids.add(mAccessStatsPerPackage.keyAt(i));
- }
-
- if (sampledUids.size() > UID_SAMPLES_COUNT_LIMIT) {
- Collections.shuffle(sampledUids);
- sampledUids = sampledUids.subList(0, UID_SAMPLES_COUNT_LIMIT);
- }
- for (Integer uid : sampledUids) {
- PackageStorageAccessStats stats = mAccessStatsPerPackage.get(uid);
- result.add(stats);
- }
-
- return result;
- }
- }
-
- private void resetStats() {
- synchronized (mLock) {
- mAccessStatsPerPackage.clear();
- mStartTimeMillis = SystemClock.uptimeMillis();
- }
- }
-
- @VisibleForTesting
- static class PackageStorageAccessStats {
- private final int mUid;
- int mTotalAccesses = 0;
- int mFilePathAccesses = 0;
- int mSecondaryStorageAccesses = 0;
-
- final ArraySet<String> mMimeTypes = new ArraySet<>();
-
- PackageStorageAccessStats(int uid) {
- this.mUid = uid;
- }
-
- PackageStorageAccessStats toNormalizedStats(long timeInterval) {
- this.mTotalAccesses = normalizeAccessesPerDay(mTotalAccesses, timeInterval);
- this.mFilePathAccesses = normalizeAccessesPerDay(mFilePathAccesses, timeInterval);
- this.mSecondaryStorageAccesses =
- normalizeAccessesPerDay(mSecondaryStorageAccesses, timeInterval);
- return this;
- }
-
- StatsEvent toStatsEvent() {
- return StatsEvent.newBuilder()
- .setAtomId(GENERAL_EXTERNAL_STORAGE_ACCESS_STATS)
- .writeInt(mUid)
- .writeInt(mTotalAccesses)
- .writeInt(mFilePathAccesses)
- .writeInt(mSecondaryStorageAccesses)
- .writeByteArray(getMimeTypesAsProto().getBytes())
- .build();
- }
-
- private ProtoOutputStream getMimeTypesAsProto() {
- ProtoOutputStream proto = new ProtoOutputStream();
- for (int i = 0; i < mMimeTypes.size(); i++) {
- String mime = mMimeTypes.valueAt(i);
- proto.write(/*fieldId*/ProtoOutputStream.FIELD_TYPE_STRING
- | ProtoOutputStream.FIELD_COUNT_REPEATED
- | 1,
- mime);
- }
- return proto;
- }
-
- private static int normalizeAccessesPerDay(int value, long interval) {
- if (interval <= 0) {
- return -1;
- }
-
- double multiplier = Double.valueOf(TimeUnit.DAYS.toMillis(1)) / interval;
- double normalizedValue = value * multiplier;
- return Double.valueOf(normalizedValue).intValue();
- }
-
- @VisibleForTesting
- int getUid() {
- return mUid;
- }
- }
-}
diff --git a/src/com/android/providers/media/metrics/TranscodeMetrics.java b/src/com/android/providers/media/metrics/TranscodeMetrics.java
deleted file mode 100644
index 4c4e1d8..0000000
--- a/src/com/android/providers/media/metrics/TranscodeMetrics.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.metrics;
-
-import static com.android.providers.media.MediaProviderStatsLog.TRANSCODING_DATA;
-
-import android.app.StatsManager;
-import android.util.StatsEvent;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Random;
-
-/**
- * Stores metrics for transcode sessions to be shared with statsd.
- */
-final class TranscodeMetrics {
- private static final List<TranscodingStatsData> TRANSCODING_STATS_DATA = new ArrayList<>();
-
- // PLEASE update these if there's a change in the proto message, per the limit set in
- // StatsEvent#MAX_PULL_PAYLOAD_SIZE
- private static final int STATS_DATA_SAMPLE_LIMIT = 300;
- private static final int STATS_DATA_COUNT_HARD_LIMIT = 500; // for safety
-
- // Total data save requests we've received for one statsd pull cycle.
- // This can be greater than TRANSCODING_STATS_DATA.size() since we might not add all the
- // incoming data because of the hard limit on the size.
- private static int sTotalStatsDataCount = 0;
-
- static List<StatsEvent> pullStatsEvents() {
- synchronized (TRANSCODING_STATS_DATA) {
- if (TRANSCODING_STATS_DATA.size() > STATS_DATA_SAMPLE_LIMIT) {
- doRandomSampling();
- }
-
- List<StatsEvent> result = getStatsEvents();
- resetStatsData();
- return result;
- }
- }
-
- private static List<StatsEvent> getStatsEvents() {
- synchronized (TRANSCODING_STATS_DATA) {
- List<StatsEvent> result = new ArrayList<>();
- StatsEvent event;
- int dataCountToFill = Math.min(TRANSCODING_STATS_DATA.size(), STATS_DATA_SAMPLE_LIMIT);
- for (int i = 0; i < dataCountToFill; ++i) {
- TranscodingStatsData statsData = TRANSCODING_STATS_DATA.get(i);
- event = StatsEvent.newBuilder().setAtomId(TRANSCODING_DATA)
- .writeString(statsData.mRequestorPackage)
- .writeInt(statsData.mAccessType)
- .writeLong(statsData.mFileSizeBytes)
- .writeInt(statsData.mTranscodeResult)
- .writeLong(statsData.mTranscodeDurationMillis)
- .writeLong(statsData.mFileDurationMillis)
- .writeLong(statsData.mFrameRate)
- .writeInt(statsData.mAccessReason).build();
-
- result.add(event);
- }
- return result;
- }
- }
-
- /**
- * The random samples would get collected in the first {@code STATS_DATA_SAMPLE_LIMIT} positions
- * inside {@code TRANSCODING_STATS_DATA}
- */
- private static void doRandomSampling() {
- Random random = new Random(System.currentTimeMillis());
-
- synchronized (TRANSCODING_STATS_DATA) {
- for (int i = 0; i < STATS_DATA_SAMPLE_LIMIT; ++i) {
- int randomIndex = random.nextInt(TRANSCODING_STATS_DATA.size() - i /* bound */)
- + i;
- Collections.swap(TRANSCODING_STATS_DATA, i, randomIndex);
- }
- }
- }
-
- @VisibleForTesting
- static void resetStatsData() {
- synchronized (TRANSCODING_STATS_DATA) {
- TRANSCODING_STATS_DATA.clear();
- sTotalStatsDataCount = 0;
- }
- }
-
- /** Saves the statsd data that'd eventually be shared in the pull callback. */
- @VisibleForTesting
- static void saveStatsData(TranscodingStatsData transcodingStatsData) {
- checkAndLimitStatsDataSizeAfterAddition(transcodingStatsData);
- }
-
- private static void checkAndLimitStatsDataSizeAfterAddition(
- TranscodingStatsData transcodingStatsData) {
- synchronized (TRANSCODING_STATS_DATA) {
- ++sTotalStatsDataCount;
-
- if (TRANSCODING_STATS_DATA.size() < STATS_DATA_COUNT_HARD_LIMIT) {
- TRANSCODING_STATS_DATA.add(transcodingStatsData);
- return;
- }
-
- // Depending on how much transcoding we are doing, we might end up accumulating a lot of
- // data by the time statsd comes back with the pull callback.
- // We don't want to just keep growing our memory usage.
- // So we simply randomly choose an element to remove with equal likeliness.
- Random random = new Random(System.currentTimeMillis());
- int replaceIndex = random.nextInt(sTotalStatsDataCount /* bound */);
-
- if (replaceIndex < STATS_DATA_COUNT_HARD_LIMIT) {
- TRANSCODING_STATS_DATA.set(replaceIndex, transcodingStatsData);
- }
- }
- }
-
- @VisibleForTesting
- static int getSavedStatsDataCount() {
- return TRANSCODING_STATS_DATA.size();
- }
-
- @VisibleForTesting
- static int getTotalStatsDataCount() {
- return sTotalStatsDataCount;
- }
-
- @VisibleForTesting
- static int getStatsDataCountHardLimit() {
- return STATS_DATA_COUNT_HARD_LIMIT;
- }
-
- @VisibleForTesting
- static int getStatsDataSampleLimit() {
- return STATS_DATA_SAMPLE_LIMIT;
- }
-
- /** This is the data to populate the proto shared with statsd. */
- static final class TranscodingStatsData {
- private final String mRequestorPackage;
- private final short mAccessType;
- private final long mFileSizeBytes;
- private final short mTranscodeResult;
- private final long mTranscodeDurationMillis;
- private final long mFileDurationMillis;
- private final long mFrameRate;
- private final short mAccessReason;
-
- TranscodingStatsData(String requestorPackage, int accessType, long fileSizeBytes,
- int transcodeResult, long transcodeDurationMillis,
- long videoDurationMillis, long frameRate, short transcodeReason) {
- mRequestorPackage = requestorPackage;
- mAccessType = (short) accessType;
- mFileSizeBytes = fileSizeBytes;
- mTranscodeResult = (short) transcodeResult;
- mTranscodeDurationMillis = transcodeDurationMillis;
- mFileDurationMillis = videoDurationMillis;
- mFrameRate = frameRate;
- mAccessReason = transcodeReason;
- }
- }
-}
diff --git a/src/com/android/providers/media/photopicker/PhotoPickerActivity.java b/src/com/android/providers/media/photopicker/PhotoPickerActivity.java
deleted file mode 100644
index 601514d..0000000
--- a/src/com/android/providers/media/photopicker/PhotoPickerActivity.java
+++ /dev/null
@@ -1,478 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker;
-
-import static com.android.providers.media.photopicker.data.PickerResult.getPickerResponseIntent;
-import static com.android.providers.media.photopicker.util.LayoutModeUtils.MODE_PHOTOS_TAB;
-
-import android.annotation.IntDef;
-import android.app.Activity;
-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.os.SystemProperties;
-import android.provider.MediaStore;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
-import android.view.WindowInsetsController;
-import android.view.WindowManager;
-
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar;
-import androidx.lifecycle.ViewModelProvider;
-
-import com.android.providers.media.R;
-import com.android.providers.media.photopicker.data.model.Category;
-import com.android.providers.media.photopicker.data.model.Item;
-import com.android.providers.media.photopicker.ui.AlbumsTabFragment;
-import com.android.providers.media.photopicker.ui.PhotosTabFragment;
-import com.android.providers.media.photopicker.ui.PreviewFragment;
-import com.android.providers.media.photopicker.util.LayoutModeUtils;
-import com.android.providers.media.photopicker.viewmodel.PickerViewModel;
-
-import com.google.android.material.bottomsheet.BottomSheetBehavior;
-import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback;
-import com.google.android.material.chip.Chip;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-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 AppCompatActivity {
-
- private static final String TAG = "PhotoPickerActivity";
- private static final String EXTRA_TAB_CHIP_TYPE = "tab_chip_type";
- private static final int TAB_CHIP_TYPE_PHOTOS = 0;
- private static final int TAB_CHIP_TYPE_ALBUMS = 1;
- private int mToolbarHeight = 0;
-
- @IntDef(prefix = { "TAB_CHIP_TYPE" }, value = {
- TAB_CHIP_TYPE_PHOTOS,
- TAB_CHIP_TYPE_ALBUMS
- })
- @Retention(RetentionPolicy.SOURCE)
- @interface TabChipType {}
-
- private PickerViewModel mPickerViewModel;
- private ViewGroup mTabChipContainer;
- private Chip mPhotosTabChip;
- private Chip mAlbumsTabChip;
- @TabChipType
- private int mSelectedTabChipType;
- private BottomSheetBehavior mBottomSheetBehavior;
- private View mBottomSheetView;
- private View mFragmentContainerView;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_photo_picker);
-
- final Toolbar toolbar = findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
-
- final int[] attrs = new int[] {R.attr.actionBarSize};
- final TypedArray ta = obtainStyledAttributes(attrs);
- // Save toolbar height so that we can use it as padding for FragmentContainerView
- mToolbarHeight = ta.getDimensionPixelSize(0, -1);
- ta.recycle();
-
- mPickerViewModel = new ViewModelProvider(this).get(PickerViewModel.class);
- try {
- mPickerViewModel.parseValuesFromIntent(getIntent());
- } catch (IllegalArgumentException e) {
- Log.w(TAG, "Finish activity due to: " + e);
- setCancelledResultAndFinishSelf();
- }
-
- mTabChipContainer = findViewById(R.id.chip_container);
- initTabChips();
- initBottomSheetBehavior();
- restoreState(savedInstanceState);
-
- // 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);
- }
-
- @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);
- state.putInt(EXTRA_TAB_CHIP_TYPE, mSelectedTabChipType);
- saveBottomSheetState();
- }
-
- private void restoreState(Bundle savedInstanceState) {
- if (savedInstanceState != null) {
- final int tabChipType = savedInstanceState.getInt(EXTRA_TAB_CHIP_TYPE,
- TAB_CHIP_TYPE_PHOTOS);
- mSelectedTabChipType = tabChipType;
- if (tabChipType == TAB_CHIP_TYPE_PHOTOS) {
- if (PreviewFragment.get(getSupportFragmentManager()) == null) {
- onTabChipClick(mPhotosTabChip);
- } else {
- // PreviewFragment is shown
- mPhotosTabChip.setSelected(true);
- }
- } else { // CHIP_TYPE_ALBUMS
- if (PhotosTabFragment.get(getSupportFragmentManager()) == null) {
- onTabChipClick(mAlbumsTabChip);
- } else {
- // PreviewFragment or PhotosTabFragment with category is shown
- mAlbumsTabChip.setSelected(true);
- }
- }
- restoreBottomSheetState();
- } else {
- // This is the first launch, set the default behavior. Hide the title, show the chips
- // and show the PhotosTabFragment
- updateCommonLayouts(MODE_PHOTOS_TAB, /* title */ "");
- onTabChipClick(mPhotosTabChip);
- saveBottomSheetState();
- }
- }
-
- private static Chip generateTabChip(LayoutInflater inflater, ViewGroup parent, String title) {
- final Chip chip = (Chip) inflater.inflate(R.layout.picker_chip_tab_header, parent, false);
- chip.setText(title);
- return chip;
- }
-
- private void initTabChips() {
- initPhotosTabChip();
- initAlbumsTabChip();
- }
-
- private void initBottomSheetBehavior() {
- mBottomSheetView = findViewById(R.id.bottom_sheet);
- mBottomSheetBehavior = BottomSheetBehavior.from(mBottomSheetView);
- initStateForBottomSheet();
-
- mBottomSheetBehavior.addBottomSheetCallback(bottomSheetCallBack());
- setRoundedCornersForBottomSheet();
- }
-
- private BottomSheetCallback bottomSheetCallBack() {
- return new BottomSheetCallback() {
- @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) {
- }
- };
- }
-
- 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 (!mPickerViewModel.canSelectMultiple() && !isOrientationLandscape()) {
- final WindowManager windowManager = getSystemService(WindowManager.class);
- final Rect displayBounds = windowManager.getCurrentWindowMetrics().getBounds();
- final int peekHeight = (int) (displayBounds.height() * 0.60);
- mBottomSheetBehavior.setPeekHeight(peekHeight);
- mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
- } else {
- mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
- mBottomSheetBehavior.setSkipCollapsed(true);
- }
- }
-
- 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;
- }
-
- private void initPhotosTabChip() {
- if (mPhotosTabChip == null) {
- mPhotosTabChip = generateTabChip(getLayoutInflater(), mTabChipContainer,
- getString(R.string.picker_photos));
- mTabChipContainer.addView(mPhotosTabChip);
- mPhotosTabChip.setOnClickListener(this::onTabChipClick);
- mPhotosTabChip.setTag(TAB_CHIP_TYPE_PHOTOS);
- }
- }
-
- private void initAlbumsTabChip() {
- if (mAlbumsTabChip == null) {
- mAlbumsTabChip = generateTabChip(getLayoutInflater(), mTabChipContainer,
- getString(R.string.picker_albums));
- mTabChipContainer.addView(mAlbumsTabChip);
- mAlbumsTabChip.setOnClickListener(this::onTabChipClick);
- mAlbumsTabChip.setTag(TAB_CHIP_TYPE_ALBUMS);
- }
- }
-
- private void onTabChipClick(@NonNull View view) {
- final int chipType = (int) view.getTag();
- mSelectedTabChipType = chipType;
-
- // Check whether the tabChip is already selected or not. If it is selected, do nothing
- if (view.isSelected()) {
- return;
- }
-
- if (chipType == TAB_CHIP_TYPE_PHOTOS) {
- mPhotosTabChip.setSelected(true);
- mAlbumsTabChip.setSelected(false);
- PhotosTabFragment.show(getSupportFragmentManager(), Category.getDefaultCategory());
- } else { // CHIP_TYPE_ALBUMS
- mPhotosTabChip.setSelected(false);
- mAlbumsTabChip.setSelected(true);
- AlbumsTabFragment.show(getSupportFragmentManager());
- }
- }
-
- public void setResultAndFinishSelf() {
- final List<Item> selectedItemList = new ArrayList<>(
- mPickerViewModel.getSelectedItems().getValue().values());
- // "persist.sys.photopicker.usepickeruri" property is used to indicate if picker uris should
- // be returned for all intent actions.
- // TODO(b/168001592): Remove this system property when intent-filter for ACTION_GET_CONTENT
- // is removed or when we don't have to send redactedUris any more.
- final boolean usePickerUriByDefault =
- SystemProperties.getBoolean("persist.sys.photopicker.usepickeruri", false);
- final boolean shouldReturnPickerUris = usePickerUriByDefault ||
- MediaStore.ACTION_PICK_IMAGES.equals(getIntent().getAction());
- setResult(Activity.RESULT_OK, getPickerResponseIntent(this, selectedItemList,
- shouldReturnPickerUris));
- finish();
- }
-
- private void setCancelledResultAndFinishSelf() {
- setResult(Activity.RESULT_CANCELED);
- finish();
- }
-
- /**
- * 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);
- }
-
- private void updateTitle(String title) {
- setTitle(title);
- }
-
- /**
- * Updates the icons and show/hide the tab chips with {@code shouldShowTabChips}.
- *
- * @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 shouldShowTabChips = mode.shouldShowTabChips;
- // 1. Set the tabChip visibility
- mTabChipContainer.setVisibility(shouldShowTabChips ? View.VISIBLE : View.GONE);
-
- // 2. Set the toolbar color
- final ColorDrawable toolbarColor;
- if (isPreview && !shouldShowTabChips) {
- // Preview defaults to black color irrespective of if it should show tab chips or not
- toolbarColor = new ColorDrawable(getColor(android.R.color.transparent));
- } else {
- toolbarColor = new ColorDrawable(getColor(R.color.picker_background_color));
- }
- getSupportActionBar().setBackgroundDrawable(toolbarColor);
-
- // 3. Set the toolbar icon.
- final Drawable icon;
- if (shouldShowTabChips) {
- 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 : getColor(R.color.picker_toolbar_icon_color));
- }
- getSupportActionBar().setHomeAsUpIndicator(icon);
- }
-
- /**
- * 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_default_black) :
- getColor(R.color.picker_background_color);
- getWindow().setNavigationBarColor(navigationBarColor);
-
- final int statusBarColor = isPreview ? getColor(R.color.preview_default_black) :
- 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;
-
- if (mode.isPreview) {
- mFragmentContainerView.setPadding(mFragmentContainerView.getPaddingLeft(), 0,
- mFragmentContainerView.getPaddingRight(),
- mFragmentContainerView.getPaddingBottom());
- } else {
- mFragmentContainerView.setPadding(mFragmentContainerView.getPaddingLeft(),
- mToolbarHeight, mFragmentContainerView.getPaddingRight(),
- mFragmentContainerView.getPaddingBottom());
- }
- }
-}
\ No newline at end of file
diff --git a/src/com/android/providers/media/photopicker/PhotoPickerProvider.java b/src/com/android/providers/media/photopicker/PhotoPickerProvider.java
deleted file mode 100644
index b3a89b4..0000000
--- a/src/com/android/providers/media/photopicker/PhotoPickerProvider.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker;
-
-import static android.provider.CloudMediaProviderContract.MediaInfo;
-
-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.CloudMediaProviderContract;
-import android.provider.MediaStore;
-
-import com.android.providers.media.MediaProvider;
-import com.android.providers.media.photopicker.data.ExternalDbFacade;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-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(@NonNull String mediaId) {
- return mDbFacade.queryMediaId(Long.parseLong(mediaId));
- }
-
- @Override
- public Cursor onQueryMedia(@Nullable Bundle extras) {
- // TODO(b/190713331): Handle extra_page
- return mDbFacade.queryMediaGeneration(extractGeneration(extras), extractAlbum(extras));
- }
-
- @Override
- public Cursor onQueryDeletedMedia(@Nullable Bundle extras) {
- return mDbFacade.queryDeletedMedia(extractGeneration(extras));
- }
-
- @Override
- public Cursor onQueryAlbums(@Nullable Bundle extras) {
- return mDbFacade.queryAlbums();
- }
-
- @Override
- public AssetFileDescriptor onOpenThumbnail(@NonNull String mediaId, @NonNull Point size,
- @NonNull CancellationSignal signal) throws FileNotFoundException {
- Bundle opts = new Bundle();
- opts.putParcelable(ContentResolver.EXTRA_SIZE, size);
- return mMediaProvider.openTypedAssetFile(fromMediaId(mediaId), "image/*", opts);
- }
-
- @Override
- public ParcelFileDescriptor onOpenMedia(@NonNull String mediaId,
- @NonNull CancellationSignal signal)
- throws FileNotFoundException {
- return mMediaProvider.openFile(fromMediaId(mediaId), "r");
- }
-
- @Override
- public Bundle onGetMediaInfo(@Nullable Bundle extras) {
- // TODO(b/190713331): Handle extra_filter_albums
- Bundle bundle = new Bundle();
- try (Cursor cursor = mDbFacade.getMediaInfo(extractGeneration(extras))) {
- if (cursor.moveToFirst()) {
- int generationIndex = cursor.getColumnIndexOrThrow(MediaInfo.MEDIA_GENERATION);
- int countIndex = cursor.getColumnIndexOrThrow(MediaInfo.MEDIA_COUNT);
-
- bundle.putString(MediaInfo.MEDIA_VERSION, MediaStore.getVersion(getContext()));
- bundle.putLong(MediaInfo.MEDIA_GENERATION, cursor.getLong(generationIndex));
- bundle.putLong(MediaInfo.MEDIA_COUNT, cursor.getLong(countIndex));
- }
- }
- return bundle;
- }
-
- 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_PRIMARY,
- Long.parseLong(mediaId));
- }
-
- private static long extractGeneration(@Nullable Bundle extras) {
- return extras == null ? 0 : extras.getLong(MediaInfo.MEDIA_GENERATION, 0);
- }
-
- private static String extractAlbum(@Nullable Bundle extras) {
- return extras == null ? null : extras.getString(
- CloudMediaProviderContract.EXTRA_FILTER_ALBUM);
- }
-}
diff --git a/src/com/android/providers/media/photopicker/PickerSyncController.java b/src/com/android/providers/media/photopicker/PickerSyncController.java
deleted file mode 100644
index 1ba5f8e..0000000
--- a/src/com/android/providers/media/photopicker/PickerSyncController.java
+++ /dev/null
@@ -1,407 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker;
-
-import static android.provider.CloudMediaProviderContract.MediaColumns;
-import static android.provider.CloudMediaProviderContract.MediaInfo;
-import static com.android.providers.media.PickerUriResolver.getMediaUri;
-import static com.android.providers.media.PickerUriResolver.getDeletedMediaUri;
-import static com.android.providers.media.PickerUriResolver.getMediaInfoUri;
-
-import android.annotation.IntDef;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-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.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.CloudMediaProvider;
-import android.provider.CloudMediaProviderContract;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-
-import com.android.providers.media.photopicker.data.PickerDbFacade;
-import com.android.providers.media.util.BackgroundThread;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * 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";
- private static final String PREFS_NAME = "picker_provider_media_info";
- private static final String PREFS_KEY_CLOUD_PROVIDER = "cloud_provider";
- private static final String PREFS_KEY_CLOUD_PREFIX = "cloud_provider:";
- private static final String PREFS_KEY_LOCAL_PREFIX = "local_provider:";
-
- public static final String LOCAL_PICKER_PROVIDER_AUTHORITY =
- "com.android.providers.media.photopicker";
- private static final long DEFAULT_SYNC_DELAY_MS = 1000;
-
- private static final int H_SYNC_PICKER = 1;
-
- private static final int SYNC_TYPE_NONE = 0;
- private static final int SYNC_TYPE_INCREMENTAL = 1;
- private static final int SYNC_TYPE_FULL = 2;
- private static final int SYNC_TYPE_RESET = 3;
-
- @IntDef(flag = false, prefix = { "SYNC_TYPE_" }, value = {
- SYNC_TYPE_NONE,
- SYNC_TYPE_INCREMENTAL,
- SYNC_TYPE_FULL,
- SYNC_TYPE_RESET,
- })
- @Retention(RetentionPolicy.SOURCE)
- private @interface SyncType {}
-
- private final Object mLock = new Object();
- private final PickerDbFacade mDbFacade;
- private final Context mContext;
- private final SharedPreferences mPrefs;
- private final String mLocalProvider;
- private final long mSyncDelayMs;
- private final PickerHandler mHandler;
-
- // TODO(b/190713331): Listen for package_removed
- private String mCloudProvider;
-
- public PickerSyncController(Context context, PickerDbFacade dbFacade) {
- this(context, dbFacade, LOCAL_PICKER_PROVIDER_AUTHORITY, DEFAULT_SYNC_DELAY_MS);
- }
-
- @VisibleForTesting
- PickerSyncController(Context context, PickerDbFacade dbFacade,
- String localProvider, long syncDelayMs) {
- mContext = context;
- mPrefs = mContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
- mDbFacade = dbFacade;
- mLocalProvider = localProvider;
- mCloudProvider = mPrefs.getString(PREFS_KEY_CLOUD_PROVIDER, /* default */ null);
- mHandler = new PickerHandler(BackgroundThread.get().getLooper());
- mSyncDelayMs = syncDelayMs;
- }
-
- private class PickerHandler extends Handler {
- public PickerHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case H_SYNC_PICKER: {
- syncPicker();
- break;
- }
- default:
- Log.w(TAG, "Unexpected handler message: " + msg);
- }
- }
- }
-
- /**
- * Syncs the local and currently enabled cloud {@link CloudMediaProvider} instances
- */
- public void syncPicker() {
- syncAndCommitProvider(mLocalProvider);
-
- synchronized (mLock) {
- syncAndCommitProvider(mCloudProvider);
-
- // Set the latest cloud provider on the facade
- mDbFacade.setCloudProvider(mCloudProvider);
- }
- }
-
- private void syncAndCommitProvider(@Nullable String authority) {
- if (authority == null) {
- // Only cloud authority can be null
- mDbFacade.resetMedia(authority);
- return;
- }
-
- final ContentResolver resolver = mContext.getContentResolver();
- final Bundle cachedMediaInfo = getCachedMediaInfo(authority);
- final Bundle latestMediaInfo = getLatestMediaInfo(authority);
-
- // Check what kind of sync is required, if any
- int result = checkSync(authority, latestMediaInfo, cachedMediaInfo);
- if (result == SYNC_TYPE_NONE) {
- return;
- }
-
- if (result == SYNC_TYPE_RESET) {
- // Odd! Can only happen if cloud provider gave us unexpected MediaInfo
- // We reset the cloud media in the picker db
- mDbFacade.resetMedia(authority);
-
- // And clear our cached MediaInfo, so that whenever the provider recovers,
- // we force a full sync
- clearCachedCloudMediaInfo(authority);
- return;
- }
-
- // Sync provider |authority| into picker db
- syncProvider(authority, cachedMediaInfo, result == SYNC_TYPE_FULL);
-
- // Commit sync position
- cacheMediaInfo(authority, latestMediaInfo);
-
- // TODO(b/190713331): Confirm that no more sync required?
- }
-
- /**
- * Returns the supported cloud {@link CloudMediaProvider} authorities.
- */
- public List<String> getSupportedCloudProviders() {
- final List<String> 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)) {
- result.add(providerInfo.authority);
- }
- }
-
- 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
- * 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(mCloudProvider, authority)) {
- Log.w(TAG, "Cloud provider already set: " + authority);
- return true;
- }
- }
-
- if (authority == null || getSupportedCloudProviders().contains(authority)) {
- synchronized (mLock) {
- mCloudProvider = authority;
-
- // This will *clear* the cloud provider on the mDbFacade and prevents any queries
- // from seeing the old or new cloud media until a sync where the cloud provider
- // on the facade will be set again
- clearCachedCloudMediaInfo(authority);
- }
-
- return true;
- }
-
- Log.w(TAG, "Cloud provider not supported: " + authority);
- return false;
- }
-
- public String getCloudProvider() {
- return mCloudProvider;
- }
-
- public String getLocalProvider() {
- return mLocalProvider;
- }
-
- /**
- * 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() {
- mHandler.removeMessages(H_SYNC_PICKER);
- mHandler.sendEmptyMessageDelayed(H_SYNC_PICKER, mSyncDelayMs);
- }
-
- // TODO(b/190713331): Check extra_pages and extra_honored_args
- private void syncProvider(String authority, Bundle cachedMediaInfo, boolean fullSync) {
- int result = 0;
- if (fullSync) {
- // Reset media
- result = mDbFacade.resetMedia(authority);
- Log.i(TAG, "Reset sync. Authority: " + authority + ". Result count: " + result);
-
- // Sync media
- try (Cursor cursor = query(getMediaUri(authority), /* extras */ null)) {
- result = mDbFacade.addMedia(cursor, authority);
- Log.i(TAG, "Full sync. Authority: " + authority + ". Result count: " + result
- + ". Cursor count: " + cursor.getCount());
- }
- } else {
- // Sync media
- final Bundle queryArgs = new Bundle();
- final long cachedGeneration = cachedMediaInfo.getLong(MediaInfo.MEDIA_GENERATION);
- queryArgs.putLong(MediaInfo.MEDIA_GENERATION, cachedGeneration);
-
- try (Cursor cursor = query(getMediaUri(authority), queryArgs)) {
- result = mDbFacade.addMedia(cursor, authority);
- Log.i(TAG, "Incremental sync. Authority: " + authority + ". Result count: "
- + result + ". Cursor count: " + cursor.getCount());
- }
-
- // Sync deleted_media
- final Bundle queryDeletedArgs = new Bundle();
- queryDeletedArgs.putLong(MediaInfo.MEDIA_GENERATION, cachedGeneration);
-
- try (Cursor cursor = query(getDeletedMediaUri(authority), queryDeletedArgs)) {
- final int idIndex = cursor.getColumnIndex(MediaColumns.ID);
- result = mDbFacade.removeMedia(cursor, idIndex, authority);
- Log.i(TAG, "Incremental deleted sync. Authority: " + authority + ". Result count: "
- + result + ". Cursor count: " + cursor.getCount());
- }
- }
- }
-
- private void cacheMediaInfo(String authority, Bundle bundle) {
- final SharedPreferences.Editor editor = mPrefs.edit();
-
- final String version = bundle.getString(MediaInfo.MEDIA_VERSION);
- final long generation = bundle.getLong(MediaInfo.MEDIA_GENERATION);
- final long count = bundle.getLong(MediaInfo.MEDIA_COUNT);
-
- editor.putString(getPrefsKey(authority, MediaInfo.MEDIA_VERSION), version);
- editor.putLong(getPrefsKey(authority, MediaInfo.MEDIA_GENERATION), generation);
- editor.putLong(getPrefsKey(authority, MediaInfo.MEDIA_COUNT), count);
-
- editor.commit();
- }
-
- private void clearCachedCloudMediaInfo(String authority) {
- if (isLocal(authority)) {
- // We never expect to clear local media info because we neither switch local providers
- // nor expect incorrect data from the local provider since we are bundled together
- return;
- }
-
- // Disable cloud provider queries on the db until next sync
- mDbFacade.setCloudProvider(null);
-
- final SharedPreferences.Editor editor = mPrefs.edit();
-
- editor.remove(getPrefsKey(authority, MediaInfo.MEDIA_VERSION));
- editor.remove(getPrefsKey(authority, MediaInfo.MEDIA_GENERATION));
- editor.remove(getPrefsKey(authority, MediaInfo.MEDIA_COUNT));
-
- if (authority == null) {
- editor.remove(PREFS_KEY_CLOUD_PROVIDER);
- } else {
- editor.putString(PREFS_KEY_CLOUD_PROVIDER, authority);
- }
-
- editor.commit();
- }
-
- private Bundle getCachedMediaInfo(String authority) {
- final Bundle bundle = new Bundle();
-
- final String version = mPrefs.getString(getPrefsKey(authority, MediaInfo.MEDIA_VERSION),
- /* default */ null);
- final long generation = mPrefs.getLong(getPrefsKey(authority, MediaInfo.MEDIA_GENERATION),
- /* default */ -1);
- final long count = mPrefs.getLong(getPrefsKey(authority, MediaInfo.MEDIA_COUNT),
- /* default */ -1);
-
- bundle.putString(MediaInfo.MEDIA_VERSION, version);
- bundle.putLong(MediaInfo.MEDIA_GENERATION, generation);
- bundle.putLong(MediaInfo.MEDIA_COUNT, count);
-
- return bundle;
- }
-
- private Bundle getLatestMediaInfo(String authority) {
- return mContext.getContentResolver().call(getMediaInfoUri(authority),
- CloudMediaProviderContract.METHOD_GET_MEDIA_INFO, /* arg */ null,
- /* extras */ null);
- }
-
- @SyncType
- private int checkSync(String authority, Bundle latestMediaInfo, Bundle cachedMediaInfo) {
- final String latestVersion = latestMediaInfo.getString(MediaInfo.MEDIA_VERSION);
- final long latestGeneration = latestMediaInfo.getLong(MediaInfo.MEDIA_GENERATION);
- final long latestCount = latestMediaInfo.getLong(MediaInfo.MEDIA_COUNT);
-
- final String cachedVersion = cachedMediaInfo.getString(MediaInfo.MEDIA_VERSION);
- final long cachedGeneration = cachedMediaInfo.getLong(MediaInfo.MEDIA_GENERATION);
- final long cachedCount = cachedMediaInfo.getLong(MediaInfo.MEDIA_COUNT);
-
- Log.d(TAG, "checkSync. Authority: " + authority + ". LatestMediaInfo: " + latestMediaInfo
- + ". CachedMediaInfo: " + cachedMediaInfo);
-
- if (TextUtils.isEmpty(latestVersion) || latestGeneration < 0 || latestCount < 0) {
- // If results from |latestMediaInfo| are unexpected, we reset the cloud provider
- Log.w(TAG, "checkSync. Authority: " + authority
- + ". Result: SYNC_TYPE_RESET. Unexpected results: " + latestMediaInfo);
- return SYNC_TYPE_RESET;
- }
-
- if (!Objects.equals(latestVersion, cachedVersion)) {
- Log.d(TAG, "checkSync. Authority: " + authority + ". Result: SYNC_TYPE_FULL");
- return SYNC_TYPE_FULL;
- }
-
- if (cachedGeneration == latestGeneration && cachedCount == latestCount) {
- Log.d(TAG, "checkSync. Authority: " + authority + ". Result: SYNC_TYPE_NONE");
- return SYNC_TYPE_NONE;
- }
-
- Log.d(TAG, "checkSync. Authority: " + authority + ". Result: SYNC_TYPE_INCREMENTAL");
- return SYNC_TYPE_INCREMENTAL;
- }
-
- 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);
- }
-}
diff --git a/src/com/android/providers/media/photopicker/UnreliableVolumeFacade.java b/src/com/android/providers/media/photopicker/UnreliableVolumeFacade.java
deleted file mode 100644
index 5006663..0000000
--- a/src/com/android/providers/media/photopicker/UnreliableVolumeFacade.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker;
-
-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/ExternalDbFacade.java b/src/com/android/providers/media/photopicker/data/ExternalDbFacade.java
deleted file mode 100644
index 27fc905..0000000
--- a/src/com/android/providers/media/photopicker/data/ExternalDbFacade.java
+++ /dev/null
@@ -1,377 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.data;
-
-import static com.android.providers.media.photopicker.util.CursorUtils.getCursorLong;
-import static com.android.providers.media.photopicker.util.CursorUtils.getCursorString;
-
-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.SQLiteQueryBuilder;
-import android.os.Environment;
-import android.provider.CloudMediaProviderContract;
-import android.provider.MediaStore.Files.FileColumns;
-import android.provider.MediaStore.MediaColumns;
-import android.provider.MediaStore;
-import android.util.Log;
-
-import androidx.annotation.VisibleForTesting;
-
-import com.android.providers.media.DatabaseHelper;
-import com.android.providers.media.photopicker.data.model.Category;
-import com.android.providers.media.util.MimeUtils;
-
-import java.util.ArrayList;
-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";
-
- private static final String TABLE_DELETED_MEDIA = "deleted_media";
- private 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_MS,
- MediaColumns.SIZE + " AS " + CloudMediaProviderContract.MediaColumns.SIZE_BYTES,
- MediaColumns.MIME_TYPE + " AS " + CloudMediaProviderContract.MediaColumns.MIME_TYPE,
- MediaColumns.DURATION + " AS " + CloudMediaProviderContract.MediaColumns.DURATION_MS,
- MediaColumns.IS_FAVORITE + " AS " + CloudMediaProviderContract.MediaColumns.IS_FAVORITE
- };
- private static final String[] PROJECTION_MEDIA_INFO = new String[] {
- "COUNT(" + MediaColumns.GENERATION_MODIFIED + ") AS "
- + CloudMediaProviderContract.MediaInfo.MEDIA_COUNT,
- "MAX(" + MediaColumns.GENERATION_MODIFIED + ") AS "
- + CloudMediaProviderContract.MediaInfo.MEDIA_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_MS,
- MediaColumns._ID + " AS " + CloudMediaProviderContract.AlbumColumns.MEDIA_COVER_ID,
- };
-
- private static final String[] PROJECTION_ALBUM_CURSOR = new String[] {
- CloudMediaProviderContract.AlbumColumns.ID,
- CloudMediaProviderContract.AlbumColumns.DATE_TAKEN_MS,
- CloudMediaProviderContract.AlbumColumns.DISPLAY_NAME,
- CloudMediaProviderContract.AlbumColumns.MEDIA_COUNT,
- 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_FAVORITE = MediaColumns.IS_FAVORITE + " = 1";
- 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_ID = MediaColumns._ID + " = ?";
- private static final String WHERE_GREATER_GENERATION =
- MediaColumns.GENERATION_MODIFIED + " > ?";
- private static final String WHERE_RELATIVE_PATH = MediaStore.MediaColumns.RELATIVE_PATH
- + " LIKE ?";
-
- // TODO(b/196071169): Confirm if everything in DIRECTORY_SCREENSHOTS should be included
- // regardless of parent path
- public static final String RELATIVE_PATH_SCREENSHOTS = Environment.DIRECTORY_PICTURES + "/"
- + Environment.DIRECTORY_SCREENSHOTS;
- public static final String RELATIVE_PATH_CAMERA = Environment.DIRECTORY_DCIM + "/Camera";
-
- private final DatabaseHelper mDatabaseHelper;
- private final Context mContext;
-
- public ExternalDbFacade(Context context, DatabaseHelper databaseHelper) {
- mContext = context;
- mDatabaseHelper = databaseHelper;
- }
-
- /**
- * 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);
- }
-
- /**
- * 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) {
- 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;
- }
-
- // Do nothing, not an interesting change for deleted_media
- return false;
- }
-
- /**
- * 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[] selectArg = new String[] {String.valueOf(oldId)};
-
- return qb.update(db, cv, select, selectArg) > 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) {
- return mDatabaseHelper.runWithTransaction(db -> {
- SQLiteQueryBuilder qb = createDeletedMediaQueryBuilder();
- String[] projection = new String[] {COLUMN_OLD_ID_AS_ID};
- String select = COLUMN_GENERATION_MODIFIED + " > ?";
- String[] selectArg = new String[] {String.valueOf(generation)};
-
- return qb.query(db, projection, select, selectArg, /* groupBy */ null,
- /* having */ null, /* orderBy */ null);
- });
- }
-
- /**
- * Returns all items from the files table where {@link MediaColumns#GENERATION_MODIFIED}
- * is greater than {@code generation}.
- */
- public Cursor queryMediaGeneration(long generation, String albumId) {
- final List<String> selectArg = new ArrayList<>();
- final String orderBy = CloudMediaProviderContract.MediaColumns.DATE_TAKEN_MS + " DESC";
-
- return mDatabaseHelper.runWithTransaction(db -> {
- SQLiteQueryBuilder qb = createFilesQueryBuilder();
- qb.appendWhereStandalone(WHERE_MEDIA_TYPE);
- qb.appendWhereStandalone(WHERE_NOT_TRASHED);
- qb.appendWhereStandalone(WHERE_NOT_PENDING);
- qb.appendWhereStandalone(WHERE_GREATER_GENERATION);
- selectArg.add(String.valueOf(generation));
-
- appendWhereForAlbum(qb, selectArg, albumId);
-
- return qb.query(db, PROJECTION_MEDIA_COLUMNS, /* select */ null,
- selectArg.toArray(new String[selectArg.size()]), /* groupBy */ null,
- /* having */ null, orderBy);
- });
- }
-
- /** Returns the media item from the files table with row id {@code id}. */
- public Cursor queryMediaId(long id) {
- final String[] selectArg = new String[] {String.valueOf(id)};
-
- return mDatabaseHelper.runWithTransaction(db -> {
- SQLiteQueryBuilder qb = createFilesQueryBuilder();
- qb.appendWhereStandalone(WHERE_MEDIA_TYPE);
- qb.appendWhereStandalone(WHERE_NOT_TRASHED);
- qb.appendWhereStandalone(WHERE_NOT_PENDING);
- qb.appendWhereStandalone(WHERE_ID);
-
- return qb.query(db, PROJECTION_MEDIA_COLUMNS, /* select */ null, selectArg,
- /* groupBy */ null, /* having */ null, /* orderBy */ null);
- });
- }
-
- /**
- * Returns the total count and max {@link MediaColumns#GENERATION_MODIFIED} value
- * of the media items in the files table greater than {@code generation}.
- */
- public Cursor getMediaInfo(long generation) {
- final String[] selectArg = new String[] {String.valueOf(generation)};
-
- return mDatabaseHelper.runWithTransaction(db -> {
- SQLiteQueryBuilder qb = createFilesQueryBuilder();
- qb.appendWhereStandalone(WHERE_MEDIA_TYPE);
- qb.appendWhereStandalone(WHERE_NOT_TRASHED);
- qb.appendWhereStandalone(WHERE_NOT_PENDING);
- qb.appendWhereStandalone(WHERE_GREATER_GENERATION);
-
- return qb.query(db, PROJECTION_MEDIA_INFO, /* select */ null, selectArg,
- /* groupBy */ null, /* having */ null, /* orderBy */ null);
- });
- }
-
- /**
- * Returns the media item categories from the files table.
- * Categories are determined with the {@link Category#CATEGORIES_LIST}.
- * If there are no media items under a category, the category is skipped from the results.
- */
- public Cursor queryAlbums() {
- final MatrixCursor c = new MatrixCursor(PROJECTION_ALBUM_CURSOR);
-
- for (String category: Category.CATEGORIES_LIST) {
- if (Category.CATEGORY_FAVORITES.equals(category)) {
- // TODO(b/196071169): Remove after removing favorites from CATEGORIES_LIST
- continue;
- }
- Cursor cursor = mDatabaseHelper.runWithTransaction(db -> {
- final SQLiteQueryBuilder qb = createFilesQueryBuilder();
- final List<String> selectionArgs = new ArrayList<>();
- appendWhereForAlbum(qb, selectionArgs, category);
-
- 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[] {
- category,
- getCursorString(cursor, CloudMediaProviderContract.AlbumColumns.DATE_TAKEN_MS),
- Category.getCategoryName(mContext, category),
- String.valueOf(count),
- getCursorString(cursor, CloudMediaProviderContract.AlbumColumns.MEDIA_COVER_ID)
- };
-
- c.addRow(projectionValue);
- }
-
- return c;
- }
-
- private static void appendWhereForAlbum(SQLiteQueryBuilder qb, List<String> selectArgs,
- String albumId) {
- if (albumId == null) {
- return;
- }
-
- switch (albumId) {
- case Category.CATEGORY_VIDEOS:
- qb.appendWhereStandalone(WHERE_VIDEO_TYPE);
- return;
- case Category.CATEGORY_CAMERA:
- qb.appendWhereStandalone(WHERE_RELATIVE_PATH);
- selectArgs.add(RELATIVE_PATH_CAMERA);
- return;
- case Category.CATEGORY_SCREENSHOTS:
- qb.appendWhereStandalone(WHERE_RELATIVE_PATH);
- selectArgs.add(RELATIVE_PATH_SCREENSHOTS);
- return;
- case Category.CATEGORY_DOWNLOADS:
- qb.appendWhereStandalone(WHERE_IS_DOWNLOAD);
- return;
- default:
- Log.w(TAG, "No match for album: " + albumId);
- }
- }
-
- private static SQLiteQueryBuilder createDeletedMediaQueryBuilder() {
- SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- qb.setTables(TABLE_DELETED_MEDIA);
-
- return qb;
- }
-
- private static SQLiteQueryBuilder createFilesQueryBuilder() {
- SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- qb.setTables(TABLE_FILES);
-
- return qb;
- }
-}
diff --git a/src/com/android/providers/media/photopicker/data/ItemsProvider.java b/src/com/android/providers/media/photopicker/data/ItemsProvider.java
deleted file mode 100644
index c35ef9b..0000000
--- a/src/com/android/providers/media/photopicker/data/ItemsProvider.java
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.data;
-
-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.database.Cursor;
-import android.database.MatrixCursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.RemoteException;
-import android.provider.MediaStore;
-import android.provider.MediaStore.Files.FileColumns;
-import android.provider.MediaStore.MediaColumns;
-import android.util.Log;
-
-import com.android.providers.media.PickerUriResolver;
-import com.android.providers.media.photopicker.data.model.Category;
-import com.android.providers.media.photopicker.data.model.Category.CategoryColumns;
-import com.android.providers.media.photopicker.data.model.Item.ItemColumns;
-import com.android.providers.media.photopicker.data.model.UserId;
-
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Provides image and video items from {@link MediaStore} collection to the Photo Picker.
- */
-public class ItemsProvider {
-
- private static final String IMAGES_VIDEOS_WHERE_CLAUSE = "( " +
- FileColumns.MEDIA_TYPE + " = " + FileColumns.MEDIA_TYPE_IMAGE + " OR "
- + FileColumns.MEDIA_TYPE + " = " + FileColumns.MEDIA_TYPE_VIDEO + " )";
- 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, {@link Category.CategoryType} are supported.
- * {@code null} defaults to {@link Category#CATEGORY_DEFAULT} which returns
- * items from all categories.
- * @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}
- *
- * @throws IllegalArgumentException thrown if unsupported values for {@code category} is passed.
- * @throws IllegalStateException thrown if unsupported value for {@code userId} is passed.
- *
- */
- @Nullable
- public Cursor getItems(@Nullable @Category.CategoryType String category, int offset,
- int limit, @Nullable String mimeType, @Nullable UserId userId) throws
- IllegalArgumentException, IllegalStateException {
- if (userId == null) {
- userId = UserId.CURRENT_USER;
- }
-
- return getItemsInternal(category, offset, limit, mimeType, userId);
- }
-
- @Nullable
- private Cursor getItemsInternal(@Nullable @Category.CategoryType String category,
- int offset, int limit, @Nullable String mimeType, @NonNull UserId userId) throws
- IllegalArgumentException, IllegalStateException {
- // Validate incoming params
- if (category != null && !Category.isValidCategory(category)) {
- throw new IllegalArgumentException("ItemsProvider does not support the given "
- + "category: " + category);
- }
-
- return query(ItemColumns.PROJECTION, category, mimeType, offset, limit, 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 buildCategoriesCursor(Category.CATEGORIES_LIST, mimeType, userId);
- }
-
- private Cursor buildCategoriesCursor(List<String> categories, @Nullable String mimeType,
- @NonNull UserId userId) {
- MatrixCursor c = new MatrixCursor(CategoryColumns.getAllColumns());
-
- for (String category: categories) {
- String[] categoryRow = getCategoryColumns(category, mimeType, userId);
- if (categoryRow != null) {
- c.addRow(categoryRow);
- }
- }
-
- return c;
- }
-
- private String[] getCategoryColumns(@Category.CategoryType String category,
- @Nullable String mimeType, @NonNull UserId userId) throws IllegalArgumentException,
- IllegalStateException {
- if (!Category.isValidCategory(category)) {
- throw new IllegalArgumentException("Category type not supported");
- }
-
- final String[] projection = new String[] { MediaColumns._ID };
- Cursor c = query(projection, category, mimeType, 0, -1, userId);
- // Send null if the cursor is null or cursor size is empty
- if (c == null || !c.moveToFirst()) {
- return null;
- }
-
- return new String[] {
- category, // category name
- c.getString(0), // coverId
- String.valueOf(c.getCount()), // item count
- category // category type
- };
- }
-
- @Nullable
- private Cursor query(@NonNull String[] projection,
- @Nullable @Category.CategoryType String category, @Nullable String mimeType, int offset,
- int limit, @NonNull UserId userId) throws IllegalStateException {
- String selection = IMAGES_VIDEOS_WHERE_CLAUSE;
- String[] selectionArgs = null;
-
- if (category != null && Category.getWhereClauseForCategory(category) != null) {
- selection += " AND (" + Category.getWhereClauseForCategory(category) + ")";
- }
-
- if (mimeType != null) {
- selection += " AND (" + MediaColumns.MIME_TYPE + " LIKE ? )";
- selectionArgs = new String[] {replaceMatchAnyChar(mimeType)};
- }
-
- if (PickerDbFacade.isPickerDbEnabled() && category == null) {
- // The picker db doesn't yet support categories, so only serve non-category queries
- // from the picker db
- return queryPickerDb(limit, mimeType, userId);
- }
- return queryMediaStore(projection, selection, selectionArgs, offset, limit, userId);
- }
-
- @Nullable
- private Cursor queryMediaStore(@NonNull String[] projection,
- @Nullable String selection, @Nullable String[] selectionArgs, int offset,
- int limit, @NonNull UserId userId) throws IllegalStateException {
-
- if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
- // If external storage is not ready, we can't load any items from MediaStore.
- // This shouldn't happen in real world use case. This may happen in tests because test
- // instrumentation kills the target package before starting the test.
- Log.w(TAG, "Couldn't query items because external storage is not ready");
- return null;
- }
-
- final Uri contentUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL);
- try (ContentProviderClient client = userId.getContentResolver(mContext)
- .acquireUnstableContentProviderClient(MediaStore.AUTHORITY)) {
- Bundle extras = new Bundle();
- extras.putString(ContentResolver.QUERY_ARG_SQL_SELECTION, selection);
- extras.putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS, selectionArgs);
- // DATE_TAKEN is time in milliseconds, whereas DATE_MODIFIED is time in seconds.
- // Sort by DATE_MODIFIED if DATE_TAKEN is NULL
- extras.putString(ContentResolver.QUERY_ARG_SQL_SORT_ORDER,
- "COALESCE(" + MediaColumns.DATE_TAKEN + "," + MediaColumns.DATE_MODIFIED +
- "* 1000) DESC");
- extras.putInt(ContentResolver.QUERY_ARG_OFFSET, offset);
- if (limit != -1) {
- extras.putInt(ContentResolver.QUERY_ARG_LIMIT, limit);
- }
-
- return client.query(contentUri, projection, extras, null);
- } catch (RemoteException e) {
- // Do nothing, return null.
- Log.e(TAG, "RemoteException while querying MediaStore for items with"
- + " selection = " + selection
- + " selectionArgs = " + Arrays.toString(selectionArgs)
- + " limit = " + limit + " offset = " + offset + " userId = " + userId, e);
- return null;
- }
- }
-
- @Nullable
- private Cursor queryPickerDb(int limit, @Nullable String mimeType, @NonNull UserId userId)
- throws IllegalStateException {
- try (ContentProviderClient client = userId.getContentResolver(mContext)
- .acquireUnstableContentProviderClient(MediaStore.AUTHORITY)) {
- final Bundle extras = new Bundle();
- extras.putInt(MediaStore.QUERY_ARG_LIMIT, limit);
- extras.putString(MediaStore.QUERY_ARG_MIME_TYPE, mimeType);
-
- return client.query(PickerUriResolver.PICKER_INTERNAL_URI, /* projection */ null,
- extras, /* cancellationSignal */ null);
- } catch (RemoteException e) {
- // Do nothing, return null.
- Log.e(TAG, "RemoteException while querying picker database for items with"
- + " mimeType filter = " + mimeType
- + " limit = " + limit + " userId = " + userId, e);
- return null;
- }
- }
-
- private static String replaceMatchAnyChar(@NonNull String mimeType) {
- return mimeType.replace('*', '%');
- }
-
- public static Uri getItemsUri(String id, String authority, UserId userId) {
- final Uri uri;
- if (authority == null) {
- uri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL,
- Long.parseLong(id));
- } else {
- // We only have authority after querying the picker db
- uri = PickerUriResolver.getMediaUri(authority).buildUpon().appendPath(id).build();
- }
-
- if (userId.equals(UserId.CURRENT_USER)) {
- return uri;
- } else {
- return ContentProvider.createContentUriForUser(uri, userId.getUserHandle());
- }
- }
-}
diff --git a/src/com/android/providers/media/photopicker/data/PickerDatabaseHelper.java b/src/com/android/providers/media/photopicker/data/PickerDatabaseHelper.java
deleted file mode 100644
index d70bd50..0000000
--- a/src/com/android/providers/media/photopicker/data/PickerDatabaseHelper.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.data;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.util.Log;
-import androidx.annotation.VisibleForTesting;
-
-/**
- * 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 = 1;
- 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);
-
- createLatestSchema(db);
- createLatestIndexes(db);
- }
-
- @Override
- public void onUpgrade(final SQLiteDatabase db, final int oldV, final int newV) {
- Log.v(TAG, "onUpgrade() for " + mName + " from " + oldV + " to " + newV);
- }
-
- @Override
- public void onDowngrade(final SQLiteDatabase db, final int oldV, final int newV) {
- Log.v(TAG, "onDowngrade() for " + mName + " from " + oldV + " to " + newV);
-
- 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),"
- + "size_bytes INTEGER NOT NULL CHECK(size_bytes > 0),"
- + "duration_ms INTEGER CHECK(duration_ms >= 0),"
- + "mime_type TEXT NOT NULL,is_favorite INTEGER,"
- + "CHECK(local_id IS NOT NULL OR cloud_id IS NOT NULL),"
- + "UNIQUE(local_id, is_visible))");
- }
-
- 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)");
- }
-}
diff --git a/src/com/android/providers/media/photopicker/data/PickerDbFacade.java b/src/com/android/providers/media/photopicker/data/PickerDbFacade.java
deleted file mode 100644
index d5e21c9..0000000
--- a/src/com/android/providers/media/photopicker/data/PickerDbFacade.java
+++ /dev/null
@@ -1,690 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.data;
-
-import android.content.ContentValues;
-import android.content.ContentUris;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteConstraintException;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteQueryBuilder;
-import android.net.Uri;
-import android.provider.CloudMediaProviderContract;
-import android.provider.MediaStore.MediaColumns;
-import android.os.Bundle;
-import android.os.SystemProperties;
-import android.util.Log;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-
-import com.android.providers.media.photopicker.PickerSyncController;
-import com.android.providers.media.photopicker.data.model.Item;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 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 final Object mLock = new Object();
- private final SQLiteDatabase mDatabase;
- private final String mLocalProvider;
- private String mCloudProvider;
-
- public PickerDbFacade(Context context) {
- final PickerDatabaseHelper databaseHelper = new PickerDatabaseHelper(context);
- mDatabase = databaseHelper.getWritableDatabase();
- mLocalProvider = PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY;
- }
-
- @VisibleForTesting
- public PickerDbFacade(Context context, String localProvider) {
- final PickerDatabaseHelper databaseHelper = new PickerDatabaseHelper(context);
- mDatabase = databaseHelper.getWritableDatabase();
- mLocalProvider = localProvider;
- }
-
- 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";
-
- @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_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_IS_FAVORITE = "is_favorite";
-
- // 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.
- private static final String PROJECTION_ID = String.format("IFNULL(%s, %s) AS %s", KEY_CLOUD_ID,
- KEY_LOCAL_ID, CloudMediaProviderContract.MediaColumns.ID);
- private static final String PROJECTION_DATE_TAKEN = String.format("%s AS %s", KEY_DATE_TAKEN_MS,
- CloudMediaProviderContract.MediaColumns.DATE_TAKEN_MS);
- private static final String PROJECTION_SIZE = String.format("%s AS %s", KEY_SIZE_BYTES,
- CloudMediaProviderContract.MediaColumns.SIZE_BYTES);
- private static final String PROJECTION_DURATION = String.format("%s AS %s", KEY_DURATION_MS,
- CloudMediaProviderContract.MediaColumns.DURATION_MS);
- private static final String PROJECTION_MIME_TYPE = String.format("%s AS %s", KEY_MIME_TYPE,
- CloudMediaProviderContract.MediaColumns.MIME_TYPE);
-
- 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_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);
-
- // 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();
-
- {
- CONTENT_VALUE_VISIBLE.put(KEY_IS_VISIBLE, 1);
- CONTENT_VALUE_HIDDEN.putNull(KEY_IS_VISIBLE);
- }
-
- /*
- * Add media belonging to {@code authority} into the picker db.
- *
- * @param cursor containing items to add
- * @param authority to add media from
- * @return the number of {@code cursor} items that were inserted/updated in the picker db
- */
- public int addMedia(Cursor cursor, String authority) {
- final boolean isLocal = isLocal(authority);
- final SQLiteQueryBuilder qb = isLocal ? QB_MATCH_LOCAL_ONLY : QB_MATCH_CLOUD;
- int counter = 0;
-
- mDatabase.beginTransaction();
- try {
- 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++;
- continue;
- }
- }
- mDatabase.setTransactionSuccessful();
- } finally {
- mDatabase.endTransaction();
- }
-
- return counter;
- }
-
- /*
- * Remove media belonging to {@code authority} from the picker db.
- *
- * @param cursor containing items to remove
- * @param idIndex column index in {@code cursor} of the local id
- * @param authority to remove media from
- * @return the number of {@code cursor} items that were deleted/updated in the picker db
- */
- public int removeMedia(Cursor cursor, int idIndex, String authority) {
- final boolean isLocal = isLocal(authority);
- final SQLiteQueryBuilder qb = isLocal ? QB_MATCH_LOCAL_ONLY : QB_MATCH_CLOUD;
-
- int counter = 0;
-
- mDatabase.beginTransaction();
- try {
- 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 String deleteArgs[] = {cursor.getString(idIndex)};
- if (qb.delete(mDatabase, /* selection */ null, deleteArgs) > 0) {
- counter++;
- }
-
- promoteCloudMediaToVisible(localId);
- }
-
- mDatabase.setTransactionSuccessful();
- } finally {
- mDatabase.endTransaction();
- }
-
- return counter;
- }
-
- /**
- * Clear local media or all cloud media from the picker db. If {@code authority} is
- * null, we also clear all cloud media.
- *
- * @param authority to determine whether local or cloud media should be cleared
- * @return the number of items deleted
- */
- public int resetMedia(String authority) {
- final boolean isLocal = isLocal(authority);
- final SQLiteQueryBuilder qb = createMediaQueryBuilder();
-
- if (isLocal) {
- qb.appendWhereStandalone(WHERE_NULL_CLOUD_ID);
- } else {
- qb.appendWhereStandalone(WHERE_NOT_NULL_CLOUD_ID);
- }
-
- int counter = 0;
-
- mDatabase.beginTransaction();
- try {
- counter = qb.delete(mDatabase, /* 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.
- mDatabase.updateWithOnConflict(TABLE_MEDIA, CONTENT_VALUE_VISIBLE, /* where */ null,
- /* whereClause */ null, SQLiteDatabase.CONFLICT_IGNORE);
- }
-
- mDatabase.setTransactionSuccessful();
- } finally {
- mDatabase.endTransaction();
- }
-
- return counter;
- }
-
- /**
- * 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;
- }
- }
-
- private boolean isLocal(String authority) {
- return mLocalProvider.equals(authority);
- }
-
- private int insertMedia(ContentValues values) {
- try {
- if (QB_MATCH_ALL.insert(mDatabase, 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 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;
- }
- }
-
- 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 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 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 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 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 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);
- }
-
- /** Filter for {@link #queryMedia} to modify returned results */
- public static class QueryFilter {
- private final int limit;
- private final long dateTakenBeforeMs;
- private final long dateTakenAfterMs;
- private final long id;
- private final long sizeBytes;
- private final String mimeType;
- private final boolean isFavorite;
-
- private QueryFilter(int limit, long dateTakenBeforeMs, long dateTakenAfterMs, long id,
- long sizeBytes, String mimeType, boolean isFavorite) {
- this.limit = limit;
- this.dateTakenBeforeMs = dateTakenBeforeMs;
- this.dateTakenAfterMs = dateTakenAfterMs;
- this.id = id;
- this.sizeBytes = sizeBytes;
- this.mimeType = mimeType;
- this.isFavorite = isFavorite;
- }
- }
-
- /** 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 long sizeBytes = LONG_DEFAULT;
- private String mimeType = STRING_DEFAULT;
- private boolean isFavorite = 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 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;
- }
-
- public QueryFilter build() {
- return new QueryFilter(limit, dateTakenBeforeMs, dateTakenAfterMs, id, sizeBytes,
- mimeType, isFavorite);
- }
- }
-
- /*
- * Returns sorted and deduped cloud and local media items from the picker db.
- *
- * Returns a {@link Cursor} containing picker db media rows sorted in reverse chronological
- * order, i.e. newest first, up to a maximum of {@code limit}.
- *
- * The results can be filtered with {@code query}.
- */
- public Cursor queryMedia(QueryFilter query) {
- final SQLiteQueryBuilder qb = createVisibleMediaQueryBuilder();
- final String[] selectionArgs = buildSelectionArgs(qb, query);
-
- return queryMedia(qb, selectionArgs, query.limit);
- }
-
- public static boolean isPickerDbEnabled() {
- return SystemProperties.getBoolean("sys.photopicker.pickerdb.enabled", false);
- }
-
- private Cursor queryMedia(SQLiteQueryBuilder qb, String[] selectionArgs, int limit) {
- // Use the <table>.<column> form to order _id to avoid ordering against the projection '_id'
- final String orderBy = "date_taken_ms DESC," + TABLE_MEDIA + "._id DESC";
- 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) {
- // If cloud provider is null, skip all cloud items in the picker db
- qb.appendWhereStandalone(WHERE_NULL_CLOUD_ID);
- }
-
- final String[] projection = new String[] {
- getProjectionAuthorityLocked(),
- PROJECTION_ID,
- PROJECTION_DATE_TAKEN,
- PROJECTION_SIZE,
- PROJECTION_DURATION,
- PROJECTION_MIME_TYPE
- };
-
- return qb.query(mDatabase, projection, /* selection */ null, selectionArgs,
- /* groupBy */ null, /* having */ null, orderBy, limitStr);
- }
- }
-
- private String getProjectionAuthorityLocked() {
- if (mCloudProvider == null) {
- return String.format("'%s' AS %s", mLocalProvider,
- CloudMediaProviderContract.MediaColumns.AUTHORITY);
- }
- return String.format("IIF(%s IS NULL, '%s', '%s') AS %s",
- KEY_CLOUD_ID, mLocalProvider, mCloudProvider,
- CloudMediaProviderContract.MediaColumns.AUTHORITY);
- }
-
- private static ContentValues cursorToContentValue(Cursor cursor, boolean isLocal) {
- final ContentValues values = new ContentValues();
- values.put(KEY_IS_VISIBLE, 1);
-
- 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_MS:
- values.put(KEY_DATE_TAKEN_MS, 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.DURATION_MS:
- values.put(KEY_DURATION_MS, cursor.getLong(index));
- break;
- case CloudMediaProviderContract.MediaColumns.IS_FAVORITE:
- values.put(KEY_IS_FAVORITE, cursor.getInt(index));
- break;
- default:
- Log.w(TAG, "Unexpected cursor key: " + key);
- }
- }
-
- return values;
- }
-
- private static String[] buildSelectionArgs(SQLiteQueryBuilder qb, QueryFilter query) {
- List<String> selectArgs = new ArrayList<>();
-
- if (query.id >= 0) {
- if (query.dateTakenAfterMs >= 0) {
- qb.appendWhereStandalone(WHERE_DATE_TAKEN_MS_AFTER);
- // Add date args twice because the sql statement evaluates date twice
- selectArgs.add(String.valueOf(query.dateTakenAfterMs));
- selectArgs.add(String.valueOf(query.dateTakenAfterMs));
- } else {
- qb.appendWhereStandalone(WHERE_DATE_TAKEN_MS_BEFORE);
- // Add date args twice because the sql statement evaluates date twice
- selectArgs.add(String.valueOf(query.dateTakenBeforeMs));
- selectArgs.add(String.valueOf(query.dateTakenBeforeMs));
- }
- selectArgs.add(String.valueOf(query.id));
- }
-
- if (query.sizeBytes >= 0) {
- qb.appendWhereStandalone(WHERE_SIZE_BYTES);
- selectArgs.add(String.valueOf(query.sizeBytes));
- }
-
- if (query.mimeType != null) {
- qb.appendWhereStandalone(WHERE_MIME_TYPE);
- selectArgs.add(query.mimeType.replace('*', '%'));
- }
-
- if (query.isFavorite) {
- qb.appendWhereStandalone(WHERE_IS_FAVORITE);
- }
-
- 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 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;
- }
-}
diff --git a/src/com/android/providers/media/photopicker/data/PickerResult.java b/src/com/android/providers/media/photopicker/data/PickerResult.java
deleted file mode 100644
index 3e08880..0000000
--- a/src/com/android/providers/media/photopicker/data/PickerResult.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.data;
-
-import android.content.ClipData;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Build;
-import android.provider.MediaStore;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RequiresApi;
-
-import com.android.modules.utils.build.SdkLevel;
-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 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(@NonNull Context context,
- @NonNull List<Item> selectedItems) {
- return getPickerResponseIntent(context, selectedItems, /* shouldReturnPickerUris */ true);
- }
-
- /**
- * @return {@code Intent} which contains Uri that has been granted access on.
- * TODO(b/168001592): Remove this method and merge it with actual method
- * {@link PickerResult#getPickerResponseIntent(Context, List)} when intent-filter for
- * ACTION_GET_CONTENT is removed or when we don't have to send redactedUris any more.
- */
- @NonNull
- public static Intent getPickerResponseIntent(@NonNull Context context,
- @NonNull List<Item> selectedItems, boolean shouldReturnPickerUris) {
- // 1. Get Picker Uris corresponding to the selected items
- List<Uri> selectedUris;
- if (shouldReturnPickerUris) {
- selectedUris = getPickerUrisForItems(selectedItems);
- } else {
- selectedUris = getRedactedUrisForItems(context.getContentResolver(), selectedItems);
- }
-
- // 2. Grant read access to picker Uris and return
- Intent intent = new Intent();
- final int size = selectedUris.size();
- if (size == 1) {
- intent.setData(selectedUris.get(0));
- } else if (size > 1) {
- // 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);
- } else {
- // TODO (b/168783994): check if this is ever possible. If yes, handle properly,
- // if not, change the above "else if" block to "else" block.
- }
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
- return intent;
- }
-
- private static Uri getPickerUri(Uri uri, String id) {
- final String userInfo = uri.getUserInfo();
- final String userId = userInfo == null ? UserId.CURRENT_USER.toString() : userInfo;
- final Uri uriWithUserId =
- PickerUriResolver.PICKER_URI.buildUpon().appendPath(userId).build();
- return uriWithUserId.buildUpon().appendPath(id).build();
- }
-
- /**
- * 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(), item.getId()));
- }
-
- return uris;
- }
-
- private static List<Uri> getRedactedUrisForItems(ContentResolver contentResolver,
- List<Item> ItemList){
- List<Uri> uris = new ArrayList<>();
- for (Item item : ItemList) {
- uris.add(item.getContentUri());
- }
-
- if (SdkLevel.isAtLeastS()) {
- return getRedactedUriFromMediaStoreAPI(contentResolver, uris);
- } else {
- // TODO (b/168783994): directly call redacted uri code logic or explore other solution.
- // This will be addressed in a follow up CL.
- return uris;
- }
- }
-
- @RequiresApi(Build.VERSION_CODES.S)
- private static List<Uri> getRedactedUriFromMediaStoreAPI(ContentResolver contentResolver,
- List<Uri> uris) {
- return MediaStore.getRedactedUri(contentResolver, uris);
- }
-}
diff --git a/src/com/android/providers/media/photopicker/data/UnreliableVolumeDatabaseHelper.java b/src/com/android/providers/media/photopicker/data/UnreliableVolumeDatabaseHelper.java
deleted file mode 100644
index 9679954..0000000
--- a/src/com/android/providers/media/photopicker/data/UnreliableVolumeDatabaseHelper.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.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
deleted file mode 100644
index 1f265de..0000000
--- a/src/com/android/providers/media/photopicker/data/UserIdManager.java
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.data;
-
-import static androidx.core.util.Preconditions.checkNotNull;
-
-import android.annotation.Nullable;
-import android.annotation.WorkerThread;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-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);
-
- /**
- * Updates cross profile restrictions values
- */
- @WorkerThread
- void updateCrossProfileValues();
-
- /**
- * Creates an implementation of {@link UserIdManager}.
- */
- static UserIdManager create(Context context) {
- return new RuntimeUserIdManager(context);
- }
-
- /**
- * Implementation of {@link UserIdManager}.
- */
- final class RuntimeUserIdManager implements UserIdManager {
-
- private static final String TAG = "UserIdManager";
-
- private final Context mContext;
- private final UserId mCurrentUser;
-
- @GuardedBy("mLock")
- private final Object mLock = new Object();
- @GuardedBy("mLock")
- private UserId mPersonalUser = null;
- @GuardedBy("mLock")
- private UserId mManagedUser = null;
-
- @GuardedBy("mLock")
- private UserId mCurrentUserProfile = null;
-
- private Intent mIntent = null;
- // Set default values to negative case, only set as false if checks pass.
- private boolean mIsBlockedByAdmin = true;
- private boolean mIsWorkProfileOff = true;
-
- private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- synchronized (mLock) {
- mPersonalUser = null;
- mManagedUser = null;
- setUserIds();
- }
- }
- };
-
- private RuntimeUserIdManager(Context context) {
- this(context, UserId.CURRENT_USER);
- }
-
- @VisibleForTesting
- RuntimeUserIdManager(Context context, UserId currentUser) {
- mContext = context.getApplicationContext();
- mCurrentUser = checkNotNull(currentUser);
- mCurrentUserProfile = mCurrentUser;
- setUserIds();
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
- mContext.registerReceiver(mIntentReceiver, filter);
- }
-
- @Override
- public boolean isMultiUserProfiles() {
- synchronized (mLock) {
- return mPersonalUser != null;
- }
- }
-
- @Override
- public UserId getPersonalUserId() {
- synchronized (mLock) {
- return mPersonalUser;
- }
- }
-
- @Override
- public UserId getManagedUserId() {
- synchronized (mLock) {
- return mManagedUser;
- }
- }
-
- @Override
- public UserId getCurrentUserProfileId() {
- synchronized (mLock) {
- return mCurrentUserProfile;
- }
- }
-
- @Override
- public void setManagedAsCurrentUserProfile() {
- setCurrentUserProfileId(getManagedUserId());
- }
-
- @Override
- public void setPersonalAsCurrentUserProfile() {
- setCurrentUserProfileId(getPersonalUserId());
- }
-
- @Override
- public void setIntentAndCheckRestrictions(Intent intent) {
- mIntent = intent;
- updateCrossProfileValues();
- }
-
- public boolean isCurrentUserSelected() {
- synchronized (mLock) {
- return mCurrentUserProfile.equals(UserId.CURRENT_USER);
- }
- }
-
- public boolean isManagedUserSelected() {
- synchronized (mLock) {
- return mCurrentUserProfile.equals(getManagedUserId());
- }
- }
-
- @Override
- public boolean isPersonalUserId() {
- return mCurrentUser.equals(getPersonalUserId());
- }
-
- @Override
- public boolean isManagedUserId() {
- return mCurrentUser.equals(getManagedUserId());
- }
-
- private void setUserIds() {
- synchronized (mLock) {
- setUserIdsInternalLocked();
- }
- }
-
- private void setCurrentUserProfileId(UserId userId) {
- synchronized (mLock) {
- mCurrentUserProfile = userId;
- }
- }
-
- @GuardedBy("mLock")
- private void setUserIdsInternalLocked() {
- 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() {
- return (!isWorkProfileOff() && !isBlockedByAdmin());
- }
-
- @Override
- public boolean isWorkProfileOff() {
- return mIsWorkProfileOff;
- }
-
- @Override
- public boolean isBlockedByAdmin() {
- return mIsBlockedByAdmin;
- }
-
- @Override
- @WorkerThread
- public void updateCrossProfileValues() {
- setCrossProfileValues();
- }
-
- @WorkerThread
- private void setCrossProfileValues() {
- final PackageManager packageManager = mContext.getPackageManager();
- // 1. Check if PICK_IMAGES intent is allowed by admin to show cross user content
- if (mIntent == null) {
- Log.e(TAG, "No intent specified to check if cross profile forwarding is"
- + " allowed.");
- return;
- }
- if (!CrossProfileUtils.isIntentAllowedCrossProfileAccess(mIntent, packageManager)) {
- mIsBlockedByAdmin = true;
- return;
- }
- mIsBlockedByAdmin = false;
-
- // 2. Check if work profile is off
- if (!isManagedUserSelected()) {
- final UserId managedUserProfileId = getManagedUserId();
- if (!CrossProfileUtils.isMediaProviderAvailable(managedUserProfileId, mContext)) {
- mIsWorkProfileOff = true;
- return;
- }
- }
- mIsWorkProfileOff = false;
- }
- }
-}
diff --git a/src/com/android/providers/media/photopicker/data/model/Category.java b/src/com/android/providers/media/photopicker/data/model/Category.java
deleted file mode 100644
index 07dbd6a..0000000
--- a/src/com/android/providers/media/photopicker/data/model/Category.java
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.data.model;
-
-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.Environment;
-import android.provider.CloudMediaProviderContract;
-import android.provider.MediaStore;
-import android.provider.MediaStore.Files.FileColumns;
-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 com.android.providers.media.photopicker.data.model.Item.ItemColumns;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Defines each category (which is group of items) for the photo picker.
- */
-public class Category {
-
- /**
- * Photo Picker categorises images/videos into pre-defined buckets based on various criteria
- * (for example based on file path location items may be in {@link #CATEGORY_SCREENSHOTS} or
- * {@link #CATEGORY_CAMERA}, based on {@link FileColumns#MEDIA_TYPE}) items may be in
- * {@link #CATEGORY_VIDEOS}). This list is predefined for v0.
- *
- * TODO (b/187919236): Add Downloads/SDCard categories.
- */
- @StringDef(prefix = { "CATEGORY_" }, value = {
- CATEGORY_DEFAULT,
- CATEGORY_SCREENSHOTS,
- CATEGORY_CAMERA,
- CATEGORY_VIDEOS,
- CATEGORY_FAVORITES,
- CATEGORY_DOWNLOADS,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface CategoryType {}
-
- /**
- * Includes all images/videos on device that are scanned by {@link MediaStore}.
- */
- public static final String CATEGORY_DEFAULT = "default";
-
- /**
- * Includes images/videos that are present in the
- * {@link Environment#DIRECTORY_PICTURES}/{@link Environment#DIRECTORY_SCREENSHOTS} directory.
- *
- * TODO(b/192932740): Include media that contains {@link Environment#DIRECTORY_SCREENSHOTS}
- * in its relative_path.
- */
- public static final String CATEGORY_SCREENSHOTS = "Screenshots";
- private static final String SCREENSHOTS_WHERE_CLAUSE =
- "(" + MediaStore.MediaColumns.RELATIVE_PATH + " LIKE '" +
- Environment.DIRECTORY_PICTURES + "/" +
- Environment.DIRECTORY_SCREENSHOTS + "/%')";
-
- /**
- * Includes images/videos that are present in the {@link Environment#DIRECTORY_DCIM}/Camera
- * directory.
- */
- public static final String CATEGORY_CAMERA = "Camera";
- private static final String CAMERA_WHERE_CLAUSE =
- "(" + MediaStore.MediaColumns.RELATIVE_PATH + " LIKE '" +
- Environment.DIRECTORY_DCIM + "/Camera/%')";
-
- /**
- * Includes videos only.
- */
- public static final String CATEGORY_VIDEOS = "Videos";
- private static final String VIDEOS_WHERE_CLAUSE =
- "(" + MediaStore.Files.FileColumns.MEDIA_TYPE +
- " = " + MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO + ")";
-
- /**
- * Includes images/videos that have {@link MediaStore.MediaColumns#IS_FAVORITE} set.
- */
- public static final String CATEGORY_FAVORITES = "Favorites";
- // TODO (b/188053832): Do not reveal implementation detail for is_favorite,
- // use MATCH_INCLUDE in queryArgs.
- private static final String FAVORITES_WHERE_CLAUSE =
- "(" + MediaStore.MediaColumns.IS_FAVORITE + " =1)";
-
- /**
- * Includes images/videos that have {@link MediaStore.MediaColumns#IS_DOWNLOAD} set.
- */
- public static final String CATEGORY_DOWNLOADS = "Downloads";
- private static final String DOWNLOADS_WHERE_CLAUSE =
- "(" + MediaStore.MediaColumns.IS_DOWNLOAD + " =1)";
-
- /**
- * Set of {@link Cursor} columns that refer to raw filesystem paths.
- */
- private static final ArrayMap<String, String> sCategoryWhereClause = new ArrayMap<>();
-
- static {
- sCategoryWhereClause.put(CATEGORY_SCREENSHOTS, SCREENSHOTS_WHERE_CLAUSE);
- sCategoryWhereClause.put(CATEGORY_CAMERA, CAMERA_WHERE_CLAUSE);
- sCategoryWhereClause.put(CATEGORY_VIDEOS, VIDEOS_WHERE_CLAUSE);
- sCategoryWhereClause.put(CATEGORY_FAVORITES, FAVORITES_WHERE_CLAUSE);
- sCategoryWhereClause.put(CATEGORY_DOWNLOADS, DOWNLOADS_WHERE_CLAUSE);
- }
-
- public static String getWhereClauseForCategory(@CategoryType String category) {
- return sCategoryWhereClause.get(category);
- }
-
- private static String[] CATEGORIES = {
- CATEGORY_FAVORITES,
- CATEGORY_CAMERA,
- CATEGORY_VIDEOS,
- CATEGORY_SCREENSHOTS,
- CATEGORY_DOWNLOADS,
- };
-
- public static List<String> CATEGORIES_LIST = Collections.unmodifiableList(
- Arrays.asList(CATEGORIES));
-
- public static boolean isValidCategory(String category) {
- return CATEGORIES_LIST.contains(category);
- }
-
- @CategoryType
- private String mCategoryType;
- private String mCategoryName;
- private Uri mCoverUri;
- private int mItemCount;
-
- private Category() {}
-
- @VisibleForTesting
- Category(@NonNull Cursor cursor, @NonNull UserId userId) {
- updateFromCursor(cursor, userId);
- }
-
- /**
- * Defines category columns for each category
- */
- public static class CategoryColumns {
- public static String NAME = CloudMediaProviderContract.AlbumColumns.DISPLAY_NAME;
- public static String COVER_ID = CloudMediaProviderContract.AlbumColumns.MEDIA_COVER_ID;
- public static String NUMBER_OF_ITEMS = CloudMediaProviderContract.AlbumColumns.MEDIA_COUNT;
- public static String CATEGORY_TYPE = CloudMediaProviderContract.AlbumColumns.ID;
-
- public static String[] getAllColumns() {
- return new String[] {
- NAME,
- COVER_ID,
- NUMBER_OF_ITEMS,
- CATEGORY_TYPE,
- };
- }
- }
-
- /**
- * @return localized category name if {@code context} is not null and {@link #mCategoryType} is
- * in {@link #CATEGORIES}, {@link #mCategoryName} otherwise.
- */
- public String getCategoryName(@Nullable Context context) {
- if (context != null) {
- final String categoryName = getCategoryName(context, mCategoryType);
- if (categoryName != null) {
- return categoryName;
- }
- }
- return mCategoryName;
- }
-
- /**
- * @return localized category name if {@link #mCategoryType} is in {@link #CATEGORIES},
- * {@code null} otherwise.
- */
- public static String getCategoryName(@NonNull Context context,
- @NonNull @CategoryType String categoryType) {
- switch (categoryType) {
- case CATEGORY_FAVORITES:
- return context.getString(R.string.picker_category_favorites);
- case CATEGORY_VIDEOS:
- return context.getString(R.string.picker_category_videos);
- case CATEGORY_CAMERA:
- return context.getString(R.string.picker_category_camera);
- case CATEGORY_SCREENSHOTS:
- return context.getString(R.string.picker_category_screenshots);
- case CATEGORY_DOWNLOADS:
- return context.getString(R.string.picker_category_downloads);
- default:
- return null;
- }
- }
-
- @CategoryType
- public String getCategoryType() {
- return mCategoryType;
- }
-
- public Uri getCoverUri() {
- return mCoverUri;
- }
-
- public int getItemCount() {
- return mItemCount;
- }
-
- /**
- * Get the category instance with the {@link #mCategoryType} is {@link #CATEGORY_DEFAULT}
- *
- * @return the default category
- */
- public static Category getDefaultCategory() {
- final Category category = new Category();
- category.mCategoryType = CATEGORY_DEFAULT;
- return category;
- }
-
- /**
- * @return {@link Category} from the given {@code cursor}
- */
- public static Category fromCursor(@NonNull Cursor cursor, @NonNull UserId userId) {
- final Category category = new Category(cursor, userId);
- return category;
- }
-
- /**
- * Update the category based on the {@code cursor}
- *
- * @param cursor the cursor to update the data
- */
- public void updateFromCursor(@NonNull Cursor cursor, @NonNull UserId userId) {
- final String authority = getCursorString(cursor, ItemColumns.AUTHORITY);
-
- mCategoryName = getCursorString(cursor, CategoryColumns.NAME);
- mCoverUri = ItemsProvider.getItemsUri(getCursorString(cursor, CategoryColumns.COVER_ID),
- authority, userId);
- mItemCount = getCursorInt(cursor, CategoryColumns.NUMBER_OF_ITEMS);
- mCategoryType = getCursorString(cursor, CategoryColumns.CATEGORY_TYPE);
- }
-}
diff --git a/src/com/android/providers/media/photopicker/data/model/Item.java b/src/com/android/providers/media/photopicker/data/model/Item.java
deleted file mode 100644
index c69bbee..0000000
--- a/src/com/android/providers/media/photopicker/data/model/Item.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.data.model;
-
-import static com.android.providers.media.photopicker.util.CursorUtils.getCursorLong;
-import static com.android.providers.media.photopicker.util.CursorUtils.getCursorString;
-
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.CloudMediaProviderContract;
-import android.provider.MediaStore;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-
-import com.android.providers.media.photopicker.data.ItemsProvider;
-import com.android.providers.media.photopicker.data.PickerDbFacade;
-import com.android.providers.media.util.MimeUtils;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Base class representing one single entity/item in the PhotoPicker.
- */
-public class Item {
-
- public static class ItemColumns {
- public static String ID = CloudMediaProviderContract.MediaColumns.ID;
- public static String MIME_TYPE = CloudMediaProviderContract.MediaColumns.MIME_TYPE;
- public static String DATE_TAKEN = CloudMediaProviderContract.MediaColumns.DATE_TAKEN_MS;
- // TODO(b/195009139): Remove after fully switching to picker db
- public static String DATE_MODIFIED = MediaStore.MediaColumns.DATE_MODIFIED;
- public static String DURATION = CloudMediaProviderContract.MediaColumns.DURATION_MS;
- public static String SIZE = CloudMediaProviderContract.MediaColumns.SIZE_BYTES;
- public static String AUTHORITY = CloudMediaProviderContract.MediaColumns.AUTHORITY;
-
- public static final String[] ALL_COLUMNS = {
- ID,
- MIME_TYPE,
- DATE_TAKEN,
- DATE_MODIFIED,
- DURATION,
- };
-
- // TODO(b/195009139): Remove after fully switching to picker db
- public static final String[] PROJECTION = {
- MediaStore.MediaColumns._ID + " AS " + ID,
- MediaStore.MediaColumns.MIME_TYPE + " AS " + MIME_TYPE,
- MediaStore.MediaColumns.DATE_TAKEN + " AS " + DATE_TAKEN,
- MediaStore.MediaColumns.DATE_MODIFIED + " AS " + DATE_MODIFIED,
- MediaStore.MediaColumns.DURATION + " AS " + DURATION,
- };
- }
-
- private static final String MIME_TYPE_GIF = "image/gif";
-
- private String mId;
- private long mDateTaken;
- private long mDuration;
- private String mMimeType;
- private Uri mUri;
- private boolean mIsImage;
- private boolean mIsVideo;
- private boolean mIsGif;
- 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 duration, Uri uri) {
- mId = id;
- mMimeType = mimeType;
- mDateTaken = dateTaken;
- mDuration = duration;
- mUri = uri;
- parseMimeType();
- }
-
- public String getId() {
- return mId;
- }
-
- public boolean isImage() {
- return mIsImage;
- }
-
- public boolean isVideo() {
- return mIsVideo;
- }
-
- public boolean isGif() {
- return mIsGif;
- }
-
- 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 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, ItemColumns.AUTHORITY);
- mId = getCursorString(cursor, ItemColumns.ID);
- mMimeType = getCursorString(cursor, ItemColumns.MIME_TYPE);
- mDateTaken = getCursorLong(cursor, ItemColumns.DATE_TAKEN);
- if (mDateTaken < 0) {
- // Convert DATE_MODIFIED to millis
- mDateTaken = getCursorLong(cursor, ItemColumns.DATE_MODIFIED) * 1000;
- }
- mDuration = getCursorLong(cursor, ItemColumns.DURATION);
-
- // TODO (b/188867567): Currently, we only has local data source,
- // get the uri from provider
- mUri = ItemsProvider.getItemsUri(mId, authority, userId);
-
- parseMimeType();
- }
-
- private void parseMimeType() {
- if (MIME_TYPE_GIF.equalsIgnoreCase(mMimeType)) {
- mIsGif = true;
- } else if (MimeUtils.isImageMimeType(mMimeType)) {
- mIsImage = true;
- } else if (MimeUtils.isVideoMimeType(mMimeType)) {
- mIsVideo = true;
- }
- }
-}
diff --git a/src/com/android/providers/media/photopicker/data/model/UserId.java b/src/com/android/providers/media/photopicker/data/model/UserId.java
deleted file mode 100644
index 65c505f..0000000
--- a/src/com/android/providers/media/photopicker/data/model/UserId.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.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;
-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 IllegalStateException if android package of the other user does not exist
- */
- Context asContext(Context context) {
- if (CURRENT_USER.equals(this)) {
- return context;
- }
- try {
- return context.createPackageContextAsUser("android", /* flags= */ 0, mUserHandle);
- } catch (PackageManager.NameNotFoundException e) {
- throw new IllegalStateException("android package not found.");
- }
- }
-
- /**
- * Return a content resolver instance of this user.
- */
- public ContentResolver getContentResolver(Context context) {
- 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/ui/AlbumGridHolder.java b/src/com/android/providers/media/photopicker/ui/AlbumGridHolder.java
deleted file mode 100644
index 9533b48..0000000
--- a/src/com/android/providers/media/photopicker/ui/AlbumGridHolder.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.ui;
-
-import android.content.Context;
-import android.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 java.text.NumberFormat;
-import java.util.Locale;
-
-/**
- * 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.getCategoryName(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 = itemView.getResources().getQuantityString(
- R.plurals.picker_album_item_count, itemCount);
-
- 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
deleted file mode 100644
index 447da5e..0000000
--- a/src/com/android/providers/media/photopicker/ui/AlbumsTabAdapter.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.ui;
-
-import android.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
deleted file mode 100644
index 04824a2..0000000
--- a/src/com/android/providers/media/photopicker/ui/AlbumsTabFragment.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.providers.media.photopicker.ui;
-
-import 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 int mBottomBarGap;
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- mBottomBarGap = getResources().getDimensionPixelSize(R.dimen.picker_album_bottom_bar_gap);
-
- final AlbumsTabAdapter adapter = new AlbumsTabAdapter(mImageLoader, this::onItemClick,
- mPickerViewModel.hasMimeTypeFilter());
- mPickerViewModel.getCategories().observe(this, categoryList -> {
- adapter.updateCategoryList(categoryList);
- });
- 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.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);
- }
-
- @Override
- protected int getBottomGapForRecyclerView(int bottomBarSize) {
- return bottomBarSize + mBottomBarGap;
- }
-
- /**
- * 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
deleted file mode 100644
index 1ec4cd1..0000000
--- a/src/com/android/providers/media/photopicker/ui/AlbumsTabItemDecoration.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.ui;
-
-import android.content.Context;
-import android.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
deleted file mode 100644
index 687ae27..0000000
--- a/src/com/android/providers/media/photopicker/ui/AutoFitRecyclerView.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.ui;
-
-import android.content.Context;
-import android.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 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(1, getMeasuredWidth() / mColumnWidth);
- ((GridLayoutManager) getLayoutManager()).setSpanCount(spanCount);
- }
- }
-
- @Override
- public void setLayoutManager(@Nullable RecyclerView.LayoutManager layoutManager) {
- super.setLayoutManager(layoutManager);
- if (layoutManager instanceof GridLayoutManager) {
- mIsGridLayout = true;
- }
- }
-
- public void setColumnWidth(int columnWidth) {
- mColumnWidth = columnWidth;
- }
-}
diff --git a/src/com/android/providers/media/photopicker/ui/BaseViewHolder.java b/src/com/android/providers/media/photopicker/ui/BaseViewHolder.java
deleted file mode 100644
index e5f0af0..0000000
--- a/src/com/android/providers/media/photopicker/ui/BaseViewHolder.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.ui;
-
-import android.content.Context;
-import android.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();
-
- /**
- * Called when view in this {@code RecyclerView.ViewHolder} has been recycled.
- * <p>
- * Optional method for BaseViewHolder subclasses to implement if they have additional actions to
- * take on {@link RecyclerView.Adapter#onViewRecycled}
- */
- public void onViewRecycled() {};
-
- /**
- * Called when a view in this {@code RecyclerView.ViewHolder} has been attached to a window.
- * <p>
- * Optional method for BaseViewHolder subclasses to implement if they have additional actions to
- * take on {@link RecyclerView.Adapter#onViewAttachedToWindow}
- */
- public void onViewAttachedToWindow() {};
-
- /**
- * Called when a view in this {@code RecyclerView.ViewHolder} has been detached from its window.
- * <p>
- * Optional method for BaseViewHolder subclasses to implement if they have additional actions to
- * take on {@link RecyclerView.Adapter#onViewDetachedFromWindow}
- */
- public void onViewDetachedFromWindow() {};
-}
diff --git a/src/com/android/providers/media/photopicker/ui/DateHeaderHolder.java b/src/com/android/providers/media/photopicker/ui/DateHeaderHolder.java
deleted file mode 100644
index 72560e8..0000000
--- a/src/com/android/providers/media/photopicker/ui/DateHeaderHolder.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.providers.media.photopicker.ui;
-import android.content.Context;
-import android.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.getDateTimeString(itemView.getContext(), dateTaken));
- }
- }
-}
diff --git a/src/com/android/providers/media/photopicker/ui/ImageLoader.java b/src/com/android/providers/media/photopicker/ui/ImageLoader.java
deleted file mode 100644
index b6d82cd..0000000
--- a/src/com/android/providers/media/photopicker/ui/ImageLoader.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.ui;
-
-import android.content.Context;
-import android.net.Uri;
-import android.widget.ImageView;
-
-import androidx.annotation.NonNull;
-
-import com.bumptech.glide.Glide;
-
-import com.android.providers.media.photopicker.data.model.Category;
-import com.android.providers.media.photopicker.data.model.Item;
-
-/**
- * A class to assist with loading and managing the Images (i.e. thumbnails and preview) associated
- * with item.
- */
-public class 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) {
- loadThumbnail(category.getCoverUri(), 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) {
- loadThumbnail(item.getContentUri(), 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)
- .load(item.getContentUri())
- .into(imageView);
- return;
- }
- // Preview as bitmap image for all other image types
- Glide.with(mContext)
- .asBitmap()
- .load(item.getContentUri())
- .into(imageView);
- }
-
- private void loadThumbnail(@NonNull Uri uri, @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(uri)
- .thumbnail()
- .into(imageView);
- }
-}
diff --git a/src/com/android/providers/media/photopicker/ui/PhotoGridHolder.java b/src/com/android/providers/media/photopicker/ui/PhotoGridHolder.java
deleted file mode 100644
index 3f3e2b4..0000000
--- a/src/com/android/providers/media/photopicker/ui/PhotoGridHolder.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.ui;
-
-import android.content.Context;
-import android.text.format.DateUtils;
-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 mIconVideo;
- 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);
- mVideoBadgeContainer = itemView.findViewById(R.id.video_container);
- mIconVideo = mVideoBadgeContainer.findViewById(R.id.icon_video);
- 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);
-
- if (item.isGif()) {
- mIconGif.setVisibility(View.VISIBLE);
- } else {
- mIconGif.setVisibility(View.GONE);
- }
-
- if (item.isVideo()) {
- mVideoBadgeContainer.setVisibility(View.VISIBLE);
- mVideoDuration.setText(DateUtils.formatElapsedTime(item.getDuration() / 1000));
- } 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.isGif() || item.isVideo();
- }
-}
diff --git a/src/com/android/providers/media/photopicker/ui/PhotosTabAdapter.java b/src/com/android/providers/media/photopicker/ui/PhotosTabAdapter.java
deleted file mode 100644
index 881dde6..0000000
--- a/src/com/android/providers/media/photopicker/ui/PhotosTabAdapter.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.ui;
-
-import android.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.photopicker.data.model.Item;
-import com.android.providers.media.photopicker.viewmodel.PickerViewModel;
-
-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 ImageLoader mImageLoader;
- private View.OnClickListener mOnClickListener;
- private PickerViewModel mPickerViewModel;
-
- public PhotosTabAdapter(@NonNull PickerViewModel pickerViewModel,
- @NonNull ImageLoader imageLoader, @NonNull View.OnClickListener listener) {
- mImageLoader = imageLoader;
- mPickerViewModel = pickerViewModel;
- mOnClickListener = listener;
- }
-
- @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,
- mPickerViewModel.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);
- final boolean isItemSelected =
- mPickerViewModel.getSelectedItems().getValue().containsKey(
- item.getContentUri());
- itemHolder.itemView.setSelected(isItemSelected);
- }
- 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
deleted file mode 100644
index 076e918..0000000
--- a/src/com/android/providers/media/photopicker/ui/PhotosTabFragment.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.providers.media.photopicker.ui;
-
-import static com.android.providers.media.photopicker.ui.PhotosTabAdapter.COLUMN_COUNT;
-
-import android.os.Bundle;
-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.Category.CategoryType;
-import com.android.providers.media.photopicker.data.model.Item;
-import com.android.providers.media.photopicker.util.LayoutModeUtils;
-
-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 String FRAGMENT_TAG = "PhotosTabFragment";
- private static final String EXTRA_CATEGORY_TYPE = "category_type";
- private static final String EXTRA_CATEGORY_NAME = "category_name";
-
- private boolean mIsDefaultCategory;
- @CategoryType
- private String mCategoryType;
- private String mCategoryName;
-
- @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) {
- mCategoryType = savedInstanceState.getString(EXTRA_CATEGORY_TYPE,
- Category.CATEGORY_DEFAULT);
- mCategoryName = savedInstanceState.getString(EXTRA_CATEGORY_NAME,
- /* defaultValue= */ "");
- }
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- final PhotosTabAdapter adapter = new PhotosTabAdapter(mPickerViewModel, mImageLoader,
- this::onItemClick);
-
- mIsDefaultCategory = TextUtils.equals(Category.CATEGORY_DEFAULT, mCategoryType);
- if (mIsDefaultCategory) {
- mPickerViewModel.getItems().observe(this, itemList -> {
- adapter.updateItemList(itemList);
- });
- } else {
- mPickerViewModel.getCategoryItems(mCategoryType).observe(this, itemList -> {
- 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.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);
- state.putString(EXTRA_CATEGORY_TYPE, mCategoryType);
- state.putString(EXTRA_CATEGORY_NAME, mCategoryName);
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- if (mIsDefaultCategory) {
- ((PhotoPickerActivity) getActivity()).updateCommonLayouts(
- LayoutModeUtils.MODE_PHOTOS_TAB, /* title */ "");
- hideProfileButton(/* hide */ false);
- } else {
- hideProfileButton(/* hide */ true);
- String categoryName = Category.getCategoryName(getContext(), mCategoryType);
-
- if (TextUtils.isEmpty(categoryName)) {
- categoryName = mCategoryName;
- }
- ((PhotoPickerActivity) getActivity()).updateCommonLayouts(
- LayoutModeUtils.MODE_ALBUM_PHOTOS_TAB, categoryName);
- }
- }
-
- private void onItemClick(@NonNull View view) {
- if (mPickerViewModel.canSelectMultiple()) {
- final boolean isSelectedBefore = view.isSelected();
-
- if (isSelectedBefore) {
- mPickerViewModel.deleteSelectedItem((Item) view.getTag());
- } else {
- if (!mPickerViewModel.isSelectionAllowed()) {
- final int maxCount = mPickerViewModel.getMaxSelectionLimit();
- final CharSequence quantityText =
- getResources().getQuantityString(R.plurals.select_up_to, maxCount);
- 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 {
- mPickerViewModel.addSelectedItem((Item) view.getTag());
- }
- }
- view.setSelected(!isSelectedBefore);
- } else {
- mPickerViewModel.clearSelectedItems();
- mPickerViewModel.addSelectedItem((Item) view.getTag());
- // Transition to PreviewFragment.
- PreviewFragment.show(getActivity().getSupportFragmentManager());
- }
- }
-
- /**
- * 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.mCategoryType = category.getCategoryType();
- fragment.mCategoryName = category.getCategoryName(/* context= */ null);
- ft.replace(R.id.fragment_container, fragment, FRAGMENT_TAG);
- if (!TextUtils.equals(category.getCategoryType(), Category.CATEGORY_DEFAULT)) {
- 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
deleted file mode 100644
index 4d8e873..0000000
--- a/src/com/android/providers/media/photopicker/ui/PhotosTabItemDecoration.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.ui;
-
-import 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/PreviewAdapter.java b/src/com/android/providers/media/photopicker/ui/PreviewAdapter.java
deleted file mode 100644
index 7bdacf0..0000000
--- a/src/com/android/providers/media/photopicker/ui/PreviewAdapter.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.ui;
-
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.providers.media.photopicker.data.model.Item;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Adapter for Preview RecyclerView to preview all images and videos.
- */
-public 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 ImageLoader mImageLoader;
-
- public PreviewAdapter(ImageLoader imageLoader) {
- mImageLoader = imageLoader;
- }
-
- @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);
- }
- }
-
- @Override
- public void onBindViewHolder(@NonNull BaseViewHolder photoHolder, int position) {
- final Item item = getItem(position);
- photoHolder.itemView.setTag(item);
- photoHolder.bind();
- }
-
- @Override
- public void onViewAttachedToWindow(BaseViewHolder holder) {
- super.onViewAttachedToWindow(holder);
- holder.onViewAttachedToWindow();
- }
-
- @Override
- public void onViewDetachedFromWindow(BaseViewHolder holder) {
- super.onViewDetachedFromWindow(holder);
- holder.onViewDetachedFromWindow();
- }
-
- @Override
- public void onViewRecycled(BaseViewHolder holder) {
- super.onViewRecycled(holder);
- holder.onViewRecycled();
- }
-
- @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;
- }
-
- public Item getItem(int position) {
- return mItemList.get(position);
- }
-
- public 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
deleted file mode 100644
index 14c3695..0000000
--- a/src/com/android/providers/media/photopicker/ui/PreviewFragment.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.ui;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.FrameLayout.LayoutParams;
-
-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.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.ArrayList;
-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 PickerViewModel mPickerViewModel;
- private ViewPager2 mViewPager;
- private PreviewAdapter mAdapter;
- private ViewPager2.OnPageChangeCallback mOnPageChangeCallBack;
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup parent,
- Bundle savedInstanceState) {
- mPickerViewModel = new ViewModelProvider(requireActivity()).get(PickerViewModel.class);
- return inflater.inflate(R.layout.fragment_preview, parent, /* attachToRoot */ false);
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- // Warning: The below code assumes that getSelectedItems will never return null.
- // We are creating a new ArrayList with selected items, this list used as data for the
- // adapter. If activity gets killed and recreated, we will lose items that were deselected.
- // TODO(b/185801129): Save the deselection state instead of making a copy of selected items.
- // TODO(b/185801129): Sort images/videos on based on date_taken
- final List<Item> selectedItemList = new ArrayList<>(
- mPickerViewModel.getSelectedItems().getValue().values());
-
- if (selectedItemList.size() > 1 && !mPickerViewModel.canSelectMultiple() ||
- selectedItemList.size() <= 0) {
- // TODO(b/185801129): This should never happen. Add appropriate log messages and
- // handle UI transitions correctly on this error condition.
- // We should also handle this situation in ViewModel
- return;
- }
-
- Button addButton = view.findViewById(R.id.preview_add_button);
-
- // On clicking add button we return the picker result to calling app.
- // This destroys PickerActivity and all fragments.
- addButton.setOnClickListener(v -> {
- ((PhotoPickerActivity) getActivity()).setResultAndFinishSelf();
- });
-
- // TODO(b/169737802): Support Videos
- // Initialize adapter to hold selected items
- ImageLoader imageLoader = new ImageLoader(getContext());
- mAdapter = new PreviewAdapter(imageLoader);
- mAdapter.updateItemList(selectedItemList);
-
- // Initialize ViewPager2 to swipe between multiple pictures/videos in preview
- mViewPager = view.findViewById(R.id.preview_viewPager);
- mViewPager.setAdapter(mAdapter);
-
- Button selectButton = view.findViewById(R.id.preview_select_button);
-
- // Update the select icon and text according to the state of selection while swiping
- // between photos
- mOnPageChangeCallBack = new OnPageChangeCallBack(selectButton);
- mViewPager.registerOnPageChangeCallback(mOnPageChangeCallBack);
-
- // Adjust the layout based on Single/Multi select and add appropriate onClick listeners
- if (!mPickerViewModel.canSelectMultiple()) {
- // Adjust the select and add button layout for single select
- LayoutParams layoutParams
- = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
- addButton.setLayoutParams(layoutParams);
- selectButton.setVisibility(View.GONE);
- } else {
- // Update add button text to include number of items selected.
- mPickerViewModel.getSelectedItems().observe(this, selectedItems -> {
- addButton.setText(generateAddButtonString(getContext(), selectedItems.size()));
- });
- selectButton.setOnClickListener(v -> {
- onClickSelect(selectButton);
- });
- }
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- // TODO(185801129): Change the layout of the toolbar or add new toolbar that can overlap
- // with image/video preview if necessary
- ((PhotoPickerActivity) getActivity()).updateCommonLayouts(LayoutModeUtils.MODE_PREVIEW,
- /* title */"");
-
- // This is necessary to ensure we call ViewHolder#bind() onResume()
- if (mAdapter != null) {
- mAdapter.notifyDataSetChanged();
- }
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- if (mOnPageChangeCallBack != null && mViewPager != null) {
- mViewPager.unregisterOnPageChangeCallback(mOnPageChangeCallBack);
- }
- }
-
- private void onClickSelect(@NonNull Button selectButton) {
- // isSelected tracks new state for select button, which is opposite of old state
- final boolean isSelected = !selectButton.isSelected();
- final Item currentItem = mAdapter.getItem(mViewPager.getCurrentItem());
-
- if (isSelected) {
- mPickerViewModel.addSelectedItem(currentItem);
- } else {
- mPickerViewModel.deleteSelectedItem(currentItem);
- }
- setSelected(selectButton, isSelected);
- }
-
- private class OnPageChangeCallBack extends ViewPager2.OnPageChangeCallback {
- private final Button mSelectButton;
-
- public OnPageChangeCallBack(@NonNull Button selectButton) {
- mSelectButton = selectButton;
- }
-
- @Override
- public void onPageSelected(int position) {
- // No action to take as we don't have deselect view here.
- if (!mPickerViewModel.canSelectMultiple()) return;
-
- // Set the appropriate select/deselect state for each item in each page based on the
- // selection list.
- setSelected(mSelectButton, mPickerViewModel.getSelectedItems().getValue().containsKey(
- mAdapter.getItem(position).getContentUri()));
- }
- }
-
- private static void setSelected(@NonNull Button selectButton, boolean isSelected) {
- selectButton.setSelected(isSelected);
- selectButton.setText(isSelected ? R.string.deselect : R.string.select);
- }
-
- public static void show(FragmentManager fm) {
- if (fm.isStateSaved()) {
- Log.d(TAG, "Skip show preview fragment because state saved");
- return;
- }
-
- final PreviewFragment fragment = new PreviewFragment();
- 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(FragmentManager fm) {
- return fm.findFragmentByTag(TAG);
- }
-
- // TODO: There is a same method in TabFragment. To find a way to reuse it.
- 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/PreviewImageHolder.java b/src/com/android/providers/media/photopicker/ui/PreviewImageHolder.java
deleted file mode 100644
index 76ce74d..0000000
--- a/src/com/android/providers/media/photopicker/ui/PreviewImageHolder.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.ui;
-
-import android.content.Context;
-import android.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
deleted file mode 100644
index 366fdb5..0000000
--- a/src/com/android/providers/media/photopicker/ui/PreviewVideoHolder.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.ui;
-
-import android.content.Context;
-import android.view.ViewGroup;
-import android.widget.VideoView;
-
-import androidx.viewpager2.widget.ViewPager2;
-
-import com.android.providers.media.R;
-import com.android.providers.media.photopicker.data.model.Item;
-
-/**
- * ViewHolder of a video item within the {@link ViewPager2}
- */
-public class PreviewVideoHolder extends BaseViewHolder {
- private final VideoView mVideoView;
-
- public PreviewVideoHolder(Context context, ViewGroup parent) {
- super(context, parent, R.layout.item_video_preview);
- mVideoView = itemView.findViewById(R.id.preview_videoView);
- }
-
- @Override
- public void bind() {
- final Item item = (Item) itemView.getTag();
- mVideoView.setVideoURI(item.getContentUri());
- }
-
- @Override
- public void onViewAttachedToWindow() {
- super.onViewAttachedToWindow();
- mVideoView.setOnPreparedListener(mp -> {
- mp.setLooping(true);
- // For simplicity, we will always start the video from the beginning.
- mp.seekTo(0);
- mp.start();
- });
- }
-
- @Override
- public void onViewDetachedFromWindow() {
- super.onViewDetachedFromWindow();
- mVideoView.pause();
- }
-
- @Override
- public void onViewRecycled() {
- super.onViewRecycled();
- // This will deallocate any MediaPlayer resources it has been holding
- mVideoView.stopPlayback();
- }
-}
diff --git a/src/com/android/providers/media/photopicker/ui/ProfileDialogFragment.java b/src/com/android/providers/media/photopicker/ui/ProfileDialogFragment.java
deleted file mode 100644
index 630029c..0000000
--- a/src/com/android/providers/media/photopicker/ui/ProfileDialogFragment.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.ui;
-
-import android.app.Dialog;
-import android.os.Bundle;
-import android.util.Log;
-
-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.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()) {
- builder.setIcon(R.drawable.ic_lock);
- builder.setTitle(getString(R.string.picker_profile_admin_title));
- final String message = userIdManager.isManagedUserSelected() ?
- getString(R.string.picker_profile_admin_msg_from_work) :
- getString(R.string.picker_profile_admin_msg_from_personal);
- builder.setMessage(message);
- builder.setPositiveButton(android.R.string.ok, null);
- } else if (userIdManager.isWorkProfileOff()) {
- builder.setIcon(R.drawable.ic_work_outline);
- builder.setTitle(getString(R.string.picker_profile_work_paused_title));
- builder.setMessage(getString(R.string.picker_profile_work_paused_msg));
- // 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);
- } else {
- Log.e(TAG, "Unknown error for profile dialog");
- return null;
- }
- return builder.create();
- }
-
- 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
deleted file mode 100644
index b3a0af3..0000000
--- a/src/com/android/providers/media/photopicker/ui/SquareImageView.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.ui;
-
-import android.content.Context;
-import android.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/TabFragment.java b/src/com/android/providers/media/photopicker/ui/TabFragment.java
deleted file mode 100644
index be92ce1..0000000
--- a/src/com/android/providers/media/photopicker/ui/TabFragment.java
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.providers.media.photopicker.ui;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.content.res.AppCompatResources;
-import androidx.fragment.app.Fragment;
-import androidx.lifecycle.ViewModelProvider;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.providers.media.R;
-import com.android.providers.media.photopicker.PhotoPickerActivity;
-import com.android.providers.media.photopicker.data.UserIdManager;
-import com.android.providers.media.photopicker.viewmodel.PickerViewModel;
-import com.android.providers.media.util.ForegroundThread;
-
-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 {
-
- private static final String TAG = "PhotoPickerTabFragment";
-
- protected PickerViewModel mPickerViewModel;
- protected ImageLoader mImageLoader;
- protected AutoFitRecyclerView mRecyclerView;
-
- private int mBottomBarSize;
- private ExtendedFloatingActionButton mProfileButton;
- private UserIdManager mUserIdManager;
- private boolean mHideProfileButton;
-
- @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);
-
- mImageLoader = new ImageLoader(getContext());
- mRecyclerView = view.findViewById(R.id.photo_list);
- mRecyclerView.setHasFixedSize(true);
- mPickerViewModel = new ViewModelProvider(requireActivity()).get(PickerViewModel.class);
-
- mProfileButton = view.findViewById(R.id.profile_button);
- mUserIdManager = mPickerViewModel.getUserIdManager();
-
- final boolean canSelectMultiple = mPickerViewModel.canSelectMultiple();
- if (canSelectMultiple) {
- final Button addButton = view.findViewById(R.id.button_add);
- addButton.setOnClickListener(v -> {
- ((PhotoPickerActivity) getActivity()).setResultAndFinishSelf();
- });
-
- final Button viewSelectedButton = view.findViewById(R.id.button_view_selected);
- // Transition to PreviewFragment on clicking "View Selected".
- viewSelectedButton.setOnClickListener(v -> {
- PreviewFragment.show(getActivity().getSupportFragmentManager());
- });
- mBottomBarSize = (int) getResources().getDimension(R.dimen.picker_bottom_bar_size);
-
- mPickerViewModel.getSelectedItems().observe(this, selectedItemList -> {
- final View bottomBar = view.findViewById(R.id.picker_bottom_bar);
- final int size = selectedItemList.size();
- int dimen = 0;
- if (size == 0) {
- bottomBar.setVisibility(View.GONE);
- } else {
- bottomBar.setVisibility(View.VISIBLE);
- addButton.setText(generateAddButtonString(getContext(), size));
- dimen = getBottomGapForRecyclerView(mBottomBarSize);
- }
- mRecyclerView.setPadding(0, 0, 0, dimen);
-
- if (mUserIdManager.isMultiUserProfiles()) {
- if (shouldShowProfileButton()) {
- mProfileButton.show();
- } else {
- mProfileButton.hide();
- }
- }
- });
- }
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- updateProfileButtonAsync();
- }
-
- private void updateProfileButtonAsync() {
- ForegroundThread.getExecutor().execute(() -> {
- mUserIdManager.updateCrossProfileValues();
-
- getActivity().runOnUiThread(() -> setUpProfileButton());
- });
- }
-
- private void setUpProfileButton() {
- if (!mUserIdManager.isMultiUserProfiles()) {
- if (mProfileButton.getVisibility() == View.VISIBLE) {
- mProfileButton.setVisibility(View.GONE);
- mRecyclerView.clearOnScrollListeners();
- }
- return;
- }
-
- if (shouldShowProfileButton()) {
- mProfileButton.setVisibility(View.VISIBLE);
-
- // TODO(b/199473568): Set up listeners for profile button only once for a fragment or
- // when the value of isMultiUserProfile changes to true
- mProfileButton.setOnClickListener(v -> onClickProfileButton());
- mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
- @Override
- public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
- super.onScrolled(recyclerView, dx, dy);
- if (dy > 0) {
- mProfileButton.hide();
- } else {
- if (shouldShowProfileButton()) {
- mProfileButton.show();
- }
- }
- }
- });
- }
-
- updateProfileButtonContent(mUserIdManager.isManagedUserSelected());
- updateProfileButtonColor(/* isDisabled */ !mUserIdManager.isCrossProfileAllowed());
- }
-
- private boolean shouldShowProfileButton() {
- return (!mPickerViewModel.canSelectMultiple() ||
- mPickerViewModel.getSelectedItems().getValue().size() == 0) &&
- !mHideProfileButton;
- }
-
- 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 int iconResId;
- final int textResId;
- if (isManagedUserSelected) {
- iconResId = R.drawable.ic_personal_mode;
- textResId = R.string.picker_personal_profile;
- } else {
- iconResId = R.drawable.ic_work_outline;
- textResId = R.string.picker_work_profile;
- }
- mProfileButton.setIconResource(iconResId);
- mProfileButton.setText(textResId);
- }
-
- private void updateProfileButtonColor(boolean isDisabled) {
- final int textAndIconResId;
- final int backgroundTintResId;
- if (isDisabled) {
- textAndIconResId = R.color.picker_profile_disabled_button_content_color;
- backgroundTintResId = R.color.picker_profile_disabled_button_background_color;
- } else {
- textAndIconResId = R.color.picker_profile_button_content_color;
- backgroundTintResId = R.color.picker_profile_button_background_color;
- }
- mProfileButton.setTextColor(AppCompatResources.getColorStateList(getContext(),
- textAndIconResId));
- mProfileButton.setIconTintResource(textAndIconResId);
- mProfileButton.setBackgroundTintList(AppCompatResources.getColorStateList(getContext(),
- backgroundTintResId));
- }
-
- protected int getBottomGapForRecyclerView(int bottomBarSize) {
- return bottomBarSize;
- }
-
- protected void hideProfileButton(boolean hide) {
- mHideProfileButton = hide;
- if (hide) {
- mProfileButton.hide();
- } else if (mUserIdManager.isMultiUserProfiles() && shouldShowProfileButton()) {
- mProfileButton.show();
- }
- }
-
- 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/util/CrossProfileUtils.java b/src/com/android/providers/media/photopicker/util/CrossProfileUtils.java
deleted file mode 100644
index 728c03e..0000000
--- a/src/com/android/providers/media/photopicker/util/CrossProfileUtils.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.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.provider.MediaStore;
-
-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 {
-
- /**
- * 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;
- }
- }
- return false;
- }
-}
diff --git a/src/com/android/providers/media/photopicker/util/CursorUtils.java b/src/com/android/providers/media/photopicker/util/CursorUtils.java
deleted file mode 100644
index a992f4e..0000000
--- a/src/com/android/providers/media/photopicker/util/CursorUtils.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.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
deleted file mode 100644
index 8c07ff6..0000000
--- a/src/com/android/providers/media/photopicker/util/DateTimeUtils.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.util;
-
-import static android.icu.text.DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE;
-import static android.icu.text.RelativeDateTimeFormatter.Style.LONG;
-
-import android.content.Context;
-import android.icu.text.RelativeDateTimeFormatter;
-import android.icu.text.RelativeDateTimeFormatter.Direction;
-import android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit;
-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 {
-
- /**
- * Formats a time according to the local conventions.
- *
- * 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 context the context
- * @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 getDateTimeString(Context context, long when) {
- // Get the system time zone
- final ZoneId zoneId = ZoneId.systemDefault();
- final LocalDate nowDate = LocalDate.now(zoneId);
-
- return getDateTimeString(context, when, nowDate);
- }
-
- @VisibleForTesting
- static String getDateTimeString(Context context, 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 {
- int flags = DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_SHOW_DATE
- | DateUtils.FORMAT_ABBREV_ALL;
- if (whenDate.getYear() == nowDate.getYear()) {
- flags |= DateUtils.FORMAT_NO_YEAR;
- } else {
- flags |= DateUtils.FORMAT_SHOW_YEAR;
- }
- return DateUtils.formatDateTime(context, when, flags);
- }
- }
-
- /**
- * 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
deleted file mode 100644
index b89e648..0000000
--- a/src/com/android/providers/media/photopicker/util/LayoutModeUtils.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.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 shouldShowTabChips;
- 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.shouldShowTabChips = true;
- mode.isPreview = false;
- break;
- case MODE_ALBUM_PHOTOS_TAB:
- mode.shouldShowTabChips = false;
- mode.isPreview = false;
- break;
- case MODE_PREVIEW:
- mode.shouldShowTabChips = 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
deleted file mode 100644
index 70d95e7..0000000
--- a/src/com/android/providers/media/photopicker/viewmodel/PickerViewModel.java
+++ /dev/null
@@ -1,402 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.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.net.Uri;
-import android.os.Bundle;
-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 androidx.lifecycle.AndroidViewModel;
-import androidx.lifecycle.LiveData;
-import androidx.lifecycle.MutableLiveData;
-
-import com.android.providers.media.photopicker.data.ItemsProvider;
-import com.android.providers.media.photopicker.data.PickerDbFacade;
-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.Category.CategoryType;
-import com.android.providers.media.photopicker.data.model.Item;
-import com.android.providers.media.photopicker.data.model.UserId;
-import com.android.providers.media.photopicker.util.DateTimeUtils;
-import com.android.providers.media.util.ForegroundThread;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * 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;
- public static final int DEFAULT_MAX_SELECTION_LIMIT = 100;
-
- // 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 selected items.
- private MutableLiveData<Map<Uri, Item>> mSelectedItemList = new MutableLiveData<>();
- // The list of categories.
- private MutableLiveData<List<Category>> mCategoryList;
-
- private ItemsProvider mItemsProvider;
- private final UserIdManager mUserIdManager;
- private boolean mSelectMultiple = false;
- private String mMimeTypeFilter = null;
- private int mMaxSelectionLimit = DEFAULT_MAX_SELECTION_LIMIT;
- // This is set to false when max selection limit is reached.
- private boolean mIsSelectionAllowed = true;
- private int mBottomSheetState;
-
- public PickerViewModel(@NonNull Application application) {
- super(application);
- final Context context = application.getApplicationContext();
- mItemsProvider = new ItemsProvider(context);
- mUserIdManager = UserIdManager.create(context);
- }
-
- @VisibleForTesting
- void setItemsProvider(@NonNull ItemsProvider itemsProvider) {
- mItemsProvider = itemsProvider;
- }
-
- /**
- * @return the {@link LiveData} of selected items {@link #mSelectedItemList}.
- */
- public LiveData<Map<Uri, Item>> getSelectedItems() {
- if (mSelectedItemList.getValue() == null) {
- Map<Uri, Item> itemList = new HashMap<>();
- mSelectedItemList.setValue(itemList);
- }
- return mSelectedItemList;
- }
-
- /**
- * Add the selected {@code item} into {@link #mSelectedItemList}.
- */
- public void addSelectedItem(Item item) {
- if (mSelectedItemList.getValue() == null) {
- Map<Uri, Item> itemList = new HashMap<>();
- mSelectedItemList.setValue(itemList);
- }
- mSelectedItemList.getValue().put(item.getContentUri(), item);
- mSelectedItemList.postValue(mSelectedItemList.getValue());
-
- updateSelectionAllowed();
- }
-
- /**
- * Clear the selected Item list {@link #mSelectedItemList}.
- */
- public void clearSelectedItems() {
- if (mSelectedItemList.getValue() == null) {
- return;
- }
- mSelectedItemList.getValue().clear();
- mSelectedItemList.postValue(mSelectedItemList.getValue());
- }
-
- /**
- * Delete the selected {@code item} from the selected item list {@link #mSelectedItemList}.
- *
- * @param item the item to be deleted from the selected item list
- */
- public void deleteSelectedItem(Item item) {
- if (mSelectedItemList.getValue() == null) {
- return;
- }
- mSelectedItemList.getValue().remove(item.getContentUri());
- mSelectedItemList.postValue(mSelectedItemList.getValue());
- updateSelectionAllowed();
- }
-
- private void updateSelectionAllowed() {
- if (!mSelectMultiple) {
- return;
- }
-
- final int size = mSelectedItemList.getValue().size();
- if (size >= mMaxSelectionLimit) {
- if (mIsSelectionAllowed) {
- mIsSelectionAllowed = false;
- }
- } else {
- // size < mMaxSelectionLimit
- if (!mIsSelectionAllowed) {
- mIsSelectionAllowed = true;
- }
- }
- }
-
- /**
- * @return {@link UserIdManager} for this context.
- */
- public UserIdManager getUserIdManager() {
- return mUserIdManager;
- }
-
- /**
- * @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(@Nullable @CategoryType String category) {
- final List<Item> items = new ArrayList<>();
- final UserId userId = mUserIdManager.getCurrentUserProfileId();
-
- 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 = TextUtils.isEmpty(category);
-
- 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);
- }
- }
-
- if (TextUtils.isEmpty(category)) {
- Log.d(TAG, "Loaded " + items.size() + " items for user " + userId.toString());
- } else {
- Log.d(TAG, "Loaded " + items.size() + " items in " + category + " for user "
- + userId.toString());
- }
- return items;
- }
-
- private void loadItemsAsync() {
- ForegroundThread.getExecutor().execute(() -> {
- mItemList.postValue(loadItems(/* category= */ null));
- });
- }
-
- /**
- * 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.
- *
- * @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 @CategoryType String category) {
- updateCategoryItems(category);
- return mCategoryItemList;
- }
-
- private void loadCategoryItemsAsync(@NonNull @CategoryType String category) {
- ForegroundThread.getExecutor().execute(() -> {
- mCategoryItemList.postValue(loadItems(category));
- });
- }
-
- /**
- * Update the item List with the {@code category} {@link #mCategoryItemList}
- */
- public void updateCategoryItems(@NonNull @CategoryType String category) {
- if (mCategoryItemList == null) {
- mCategoryItemList = new MutableLiveData<>();
- }
- loadCategoryItemsAsync(category);
- }
-
- /**
- * @return the list of Categories {@link #mCategoryList}
- */
- public LiveData<List<Category>> getCategories() {
- if (mCategoryList == null) {
- updateCategories();
- }
- return mCategoryList;
- }
-
- private List<Category> loadCategories() {
- final List<Category> categoryList = new ArrayList<>();
- final UserId userId = mUserIdManager.getCurrentUserProfileId();
- 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() {
- ForegroundThread.getExecutor().execute(() -> {
- mCategoryList.postValue(loadCategories());
- });
- }
-
- /**
- * Update the category List {@link #mCategoryList}
- */
- public void updateCategories() {
- if (mCategoryList == null) {
- mCategoryList = new MutableLiveData<>();
- }
- loadCategoriesAsync();
- }
-
- /**
- * Return whether supports multiple select {@link #mSelectMultiple} or not
- */
- public boolean canSelectMultiple() {
- return mSelectMultiple;
- }
-
- /**
- * 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);
-
- mSelectMultiple = intent.getBooleanExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);
-
- final String mimeType = intent.getType();
- if (isMimeTypeMedia(mimeType)) {
- mMimeTypeFilter = mimeType;
- }
-
- final Bundle extras = intent.getExtras();
- final boolean isExtraPickImagesMaxSet =
- extras != null && extras.containsKey(MediaStore.EXTRA_PICK_IMAGES_MAX);
- // 1. Check EXTRA_PICK_IMAGES_MAX only if EXTRA_ALLOW_MULTIPLE is set.
- // 2. Do not show "Set up to max items" message if EXTRA_PICK_IMAGES_MAX is not set
- if (mSelectMultiple && isExtraPickImagesMaxSet) {
- final int extraMax = intent.getIntExtra(MediaStore.EXTRA_PICK_IMAGES_MAX,
- /* defaultValue */ -1);
- // Multi selection max limit should always be greater than 0
- if (extraMax <= 0) {
- throw new IllegalArgumentException("Invalid EXTRA_PICK_IMAGES_MAX value");
- }
- // Multi selection limit should always be less than global max values allowed to select.
- if (extraMax <= DEFAULT_MAX_SELECTION_LIMIT) {
- mMaxSelectionLimit = extraMax;
- }
- }
- }
-
- private static boolean isMimeTypeMedia(@Nullable String mimeType) {
- return isImageMimeType(mimeType) || isVideoMimeType(mimeType);
- }
-
- /**
- * Return maximum limit of items that can be selected
- */
- public int getMaxSelectionLimit() {
- return mMaxSelectionLimit;
- }
-
- /**
- * Return whether more items can be selected or not.
- */
- public boolean isSelectionAllowed() {
- return mIsSelectionAllowed;
- }
-
- /**
- * Set BottomSheet state
- */
- public void setBottomSheetState(int state) {
- mBottomSheetState = state;
- }
-
- /**
- * @return BottomSheet state
- */
- public int getBottomSheetState() {
- return mBottomSheetState;
- }
-}
diff --git a/src/com/android/providers/media/scan/LegacyMediaScanner.java b/src/com/android/providers/media/scan/LegacyMediaScanner.java
index d8d3bed..0a53a0d 100644
--- a/src/com/android/providers/media/scan/LegacyMediaScanner.java
+++ b/src/com/android/providers/media/scan/LegacyMediaScanner.java
@@ -21,8 +21,6 @@
import androidx.annotation.Nullable;
-import com.android.providers.media.MediaVolume;
-
import java.io.File;
@Deprecated
@@ -54,7 +52,7 @@
}
@Override
- public void onDetachVolume(MediaVolume volume) {
+ public void onDetachVolume(String volumeName) {
throw new UnsupportedOperationException();
}
diff --git a/src/com/android/providers/media/scan/MediaScanner.java b/src/com/android/providers/media/scan/MediaScanner.java
index 45d2a24..1306873 100644
--- a/src/com/android/providers/media/scan/MediaScanner.java
+++ b/src/com/android/providers/media/scan/MediaScanner.java
@@ -26,8 +26,6 @@
import androidx.annotation.Nullable;
-import com.android.providers.media.MediaVolume;
-
import java.io.File;
public interface MediaScanner {
@@ -40,7 +38,7 @@
public void scanDirectory(File file, int reason);
public Uri scanFile(File file, int reason);
public Uri scanFile(File file, int reason, @Nullable String ownerPackage);
- public void onDetachVolume(MediaVolume volume);
+ public void onDetachVolume(String volumeName);
public void onIdleScanStopped();
public void onDirectoryDirty(File file);
}
diff --git a/src/com/android/providers/media/scan/ModernMediaScanner.java b/src/com/android/providers/media/scan/ModernMediaScanner.java
index 43e2866..334f84f 100644
--- a/src/com/android/providers/media/scan/ModernMediaScanner.java
+++ b/src/com/android/providers/media/scan/ModernMediaScanner.java
@@ -37,7 +37,6 @@
import static android.media.MediaMetadataRetriever.METADATA_KEY_MIMETYPE;
import static android.media.MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS;
import static android.media.MediaMetadataRetriever.METADATA_KEY_TITLE;
-import static android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_CODEC_MIME_TYPE;
import static android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT;
import static android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION;
import static android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH;
@@ -92,12 +91,11 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import com.android.modules.utils.build.SdkLevel;
-import com.android.providers.media.MediaVolume;
import com.android.providers.media.util.DatabaseUtils;
import com.android.providers.media.util.ExifUtils;
import com.android.providers.media.util.FileUtils;
import com.android.providers.media.util.IsoInterface;
+import com.android.providers.media.util.Logging;
import com.android.providers.media.util.LongArray;
import com.android.providers.media.util.Metrics;
import com.android.providers.media.util.MimeUtils;
@@ -175,6 +173,12 @@
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
static final int MAX_EXCLUDE_DIRS = 450;
+ private static final Pattern PATTERN_VISIBLE = Pattern.compile(
+ "(?i)^/storage/[^/]+(?:/[0-9]+)?(?:/Android/sandbox/([^/]+))?$");
+ private static final Pattern PATTERN_INVISIBLE = Pattern.compile(
+ "(?i)^/storage/[^/]+(?:/[0-9]+)?(?:/Android/sandbox/([^/]+))?/" +
+ "(?:(?:Android/(?:data|obb)$)|(?:(?: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(
@@ -263,10 +267,10 @@
}
@Override
- public void onDetachVolume(MediaVolume volume) {
+ public void onDetachVolume(String volumeName) {
synchronized (mActiveScans) {
for (Scan scan : mActiveScans) {
- if (volume.equals(scan.mVolume)) {
+ if (volumeName.equals(scan.mVolumeName)) {
scan.mSignal.cancel();
}
}
@@ -315,7 +319,6 @@
private final File mRoot;
private final int mReason;
- private final MediaVolume mVolume;
private final String mVolumeName;
private final Uri mFilesUri;
private final CancellationSignal mSignal;
@@ -358,13 +361,7 @@
mRoot = root;
mReason = reason;
-
- if (FileUtils.contains(Environment.getStorageDirectory(), root)) {
- mVolume = MediaVolume.fromStorageVolume(FileUtils.getStorageVolume(mContext, root));
- } else {
- mVolume = MediaVolume.fromInternal();
- }
- mVolumeName = mVolume.getName();
+ mVolumeName = FileUtils.getVolumeName(mContext, root);
mFilesUri = MediaStore.Files.getContentUri(mVolumeName);
mSignal = new CancellationSignal();
@@ -658,8 +655,6 @@
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
@@ -1218,11 +1213,6 @@
sAudioTypes.put(Environment.DIRECTORY_PODCASTS, AudioColumns.IS_PODCAST);
sAudioTypes.put(Environment.DIRECTORY_AUDIOBOOKS, AudioColumns.IS_AUDIOBOOK);
sAudioTypes.put(Environment.DIRECTORY_MUSIC, AudioColumns.IS_MUSIC);
- if (SdkLevel.isAtLeastS()) {
- sAudioTypes.put(Environment.DIRECTORY_RECORDINGS, AudioColumns.IS_RECORDING);
- } else {
- sAudioTypes.put(FileUtils.DIRECTORY_RECORDINGS, AudioColumns.IS_RECORDING);
- }
}
private static @NonNull ContentProviderOperation.Builder scanItemAudio(long existingId,
@@ -1311,7 +1301,6 @@
op.withValue(VideoColumns.COLOR_STANDARD, null);
op.withValue(VideoColumns.COLOR_TRANSFER, null);
op.withValue(VideoColumns.COLOR_RANGE, null);
- op.withValue(FileColumns._VIDEO_CODEC_TYPE, null);
try (FileInputStream is = new FileInputStream(file)) {
try (MediaMetadataRetriever mmr = new MediaMetadataRetriever()) {
@@ -1334,8 +1323,6 @@
parseOptional(mmr.extractMetadata(METADATA_KEY_COLOR_TRANSFER)));
withOptionalValue(op, VideoColumns.COLOR_RANGE,
parseOptional(mmr.extractMetadata(METADATA_KEY_COLOR_RANGE)));
- withOptionalValue(op, FileColumns._VIDEO_CODEC_TYPE,
- parseOptional(mmr.extractMetadata(METADATA_KEY_VIDEO_CODEC_MIME_TYPE)));
}
// Also hunt around for XMP metadata
@@ -1671,14 +1658,14 @@
// Handle well-known paths that should always be visible or invisible,
// regardless of .nomedia presence
- if (FileUtils.shouldBeVisible(dir.getAbsolutePath())) {
+ if (PATTERN_VISIBLE.matcher(dir.getAbsolutePath()).matches()) {
// Well known paths can never be a hidden directory. Delete any non-standard nomedia
// presence in well known path.
nomedia.delete();
return true;
}
- if (FileUtils.shouldBeInvisible(dir.getAbsolutePath())) {
+ if (PATTERN_INVISIBLE.matcher(dir.getAbsolutePath()).matches()) {
// 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/NullMediaScanner.java b/src/com/android/providers/media/scan/NullMediaScanner.java
index 7a1a396..3f84109 100644
--- a/src/com/android/providers/media/scan/NullMediaScanner.java
+++ b/src/com/android/providers/media/scan/NullMediaScanner.java
@@ -23,8 +23,6 @@
import androidx.annotation.Nullable;
-import com.android.providers.media.MediaVolume;
-
import java.io.File;
/**
@@ -63,7 +61,7 @@
}
@Override
- public void onDetachVolume(MediaVolume volume) {
+ public void onDetachVolume(String volumeName) {
// Ignored
}
diff --git a/src/com/android/providers/media/scan/UnreliableVolumeScanner.java b/src/com/android/providers/media/scan/UnreliableVolumeScanner.java
deleted file mode 100644
index 58715ad..0000000
--- a/src/com/android/providers/media/scan/UnreliableVolumeScanner.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.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/FileUtils.java b/src/com/android/providers/media/util/FileUtils.java
index 0e50fd8..1b1ee49 100644
--- a/src/com/android/providers/media/util/FileUtils.java
+++ b/src/com/android/providers/media/util/FileUtils.java
@@ -45,13 +45,9 @@
import android.content.ClipDescription;
import android.content.ContentValues;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.net.Uri;
-import android.os.Build;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
-import android.os.UserHandle;
-import android.os.SystemProperties;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.provider.MediaStore;
@@ -68,8 +64,6 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import com.android.modules.utils.build.SdkLevel;
-
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
@@ -863,39 +857,16 @@
}
/**
- * Return StorageVolume corresponding to the file on Path
- */
- public static @NonNull StorageVolume getStorageVolume(@NonNull Context context,
- @NonNull File path) throws FileNotFoundException {
- int userId = extractUserId(path.getPath());
- Context userContext = context;
- if (userId >= 0 && (context.getUser().getIdentifier() != userId)) {
- // This volume is for a different user than our context, create a context
- // for that user to retrieve the correct volume.
- try {
- userContext = context.createPackageContextAsUser("system", 0,
- UserHandle.of(userId));
- } catch (PackageManager.NameNotFoundException e) {
- throw new FileNotFoundException("Can't get package context for user " + userId);
- }
- }
-
- StorageVolume volume = userContext.getSystemService(StorageManager.class)
- .getStorageVolume(path);
- if (volume == null) {
- throw new FileNotFoundException("Can't find volume for " + path.getPath());
- }
-
- return volume;
- }
-
- /**
* Return volume name which hosts the given path.
*/
public static @NonNull String getVolumeName(@NonNull Context context, @NonNull File path)
throws FileNotFoundException {
if (contains(Environment.getStorageDirectory(), path)) {
- StorageVolume volume = getStorageVolume(context, path);
+ StorageVolume volume = context.getSystemService(StorageManager.class)
+ .getStorageVolume(path);
+ if (volume == null) {
+ throw new FileNotFoundException("Can't find volume for " + path.getPath());
+ }
return volume.getMediaStoreVolumeName();
} else {
return MediaStore.VOLUME_INTERNAL;
@@ -903,9 +874,9 @@
}
public static final Pattern PATTERN_DOWNLOADS_FILE = Pattern.compile(
- "(?i)^/storage/[^/]+/(?:[0-9]+/)?Download/.+");
+ "(?i)^/storage/[^/]+/(?:[0-9]+/)?(?:Android/sandbox/[^/]+/)?Download/.+");
public static final Pattern PATTERN_DOWNLOADS_DIRECTORY = Pattern.compile(
- "(?i)^/storage/[^/]+/(?:[0-9]+/)?Download/?");
+ "(?i)^/storage/[^/]+/(?:[0-9]+/)?(?:Android/sandbox/[^/]+/)?Download/?");
public static final Pattern PATTERN_EXPIRES_FILE = Pattern.compile(
"(?i)^\\.(pending|trashed)-(\\d+)-([^/]+)$");
public static final Pattern PATTERN_PENDING_FILEPATH_FOR_SQL = Pattern.compile(
@@ -947,96 +918,46 @@
return PATTERN_DOWNLOADS_DIRECTORY.matcher(path).matches();
}
- private static final boolean PROP_CROSS_USER_ALLOWED =
- SystemProperties.getBoolean("external_storage.cross_user.enabled", false);
-
- private static final String PROP_CROSS_USER_ROOT = isCrossUserEnabled()
- ? SystemProperties.get("external_storage.cross_user.root", "") : "";
-
- private static final String PROP_CROSS_USER_ROOT_PATTERN = ((PROP_CROSS_USER_ROOT.isEmpty())
- ? "" : "(?:" + PROP_CROSS_USER_ROOT + "/)?");
-
/**
* Regex that matches paths in all well-known package-specific directories,
* and which captures the package name as the first group.
*/
public static final Pattern PATTERN_OWNED_PATH = Pattern.compile(
- "(?i)^/storage/[^/]+/(?:[0-9]+/)?"
- + PROP_CROSS_USER_ROOT_PATTERN
- + "Android/(?:data|media|obb)/([^/]+)(/?.*)?");
+ "(?i)^/storage/[^/]+/(?:[0-9]+/)?Android/(?:data|media|obb|sandbox)/([^/]+)(/?.*)?");
/**
* Regex that matches Android/obb or Android/data path.
*/
public static final Pattern PATTERN_DATA_OR_OBB_PATH = Pattern.compile(
- "(?i)^/storage/[^/]+/(?:[0-9]+/)?"
- + PROP_CROSS_USER_ROOT_PATTERN
- + "Android/(?:data|obb)/?$");
+ "(?i)^/storage/[^/]+/(?:[0-9]+/)?Android/(?:data|obb)/?$");
/**
* Regex that matches Android/obb paths.
*/
public static final Pattern PATTERN_OBB_OR_CHILD_PATH = Pattern.compile(
- "(?i)^/storage/[^/]+/(?:[0-9]+/)?"
- + PROP_CROSS_USER_ROOT_PATTERN
- + "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.
- */
- public static final String DIRECTORY_RECORDINGS = "Recordings";
+ "(?i)^/storage/[^/]+/(?:[0-9]+/)?Android/(?:obb)(/?.*)");
@VisibleForTesting
- public static final String[] DEFAULT_FOLDER_NAMES;
- static {
- if (SdkLevel.isAtLeastS()) {
- DEFAULT_FOLDER_NAMES = new String[]{
- Environment.DIRECTORY_MUSIC,
- Environment.DIRECTORY_PODCASTS,
- Environment.DIRECTORY_RINGTONES,
- Environment.DIRECTORY_ALARMS,
- Environment.DIRECTORY_NOTIFICATIONS,
- Environment.DIRECTORY_PICTURES,
- Environment.DIRECTORY_MOVIES,
- Environment.DIRECTORY_DOWNLOADS,
- Environment.DIRECTORY_DCIM,
- Environment.DIRECTORY_DOCUMENTS,
- Environment.DIRECTORY_AUDIOBOOKS,
- Environment.DIRECTORY_RECORDINGS,
- };
- } else {
- DEFAULT_FOLDER_NAMES = new String[]{
- Environment.DIRECTORY_MUSIC,
- Environment.DIRECTORY_PODCASTS,
- Environment.DIRECTORY_RINGTONES,
- Environment.DIRECTORY_ALARMS,
- Environment.DIRECTORY_NOTIFICATIONS,
- Environment.DIRECTORY_PICTURES,
- Environment.DIRECTORY_MOVIES,
- Environment.DIRECTORY_DOWNLOADS,
- Environment.DIRECTORY_DCIM,
- Environment.DIRECTORY_DOCUMENTS,
- Environment.DIRECTORY_AUDIOBOOKS,
- DIRECTORY_RECORDINGS,
- };
- }
- }
+ public static final String[] DEFAULT_FOLDER_NAMES = {
+ Environment.DIRECTORY_MUSIC,
+ Environment.DIRECTORY_PODCASTS,
+ Environment.DIRECTORY_RINGTONES,
+ Environment.DIRECTORY_ALARMS,
+ Environment.DIRECTORY_NOTIFICATIONS,
+ Environment.DIRECTORY_PICTURES,
+ Environment.DIRECTORY_MOVIES,
+ Environment.DIRECTORY_DOWNLOADS,
+ Environment.DIRECTORY_DCIM,
+ Environment.DIRECTORY_DOCUMENTS,
+ Environment.DIRECTORY_AUDIOBOOKS,
+ };
/**
- * Regex that matches paths for {@link MediaColumns#RELATIVE_PATH}
+ * Regex that matches paths for {@link MediaColumns#RELATIVE_PATH}; it
+ * captures both top-level paths and sandboxed paths.
*/
private static final Pattern PATTERN_RELATIVE_PATH = Pattern.compile(
- "(?i)^/storage/(?:emulated/[0-9]+/|[^/]+/)");
+ "(?i)^/storage/(?:emulated/[0-9]+/|[^/]+/)(Android/sandbox/([^/]+)/)?");
/**
* Regex that matches paths under well-known storage paths.
@@ -1044,33 +965,13 @@
private static final Pattern PATTERN_VOLUME_NAME = Pattern.compile(
"(?i)^/storage/([^/]+)");
- /**
- * Regex that matches user-ids under well-known storage paths.
- */
- private static final Pattern PATTERN_USER_ID = Pattern.compile(
- "(?i)^/storage/emulated/([0-9]+)");
-
private static final String CAMERA_RELATIVE_PATH =
String.format("%s/%s/", Environment.DIRECTORY_DCIM, "Camera");
- public static boolean isCrossUserEnabled() {
- return PROP_CROSS_USER_ALLOWED || SdkLevel.isAtLeastS();
- }
-
private static @Nullable String normalizeUuid(@Nullable String fsUuid) {
return fsUuid != null ? fsUuid.toLowerCase(Locale.ROOT) : null;
}
- public static int extractUserId(@Nullable String data) {
- if (data == null) return -1;
- final Matcher matcher = PATTERN_USER_ID.matcher(data);
- if (matcher.find()) {
- return Integer.parseInt(matcher.group(1));
- }
-
- return -1;
- }
-
public static @Nullable String extractVolumePath(@Nullable String data) {
if (data == null) return null;
final Matcher matcher = PATTERN_RELATIVE_PATH.matcher(data);
@@ -1156,21 +1057,6 @@
}
}
- public static boolean isExternalMediaDirectory(@NonNull String path) {
- return isExternalMediaDirectory(path, PROP_CROSS_USER_ROOT);
- }
-
- @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);
- }
- return false;
- }
-
/**
* Returns true if relative path is Android/data or Android/obb path.
*/
@@ -1189,18 +1075,6 @@
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.
@@ -1211,26 +1085,8 @@
if (relativePath == null) {
return null;
}
-
- return extractTopLevelDir(relativePath.split("/"));
- }
-
- @Nullable
- public static String extractTopLevelDir(String[] relativePathSegments) {
- return extractTopLevelDir(relativePathSegments, PROP_CROSS_USER_ROOT);
- }
-
- @VisibleForTesting
- @Nullable
- static String extractTopLevelDir(String[] relativePathSegments, String crossUserRoot) {
- if (relativePathSegments == null) return null;
-
- final String topLevelDir = relativePathSegments.length > 0 ? relativePathSegments[0] : null;
- if (crossUserRoot != null && crossUserRoot.equals(topLevelDir)) {
- return relativePathSegments.length > 1 ? relativePathSegments[1] : null;
- }
-
- return topLevelDir;
+ final String[] relativePathSegments = relativePath.split("/");
+ return relativePathSegments.length > 0 ? relativePathSegments[0] : null;
}
public static boolean isDefaultDirectoryName(@Nullable String dirName) {
@@ -1453,11 +1309,6 @@
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()));
@@ -1475,42 +1326,12 @@
return false;
}
- if (isScreenshotsDirNonHidden(relativePath, name)) {
- nomedia.delete();
- return false;
- }
-
// .nomedia is present which makes this directory as hidden directory
Logging.logPersistent("Observed non-standard " + nomedia);
return true;
}
/**
- * Consider Screenshots directory in root directory or inside well-known directory as always
- * non-hidden. Nomedia file in these directories will not be able to hide these directories.
- * i.e., some examples of directories that will be considered non-hidden are
- * <ul>
- * <li> /storage/emulated/0/Screenshots or
- * <li> /storage/emulated/0/DCIM/Screenshots or
- * <li> /storage/emulated/0/Pictures/Screenshots ...
- * </ul>
- * Some examples of directories that can be considered as hidden with nomedia are
- * <ul>
- * <li> /storage/emulated/0/foo/Screenshots or
- * <li> /storage/emulated/0/DCIM/Foo/Screenshots or
- * <li> /storage/emulated/0/Pictures/foo/bar/Screenshots ...
- * </ul>
- */
- private static boolean isScreenshotsDirNonHidden(@NonNull String[] relativePath,
- @NonNull String name) {
- if (name.equalsIgnoreCase(Environment.DIRECTORY_SCREENSHOTS)) {
- return (relativePath.length == 1 &&
- (TextUtils.isEmpty(relativePath[0]) || isDefaultDirectoryName(relativePath[0])));
- }
- return false;
- }
-
- /**
* Test if this given file should be considered hidden.
*/
@VisibleForTesting
@@ -1565,8 +1386,7 @@
}
/**
- * @return {@code true} if {@code dir} has nomedia and it is dirty directory, so it should be
- * scanned. Returns {@code false} otherwise.
+ * @return {@code true} if {@code dir} is dirty and should be scanned, {@code false} otherwise.
*/
public static boolean isDirectoryDirty(File dir) {
File nomedia = new File(dir, ".nomedia");
@@ -1581,7 +1401,7 @@
Log.w(TAG, "Failed to read directory dirty" + dir);
}
}
- return false;
+ return true;
}
/**
diff --git a/src/com/android/providers/media/util/Metrics.java b/src/com/android/providers/media/util/Metrics.java
index 024151d..410da3a 100644
--- a/src/com/android/providers/media/util/Metrics.java
+++ b/src/com/android/providers/media/util/Metrics.java
@@ -111,10 +111,10 @@
}
public static void logSchemaChange(@NonNull String volumeName, int versionFrom, int versionTo,
- long itemCount, long durationMillis, @NonNull String databaseUuid) {
+ long itemCount, long durationMillis) {
Logging.logPersistent(String.format(
- "Changed schema version on %s from %d to %d, %d items taking %dms UUID %s",
- volumeName, versionFrom, versionTo, itemCount, durationMillis, databaseUuid));
+ "Changed schema version on %s from %d to %d, %d items taking %dms",
+ volumeName, versionFrom, versionTo, itemCount, durationMillis));
final float normalizedDurationMillis = ((float) durationMillis) / itemCount;
diff --git a/src/com/android/providers/media/util/MimeUtils.java b/src/com/android/providers/media/util/MimeUtils.java
index 38c3635..09417bd 100644
--- a/src/com/android/providers/media/util/MimeUtils.java
+++ b/src/com/android/providers/media/util/MimeUtils.java
@@ -127,11 +127,6 @@
return 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) {
if (mimeType == null) return false;
switch (mimeType.toLowerCase(Locale.ROOT)) {
diff --git a/src/com/android/providers/media/util/PermissionUtils.java b/src/com/android/providers/media/util/PermissionUtils.java
index 28dc14b..d1dd4b2 100644
--- a/src/com/android/providers/media/util/PermissionUtils.java
+++ b/src/com/android/providers/media/util/PermissionUtils.java
@@ -16,19 +16,16 @@
package com.android.providers.media.util;
-import static android.Manifest.permission.ACCESS_MEDIA_LOCATION;
import static android.Manifest.permission.ACCESS_MTP;
import static android.Manifest.permission.BACKUP;
import static android.Manifest.permission.INSTALL_PACKAGES;
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.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;
@@ -41,7 +38,6 @@
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;
@@ -49,6 +45,8 @@
public class PermissionUtils {
+ public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
+
// Callers must hold both the old and new permissions, so that we can
// handle obscure cases like when an app targets Q but was installed on
// a device that was originally running on P before being upgraded to Q.
@@ -62,7 +60,7 @@
public static void clearOpDescription() { sOpDescription.set(null); }
public static boolean checkPermissionSelf(@NonNull Context context, int pid, int uid) {
- return UserHandle.getAppId(android.os.Process.myUid()) == UserHandle.getAppId(uid);
+ return android.os.Process.myUid() == uid;
}
public static boolean checkPermissionShell(@NonNull Context context, int pid, int uid) {
@@ -81,9 +79,13 @@
*/
public static boolean checkPermissionManager(@NonNull Context context, int pid,
int uid, @NonNull String packageName, @Nullable String attributionTag) {
- return checkPermissionForDataDelivery(context, MANAGE_EXTERNAL_STORAGE, pid, uid,
+ if (checkPermissionForDataDelivery(context, MANAGE_EXTERNAL_STORAGE, pid, uid,
packageName, attributionTag,
- generateAppOpMessage(packageName,sOpDescription.get()));
+ generateAppOpMessage(packageName,sOpDescription.get()))) {
+ return true;
+ }
+ // Fallback to OPSTR_NO_ISOLATED_STORAGE app op.
+ return checkNoIsolatedStorageGranted(context, uid, packageName, attributionTag);
}
/**
@@ -117,34 +119,10 @@
generateAppOpMessage(packageName,sOpDescription.get()));
}
- /**
- * Check if the given package has been granted the
- * android.Manifest.permission#ACCESS_MEDIA_LOCATION permission.
- */
- public static boolean checkPermissionAccessMediaLocation(@NonNull Context context, int pid,
- int uid, @NonNull String packageName, @Nullable String attributionTag) {
- return checkPermissionForDataDelivery(context, ACCESS_MEDIA_LOCATION, pid, uid, packageName,
- attributionTag, generateAppOpMessage(packageName, sOpDescription.get()));
- }
-
- /**
- * Check if the given package has been granted the
- * android.Manifest.permission#MANAGE_MEDIA permission.
- */
- public static boolean checkPermissionManageMedia(@NonNull Context context, int pid, int uid,
- @NonNull String packageName, @Nullable String attributionTag) {
- return checkPermissionForDataDelivery(context, MANAGE_MEDIA, pid, uid, packageName,
- attributionTag, generateAppOpMessage(packageName, sOpDescription.get()));
- }
-
- public static boolean checkIsLegacyStorageGranted(@NonNull Context context, int uid,
- String packageName, @Nullable String attributionTag) {
- if (context.getSystemService(AppOpsManager.class)
- .unsafeCheckOp(OPSTR_LEGACY_STORAGE, uid, packageName) == MODE_ALLOWED) {
- return true;
- }
- // Check OPSTR_NO_ISOLATED_STORAGE app op.
- return checkNoIsolatedStorageGranted(context, uid, packageName, attributionTag);
+ public static boolean checkIsLegacyStorageGranted(
+ @NonNull Context context, int uid, String packageName) {
+ return context.getSystemService(AppOpsManager.class)
+ .unsafeCheckOp(OPSTR_LEGACY_STORAGE, uid, packageName) == MODE_ALLOWED;
}
public static boolean checkPermissionReadAudio(@NonNull Context context, int pid, int uid,
@@ -430,7 +408,6 @@
private static boolean isAppOpPermission(String permission) {
switch (permission) {
case MANAGE_EXTERNAL_STORAGE:
- case MANAGE_MEDIA:
return true;
}
return false;
@@ -438,7 +415,6 @@
private static boolean isRuntimePermission(String permission) {
switch (permission) {
- case ACCESS_MEDIA_LOCATION:
case READ_EXTERNAL_STORAGE:
case WRITE_EXTERNAL_STORAGE:
return true;
diff --git a/src/com/android/providers/media/util/UserCache.java b/src/com/android/providers/media/util/UserCache.java
deleted file mode 100644
index a5b05c4..0000000
--- a/src/com/android/providers/media/util/UserCache.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.util;
-
-import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
-import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
-
-import android.app.admin.DevicePolicyManager;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.os.Process;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.LongSparseArray;
-
-import androidx.annotation.GuardedBy;
-import androidx.annotation.NonNull;
-
-import com.android.modules.utils.build.SdkLevel;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * UserCache is a class that keeps track of all users that the current MediaProvider
- * instance is responsible for. By default, it handles storage for the user it is running as,
- * but as of Android API 31, it will also handle storage for profiles that share media
- * with their parent - profiles for which @link{UserManager#isMediaSharedWithParent} is set.
- *
- * It also keeps a cache of user contexts, for improving these lookups.
- *
- * Note that we don't use the USER_ broadcasts for keeping this state up to date, because they
- * aren't guaranteed to be received before the volume events for a user.
- */
-public class UserCache {
- final Object mLock = new Object();
- final Context mContext;
- final UserManager mUserManager;
-
- @GuardedBy("mLock")
- final LongSparseArray<Context> mUserContexts = new LongSparseArray<>();
- @GuardedBy("mLock")
- final LongSparseArray<Boolean> mUserIsWorkProfile = new LongSparseArray<>();
-
- @GuardedBy("mLock")
- final ArrayList<UserHandle> mUsers = new ArrayList<>();
-
- public UserCache(Context context) {
- mContext = context;
- mUserManager = context.getSystemService(UserManager.class);
-
- update();
- }
-
- private void update() {
- List<UserHandle> profiles = mUserManager.getEnabledProfiles();
- synchronized (mLock) {
- mUsers.clear();
- // Add the user we're running as by default
- mUsers.add(Process.myUserHandle());
- if (!SdkLevel.isAtLeastS()) {
- // Before S, we only handle the owner user
- 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()) {
- mUsers.add(profile);
- }
- }
- }
- }
- }
-
- public @NonNull List<UserHandle> updateAndGetUsers() {
- update();
- synchronized (mLock) {
- return (List<UserHandle>) mUsers.clone();
- }
- }
-
- public @NonNull List<UserHandle> getUsersCached() {
- synchronized (mLock) {
- return (List<UserHandle>) mUsers.clone();
- }
- }
-
- public boolean isWorkProfile(int userId) {
- if (userId == 0) {
- // Owner user can not have a work profile
- return false;
- }
- synchronized (mLock) {
- int index = mUserIsWorkProfile.indexOfKey(userId);
- if (index >= 0) {
- return mUserIsWorkProfile.valueAt(index);
- }
- }
-
- Context userContext = getContextForUser(UserHandle.of(userId));
- PackageManager packageManager = userContext.getPackageManager();
- DevicePolicyManager policyManager = userContext.getSystemService(
- DevicePolicyManager.class);
- boolean isWorkProfile = false;
- for (ApplicationInfo ai : packageManager.getInstalledApplications(
- MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE)) {
- if (policyManager.isProfileOwnerApp(ai.packageName)) {
- isWorkProfile = true;
- }
- }
-
- synchronized (mLock) {
- mUserIsWorkProfile.put(userId, isWorkProfile);
- }
-
- return isWorkProfile;
- }
-
- public @NonNull Context getContextForUser(@NonNull UserHandle user) {
- Context userContext;
- synchronized (mLock) {
- userContext = mUserContexts.get(user.getIdentifier());
- if (userContext != null) {
- return userContext;
- }
- }
- try {
- userContext = mContext.createPackageContextAsUser("system", 0, user);
- synchronized (mLock) {
- mUserContexts.put(user.getIdentifier(), userContext);
- }
- return userContext;
- } catch (PackageManager.NameNotFoundException e) {
- throw new RuntimeException("Failed to create context for user " + user, e);
- }
- }
-
- /**
- * Returns whether the passed in user shares media with its parent (or peer).
- *
- * @param user user to check
- * @return whether the user shares media with its parent
- */
- public boolean userSharesMediaWithParent(@NonNull UserHandle user) {
- if (Process.myUserHandle().equals(user)) {
- // Early return path - the owner user doesn't have a parent
- return false;
- }
- boolean found = userSharesMediaWithParentCached(user);
- if (!found) {
- // Update the cache and try again
- update();
- found = userSharesMediaWithParentCached(user);
- }
- return found;
- }
-
- /**
- * Returns whether the passed in user shares media with its parent (or peer).
- * Note that the value returned here is based on cached data; it relies on
- * other callers to keep the user cache up-to-date.
- *
- * @param user user to check
- * @return whether the user shares media with its parent
- */
- public boolean userSharesMediaWithParentCached(@NonNull UserHandle user) {
- synchronized (mLock) {
- // It must be a user that we manage, and not equal to the main user that we run as
- return !Process.myUserHandle().equals(user) && mUsers.contains(user);
- }
- }
-
- public void dump(PrintWriter writer) {
- writer.println("User cache state:");
- synchronized (mLock) {
- for (UserHandle user : mUsers) {
- writer.println(" user: " + user);
- }
- }
- }
-}
diff --git a/tests/Android.bp b/tests/Android.bp
index 508a19a..25dd84a 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -1,93 +1,8 @@
-android_test_helper_app {
- name: "MediaProviderTestAppForPermissionActivity",
- manifest: "test_app/TestAppForPermissionActivity.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: [
- "device-tests",
- "mts-mediaprovider",
- ],
-}
-
-android_test_helper_app {
- name: "MediaProviderTestAppWithStoragePerms",
- manifest: "test_app/TestAppWithStoragePerms.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: [
- "device-tests",
- "mts-mediaprovider",
- ],
-}
-
-android_test_helper_app {
- name: "MediaProviderTestAppWithoutPerms",
- manifest: "test_app/TestAppWithoutPerms.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: [
- "device-tests",
- "mts-mediaprovider",
- ],
-}
-
-android_test_helper_app {
- name: "LegacyMediaProviderTestApp",
- manifest: "test_app/LegacyTestApp.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: "28",
- min_sdk_version: "30",
- test_suites: [
- "device-tests",
- "mts-mediaprovider",
- ],
-}
-
// This looks a bit awkward, but we need our tests to run against either
// MediaProvider or MediaProviderGoogle, and we don't know which one is
// 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: [
@@ -123,15 +38,7 @@
"androidx.test.rules",
"guava",
"mockito-target",
- "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",
],
certificate: "media",
@@ -144,15 +51,6 @@
"-Xep:MissingFail:ERROR",
],
},
-
- java_resources: [
- ":MediaProviderTestAppWithStoragePerms",
- ":MediaProviderTestAppWithoutPerms",
- ":MediaProviderTestAppForPermissionActivity",
- ":LegacyMediaProviderTestApp",
- ],
-
- min_sdk_version: "30",
}
filegroup {
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 6fa0824..b5c556a 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -2,69 +2,15 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.providers.media.tests">
- <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
-
<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" />
- <uses-permission android:name="android.permission.MANAGE_USERS" />
- <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
+
<application android:label="MediaProvider Tests">
<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.PhotoPickerActivity"
- android:theme="@style/PickerDefaultTheme"
- android:excludeFromRecents="true">
- <intent-filter>
- <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>
- <action android:name="android.provider.action.PICK_IMAGES" />
- <category android:name="android.intent.category.DEFAULT" />
- </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..fe7de6a 100644
--- a/tests/AndroidTest.xml
+++ b/tests/AndroidTest.xml
@@ -16,10 +16,6 @@
<configuration description="Runs Tests for MediaProvder.">
<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="MediaProviderTestAppWithStoragePerms.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>
diff --git a/tests/client/Android.bp b/tests/client/Android.bp
index 1541da6..a5b5beb 100644
--- a/tests/client/Android.bp
+++ b/tests/client/Android.bp
@@ -1,12 +1,3 @@
-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: "MediaProviderClientTests",
test_suites: [
diff --git a/tests/client/AndroidManifest.xml b/tests/client/AndroidManifest.xml
index f770559..42175b6 100644
--- a/tests/client/AndroidManifest.xml
+++ b/tests/client/AndroidManifest.xml
@@ -6,7 +6,7 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <application android:label="MediaProvider Tests" android:requestRawExternalStorageAccess="true">
+ <application android:label="MediaProvider Tests">
<uses-library android:name="android.test.runner" />
</application>
diff --git a/tests/client/AndroidTest.xml b/tests/client/AndroidTest.xml
index 8ebd0a5..22ca7d3 100644
--- a/tests/client/AndroidTest.xml
+++ b/tests/client/AndroidTest.xml
@@ -18,7 +18,6 @@
<option name="test-file-name" value="MediaProviderClientTests.apk" />
<option name="install-arg" value="-g" />
</target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="framework-base-presubmit" />
diff --git a/tests/client/res/raw/orientation_90.jpg b/tests/client/res/raw/orientation_90.jpg
deleted file mode 100644
index aefca34..0000000
--- a/tests/client/res/raw/orientation_90.jpg
+++ /dev/null
Binary files differ
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 26c40fc..042223c 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, use the test app without permissions
+ * 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.
*/
- private static final String PERMISSIONLESS_APP =
- "com.android.providers.media.testapp.withoutperms";
+ private static final String PERMISSIONLESS_APP = "com.android.egg";
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..e1af6f6 100644
--- a/tests/client/src/com/android/providers/media/client/DownloadProviderTest.java
+++ b/tests/client/src/com/android/providers/media/client/DownloadProviderTest.java
@@ -127,6 +127,9 @@
private void deleteOtherPackageExternalFiles(List<File> otherPackageDirs) throws Exception {
for (File dir: otherPackageDirs) {
executeShellCommand("rm -r " + dir.getAbsolutePath());
+ // Need to wait for the directory to be deleted, as it is flaky sometimes due to the
+ // race condition in rm and the next mkdir for the next test.
+ pollForDirectoryToBeDeleted(dir);
}
}
@@ -154,4 +157,13 @@
() -> dir.exists(),
"Timed out while waiting for dir " + dir + " to be created");
}
+
+ /**
+ * Polls for directory to be deleted
+ */
+ private static void pollForDirectoryToBeDeleted(File dir) throws Exception {
+ pollForCondition(
+ () -> !dir.exists(),
+ "Timed out while waiting for dir " + dir + " to be deleted");
+ }
}
diff --git a/tests/client/src/com/android/providers/media/client/LegacyProviderMigrationTest.java b/tests/client/src/com/android/providers/media/client/LegacyProviderMigrationTest.java
index dde3911..831aa9b 100644
--- a/tests/client/src/com/android/providers/media/client/LegacyProviderMigrationTest.java
+++ b/tests/client/src/com/android/providers/media/client/LegacyProviderMigrationTest.java
@@ -18,8 +18,6 @@
import static android.provider.MediaStore.rewriteToLegacy;
-import static com.google.common.truth.Truth.assertWithMessage;
-
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -38,7 +36,6 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
-import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.os.SystemClock;
import android.os.storage.StorageManager;
@@ -70,12 +67,10 @@
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
-import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
@@ -122,25 +117,15 @@
.appendQueryParameter("silent", "true").build();
}
- private ContentValues generateValues(int mediaType, String mimeType, String dirName)
- throws Exception {
- return generateValues(mediaType, mimeType, dirName, 0);
- }
-
- private ContentValues generateValues(int mediaType, String mimeType, String dirName, int resId)
- throws Exception {
+ private ContentValues generateValues(int mediaType, String mimeType, String dirName) {
final Context context = InstrumentationRegistry.getContext();
final File dir = context.getSystemService(StorageManager.class)
.getStorageVolume(MediaStore.Files.getContentUri(mVolumeName)).getDirectory();
final File subDir = new File(dir, dirName);
- File file = new File(subDir, "legacy" + System.nanoTime() + "."
+ final File file = new File(subDir, "legacy" + System.nanoTime() + "."
+ MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType));
- if (resId != 0) {
- file = stageFile(resId, file.getAbsolutePath());
- }
-
final ContentValues values = new ContentValues();
values.put(FileColumns.MEDIA_TYPE, mediaType);
values.put(MediaColumns.DATA, file.getAbsolutePath());
@@ -153,25 +138,6 @@
return values;
}
- private static File stageFile(int resId, String path) throws Exception {
- final Context context = InstrumentationRegistry.getContext();
- final File file = new File(path);
- try (InputStream in = context.getResources().openRawResource(resId);
- OutputStream out = new FileOutputStream(file)) {
- FileUtils.copy(in, out);
- }
- return file;
- }
-
- @Test
- public void testLegacy_Orientation() throws Exception {
- // Use an image file with orientation of 90 degrees
- final ContentValues values = generateValues(FileColumns.MEDIA_TYPE_IMAGE,
- "image/jpeg", Environment.DIRECTORY_PICTURES, R.raw.orientation_90);
- values.put(MediaColumns.ORIENTATION, String.valueOf(90));
- doLegacy(mExternalImages, values);
- }
-
@Test
public void testLegacy_Pending() throws Exception {
final ContentValues values = generateValues(FileColumns.MEDIA_TYPE_IMAGE,
@@ -489,31 +455,12 @@
values.remove(FileColumns.DATA);
}
- // This will delete MediaProvider data and restarts MediaProvider, and mounts storage.
clearProviders(context, ui);
- // Make sure we do not lose the ORIENTATION column after database migration
- // We check this column again after the scan
- if (values.getAsString(MediaColumns.ORIENTATION) != null) {
- assertOrientationColumn(collectionUri, values, context, legacyFile);
- }
-
// And force a scan to confirm upgraded data survives
MediaStore.scanVolume(context.getContentResolver(),
MediaStore.getVolumeName(collectionUri));
- assertColumnsHaveExpectedValues(collectionUri, values, context, legacyFile);
- }
-
- private void assertOrientationColumn(Uri collectionUri, ContentValues originalValues,
- Context context, File legacyFile) throws Exception {
- final ContentValues values = new ContentValues();
- values.put(MediaColumns.ORIENTATION, (String) originalValues.get(MediaColumns.ORIENTATION));
- assertColumnsHaveExpectedValues(collectionUri, values, context, legacyFile);
- }
-
- private void assertColumnsHaveExpectedValues(Uri collectionUri, ContentValues values,
- Context context, File legacyFile) throws Exception {
// Confirm that details from legacy provider have migrated
try (ContentProviderClient modern = context.getContentResolver()
.acquireContentProviderClient(MediaStore.AUTHORITY)) {
@@ -526,14 +473,13 @@
extras.putInt(MediaStore.QUERY_ARG_MATCH_TRASHED, MediaStore.MATCH_INCLUDE);
extras.putInt(MediaStore.QUERY_ARG_MATCH_FAVORITE, MediaStore.MATCH_INCLUDE);
- try (Cursor cursor = pollForCursor(modern, collectionUri, extras)) {
- assertNotNull(cursor);
+ try (Cursor cursor = modern.query(collectionUri, null, extras, null)) {
assertTrue(cursor.moveToFirst());
+ final ContentValues actualValues = new ContentValues();
for (String key : values.keySet()) {
- assertWithMessage("Checking key %s", key)
- .that(cursor.getString(cursor.getColumnIndexOrThrow(key)))
- .isEqualTo(values.get(key));
+ actualValues.put(key, cursor.getString(cursor.getColumnIndexOrThrow(key)));
}
+ assertEquals(values, actualValues);
}
}
}
@@ -549,23 +495,6 @@
MediaStore.waitForIdle(resolver);
}
- private static Cursor pollForCursor(ContentProviderClient modern, Uri collectionUri,
- Bundle extras) throws Exception {
- Cursor cursor = null;
- for (int i = 0; i < POLLING_TIMEOUT_MILLIS / POLLING_SLEEP_MILLIS; i++) {
- try {
- cursor = modern.query(collectionUri, null, extras, null);
- return cursor;
- } catch (IllegalArgumentException e) {
- // try again
- }
- Log.v(TAG, "Waiting for..." + collectionUri);
- SystemClock.sleep(POLLING_SLEEP_MILLIS);
- }
- fail("Timed out while waiting for uri " + collectionUri);
- return cursor;
- }
-
private static void pollForFile(File file) {
for (int i = 0; i < POLLING_TIMEOUT_MILLIS / POLLING_SLEEP_MILLIS; i++) {
if (file.exists()) return;
diff --git a/tests/client/src/com/android/providers/media/client/PerformanceTest.java b/tests/client/src/com/android/providers/media/client/PerformanceTest.java
index 0088514..72b4234 100644
--- a/tests/client/src/com/android/providers/media/client/PerformanceTest.java
+++ b/tests/client/src/com/android/providers/media/client/PerformanceTest.java
@@ -37,16 +37,13 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
-import androidx.test.uiautomator.UiDevice;
import com.android.providers.media.tests.utils.Timer;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.BufferedWriter;
import java.io.File;
-import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -59,12 +56,8 @@
/**
* Since we're right in the critical path between camera and gallery apps, we
* need to meet some pretty strict performance deadlines.
- *
- * This test is marked as {@code LargeTest} for it to not run in presubmit as it does not make any
- * assertions, and any performance regressions are caught separately by Crystallball.
*/
@RunWith(AndroidJUnit4.class)
-@LargeTest
public class PerformanceTest {
private static final String TAG = "PerformanceTest";
@@ -88,9 +81,19 @@
doSingle(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, timers);
}
- // The numbers dumped by the timers are monitored using crystalball and regressions are
- // reported from there.
timers.dumpResults();
+
+ // Verify that core actions finished within 30ms deadline
+ final long actionDeadline = 30;
+ assertThat(timers.actionInsert.getAverageDurationMillis()).isLessThan(actionDeadline);
+ assertThat(timers.actionUpdate.getAverageDurationMillis()).isLessThan(actionDeadline);
+ assertThat(timers.actionDelete.getAverageDurationMillis()).isLessThan(actionDeadline);
+
+ // Verify that external notifications finished within 30ms deadline
+ final long notifyDeadline = 30;
+ assertThat(timers.notifyInsert.getAverageDurationMillis()).isLessThan(notifyDeadline);
+ assertThat(timers.notifyUpdate.getAverageDurationMillis()).isLessThan(notifyDeadline);
+ assertThat(timers.notifyDelete.getAverageDurationMillis()).isLessThan(notifyDeadline);
}
private void doSingle(Uri collection, Timers timers) throws Exception {
@@ -157,9 +160,19 @@
doBulk(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, timers);
}
- // The numbers dumped by the timers are monitored using crystalball and regressions are
- // reported from there.
timers.dumpResults();
+
+ // Verify that core actions finished within 30ms deadline
+ final long actionDeadline = 30 * COUNT_BULK;
+ assertThat(timers.actionInsert.getAverageDurationMillis()).isLessThan(actionDeadline);
+ assertThat(timers.actionUpdate.getAverageDurationMillis()).isLessThan(actionDeadline);
+ assertThat(timers.actionDelete.getAverageDurationMillis()).isLessThan(actionDeadline);
+
+ // Verify that external notifications finished within 100ms deadline
+ final long notifyDeadline = 100;
+ assertThat(timers.notifyInsert.getAverageDurationMillis()).isLessThan(notifyDeadline);
+ assertThat(timers.notifyUpdate.getAverageDurationMillis()).isLessThan(notifyDeadline);
+ assertThat(timers.notifyDelete.getAverageDurationMillis()).isLessThan(notifyDeadline);
}
private void doBulk(Uri collection, Timers timers) throws Exception {
@@ -244,6 +257,7 @@
testDirOperations_size(500);
}
+ @LargeTest
@Test
public void testDirOperations_1000() throws Exception {
testDirOperations_size(1000);
@@ -251,8 +265,7 @@
private void testDirOperations_size(int size) throws Exception {
Timer createTimer = new Timer("mkdir");
- Timer readdirTimer = new Timer("readdir");
- Timer isFileTimer = new Timer("isFile");
+ Timer readTimer = new Timer("readdir");
// We have different timers for rename dir only and rename files as we want to track the
// performance for both of the following:
// 1. Renaming a directory is significantly faster (for file managers) as we do not update
@@ -263,23 +276,18 @@
Timer renameFilesTimer = new Timer("renamefiles");
Timer deleteTimer = new Timer("rmdir");
for (int i = 0; i < COUNT_REPEAT; i++ ) {
- doDirOperations(size, createTimer, readdirTimer, isFileTimer,
- renameDirTimer, renameFilesTimer, deleteTimer);
+ doDirOperations(size, createTimer, readTimer, renameDirTimer, renameFilesTimer,
+ deleteTimer);
}
-
- // The numbers dumped by the timers are monitored using crystalball and regressions are
- // reported from there.
createTimer.dumpResults();
- readdirTimer.dumpResults();
- isFileTimer.dumpResults();
+ readTimer.dumpResults();
renameDirTimer.dumpResults();
renameFilesTimer.dumpResults();
deleteTimer.dumpResults();
}
- private void doDirOperations(int size, Timer createTimer, Timer readdirTimer,
- Timer isFileTimer, Timer renameDirTimer, Timer renameFilesTimer,
- Timer deleteTimer) throws Exception {
+ private void doDirOperations(int size, Timer createTimer, Timer readTimer,
+ Timer renameDirTimer, Timer renameFilesTimer, Timer deleteTimer) throws Exception {
createTimer.start();
File testDir = new File(new File(Environment.getExternalStorageDirectory(),
"Download"), "test_dir_" + System.nanoTime());
@@ -287,7 +295,7 @@
List<File> files = new ArrayList<>();
for (int i = 0; i < size; i++) {
File file = new File(testDir, "file_" + System.nanoTime());
- assertThat(file.createNewFile()).isTrue();
+ assertTrue(file.createNewFile());
files.add(file);
}
createTimer.stop();
@@ -295,22 +303,13 @@
File renamedTestDir = new File(new File(Environment.getExternalStorageDirectory(),
"Download"), "renamed_test_dir_" + System.nanoTime());
try {
- readdirTimer.start();
+ readTimer.start();
File[] result = testDir.listFiles();
- readdirTimer.stop();
- assertThat(result.length).isEqualTo(size);
-
- // Drop cache as this info is cached in the initial lookup
- executeDropCachesImpl();
- // This calls into lookup libfuse method
- isFileTimer.start();
- for (File file: files) {
- file.isFile();
- }
- isFileTimer.stop();
+ readTimer.stop();
+ assertEquals(size, result.length);
renameDirTimer.start();
- assertThat(testDir.renameTo(renamedTestDir)).isTrue();
+ assertTrue(testDir.renameTo(renamedTestDir));
renameDirTimer.stop();
testDir = renamedTestDir;
@@ -322,7 +321,7 @@
List<File> renamedFiles = new ArrayList<>();
for (File file : files) {
File newFile = new File(testDir, "file_" + System.nanoTime());
- assertThat(file.renameTo(newFile)).isTrue();
+ assertTrue(file.renameTo(newFile));
renamedFiles.add(newFile);
}
renameFilesTimer.stop();
@@ -332,9 +331,9 @@
} finally {
deleteTimer.start();
for (File file : files) {
- assertThat(file.delete()).isTrue();
+ assertTrue(file.delete());
}
- assertThat(testDir.delete()).isTrue();
+ assertTrue(testDir.delete());
deleteTimer.stop();
}
}
@@ -415,32 +414,4 @@
.unregisterContentObserver(this);
}
}
-
- /**
- * Drops the disk cache.
- */
- private void executeDropCachesImpl() throws Exception {
- // Create a temporary file which contains the dropCaches command.
- // Do this because we cannot write to /proc/sys/vm/drop_caches directly,
- // as executeShellCommand parses the '>' character as a literal.
- File outputDir = InstrumentationRegistry.getInstrumentation().
- getContext().getCacheDir();
- File outputFile = File.createTempFile("drop_cache_script", ".sh", outputDir);
- outputFile.setWritable(true);
- outputFile.setExecutable(true, /*ownersOnly*/false);
-
- String dropCacheScriptPath = outputFile.toString();
-
- // If this works correctly, the next log-line will print 'Success'.
- String dropCacheCmd = "sync; echo 3 > /proc/sys/vm/drop_caches "
- + "&& echo Success || echo Failure";
- BufferedWriter writer = new BufferedWriter(new FileWriter(dropCacheScriptPath));
- writer.write(dropCacheCmd);
- writer.close();
-
- String result = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).
- executeShellCommand(dropCacheScriptPath);
- Log.v(TAG, "dropCaches output was: " + result);
- outputFile.delete();
- }
}
diff --git a/tests/client/src/com/android/providers/media/client/PublicVolumeTest.java b/tests/client/src/com/android/providers/media/client/PublicVolumeTest.java
deleted file mode 100644
index def72f7..0000000
--- a/tests/client/src/com/android/providers/media/client/PublicVolumeTest.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.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.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertNotNull;
-
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.MediaStore;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.OutputStream;
-
-@RunWith(AndroidJUnit4.class)
-public class PublicVolumeTest {
- @BeforeClass
- public static void setUp() throws Exception {
- createNewPublicVolume();
- }
-
- @AfterClass
- public static void tearDown() throws Exception {
- deletePublicVolumes();
- }
-
- /**
- * Test that we can query database rows of recently unmounted volume
- */
- @Test
- public void testIncludeRecentlyUnmountedVolumes() throws Exception {
- Context context = InstrumentationRegistry.getTargetContext();
- ContentResolver contentResolver = context.getContentResolver();
- MediaStore.waitForIdle(contentResolver);
- final String displayName = "UnmountedVolumeTest" + System.nanoTime();
-
- // Create image files in all volumes
- for (String volumeName : MediaStore.getExternalVolumeNames(context)) {
- ContentValues values = new ContentValues();
- values.clear();
- values.put(MediaStore.MediaColumns.DISPLAY_NAME, displayName + ".jpeg");
-
- final Uri targetUri = contentResolver.insert(
- MediaStore.Images.Media.getContentUri(volumeName), values);
- assertNotNull(targetUri);
-
- try (OutputStream out = contentResolver.openOutputStream(targetUri)) {
- }
- }
-
- final int volumeCount = MediaStore.getExternalVolumeNames(context).size();
- Bundle extras = new Bundle();
- // Filter only image files added by this test
- extras.putString(ContentResolver.QUERY_ARG_SQL_SELECTION,
- MediaStore.MediaColumns.DISPLAY_NAME + " LIKE ?");
- extras.putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS,
- new String[] {displayName + "%"});
- // Verify that we can see both image files.
- try (Cursor c = contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
- new String[]{MediaStore.MediaColumns._ID}, extras, null)) {
- assertThat(c.getCount()).isEqualTo(volumeCount);
- }
-
- unmountPublicVolume();
-
- // Verify that we don't see image file of unmounted volume.
- try (Cursor c = contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
- new String[]{MediaStore.MediaColumns._ID}, extras, null)) {
- assertThat(c.getCount()).isEqualTo(volumeCount - 1);
- }
-
- // Verify that querying with QUERY_ARG_INCLUDE_RECENTLY_UNMOUNTED_VOLUMES
- // includes database rows of unmounted volume.
- extras.putBoolean(MediaStore.QUERY_ARG_INCLUDE_RECENTLY_UNMOUNTED_VOLUMES, true);
- try (Cursor c = contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
- new String[]{MediaStore.MediaColumns._ID}, extras, null)) {
- assertThat(c.getCount()).isEqualTo(volumeCount);
- }
-
- // Mount public volume to avoid side effects to other tests which reuse
- // the same public volume
- mountPublicVolume();
- }
-}
-
diff --git a/tests/res/raw/test_gif.gif b/tests/res/raw/test_gif.gif
deleted file mode 100644
index d2e2aad..0000000
--- a/tests/res/raw/test_gif.gif
+++ /dev/null
Binary files differ
diff --git a/tests/src/com/android/providers/media/DatabaseHelperTest.java b/tests/src/com/android/providers/media/DatabaseHelperTest.java
index 4be9f09..95a1f95 100644
--- a/tests/src/com/android/providers/media/DatabaseHelperTest.java
+++ b/tests/src/com/android/providers/media/DatabaseHelperTest.java
@@ -18,31 +18,22 @@
import static android.provider.MediaStore.VOLUME_EXTERNAL_PRIMARY;
-import static com.android.providers.media.DatabaseHelper.VERSION_LATEST;
import static com.android.providers.media.DatabaseHelper.makePristineSchema;
-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.TEST_DOWNGRADE_DB;
-import static com.android.providers.media.DatabaseHelper.TEST_CLEAN_DB;
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.assertTrue;
-import android.Manifest;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
-import android.os.UserHandle;
import android.provider.Column;
import android.provider.MediaStore.Audio.AudioColumns;
-import android.provider.MediaStore.Audio;
import android.provider.MediaStore.Files.FileColumns;
import android.util.Log;
@@ -64,6 +55,12 @@
@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;
@@ -71,8 +68,6 @@
@Before
public void setUp() {
- InstrumentationRegistry.getInstrumentation().getUiAutomation()
- .adoptShellPermissionIdentity(Manifest.permission.INTERACT_ACROSS_USERS);
final Context context = InstrumentationRegistry.getTargetContext();
sIsolatedContext = new IsolatedContext(context, TAG, /*asFuseThread*/ false);
sIsolatedResolver = sIsolatedContext.getContentResolver();
@@ -80,7 +75,7 @@
@Test
public void testFilterVolumeNames() throws Exception {
- try (DatabaseHelper helper = new DatabaseHelperT(sIsolatedContext, TEST_CLEAN_DB)) {
+ try (DatabaseHelper helper = new DatabaseHelperS(sIsolatedContext, TEST_CLEAN_DB)) {
SQLiteDatabase db = helper.getWritableDatabaseForTest();
{
final ContentValues values = new ContentValues();
@@ -239,28 +234,23 @@
}
@Test
- public void testTtoO() throws Exception {
- assertDowngrade(DatabaseHelperT.class, DatabaseHelperO.class);
+ public void testStoO() throws Exception {
+ assertDowngrade(DatabaseHelperS.class, DatabaseHelperO.class);
}
@Test
- public void testTtoP() throws Exception {
- assertDowngrade(DatabaseHelperT.class, DatabaseHelperP.class);
+ public void testStoP() throws Exception {
+ assertDowngrade(DatabaseHelperS.class, DatabaseHelperP.class);
}
@Test
- public void testTtoQ() throws Exception {
- assertDowngrade(DatabaseHelperT.class, DatabaseHelperQ.class);
+ public void testStoQ() throws Exception {
+ assertDowngrade(DatabaseHelperS.class, DatabaseHelperQ.class);
}
@Test
- public void testTtoR() throws Exception {
- assertDowngrade(DatabaseHelperT.class, DatabaseHelperR.class);
- }
-
- @Test
- public void testTtoS() throws Exception {
- assertDowngrade(DatabaseHelperT.class, DatabaseHelperR.class);
+ public void testStoR() throws Exception {
+ assertDowngrade(DatabaseHelperS.class, DatabaseHelperR.class);
}
private void assertDowngrade(Class<? extends DatabaseHelper> before,
@@ -294,30 +284,25 @@
}
@Test
- public void testOtoT() throws Exception {
- assertRecompute(DatabaseHelperO.class, DatabaseHelperT.class);
- assertUpgrade(DatabaseHelperO.class, DatabaseHelperT.class);
+ public void testOtoS() throws Exception {
+ assertRecompute(DatabaseHelperO.class, DatabaseHelperS.class);
+ assertUpgrade(DatabaseHelperO.class, DatabaseHelperS.class);
}
@Test
- public void testPtoT() throws Exception {
- assertRecompute(DatabaseHelperP.class, DatabaseHelperT.class);
- assertUpgrade(DatabaseHelperP.class, DatabaseHelperT.class);
+ public void testPtoS() throws Exception {
+ assertRecompute(DatabaseHelperP.class, DatabaseHelperS.class);
+ assertUpgrade(DatabaseHelperP.class, DatabaseHelperS.class);
}
@Test
- public void testQtoT() throws Exception {
- assertUpgrade(DatabaseHelperQ.class, DatabaseHelperT.class);
+ public void testQtoS() throws Exception {
+ assertUpgrade(DatabaseHelperQ.class, DatabaseHelperS.class);
}
@Test
- public void testRtoT() throws Exception {
- assertUpgrade(DatabaseHelperR.class, DatabaseHelperT.class);
- }
-
- @Test
- public void testStoT() throws Exception {
- assertUpgrade(DatabaseHelperS.class, DatabaseHelperT.class);
+ public void testRtoS() throws Exception {
+ assertUpgrade(DatabaseHelperR.class, DatabaseHelperS.class);
}
private void assertRecompute(Class<? extends DatabaseHelper> before,
@@ -361,6 +346,17 @@
{
final ContentValues values = new ContentValues();
values.put(FileColumns.DATA,
+ "/storage/0000-0000/Android/sandbox/com.example2/Download/dir/foo.mp4");
+ values.put(FileColumns.DATE_ADDED, System.currentTimeMillis());
+ values.put(FileColumns.DATE_MODIFIED, System.currentTimeMillis());
+ values.put(FileColumns.DISPLAY_NAME, "foo.mp4");
+ values.put(FileColumns.MEDIA_TYPE, FileColumns.MEDIA_TYPE_VIDEO);
+ values.put(FileColumns.MIME_TYPE, "video/mp4");
+ assertFalse(db.insert("files", FileColumns.DATA, values) == -1);
+ }
+ {
+ final ContentValues values = new ContentValues();
+ values.put(FileColumns.DATA,
"/storage/emulated/0/Download/foo");
values.put(FileColumns.DATE_ADDED, System.currentTimeMillis());
values.put(FileColumns.DATE_MODIFIED, System.currentTimeMillis());
@@ -410,6 +406,18 @@
c.getString(c.getColumnIndexOrThrow(FileColumns.OWNER_PACKAGE_NAME)));
assertEquals("1", c.getString(c.getColumnIndexOrThrow(FileColumns.IS_DOWNLOAD)));
}
+ try (Cursor c = db.query("files", null, FileColumns.DISPLAY_NAME + "='foo.mp4'",
+ null, null, null, null)) {
+ assertEquals(1, c.getCount());
+ assertTrue(c.moveToFirst());
+ assertEquals("/storage/0000-0000/Android/sandbox/com.example2/Download/dir/foo.mp4",
+ c.getString(c.getColumnIndexOrThrow(FileColumns.DATA)));
+ assertEquals("video/mp4",
+ c.getString(c.getColumnIndexOrThrow(FileColumns.MIME_TYPE)));
+ assertEquals("com.example2",
+ c.getString(c.getColumnIndexOrThrow(FileColumns.OWNER_PACKAGE_NAME)));
+ assertEquals("1", c.getString(c.getColumnIndexOrThrow(FileColumns.IS_DOWNLOAD)));
+ }
try (Cursor c = db.query("files", null,
FileColumns.DATA + "='/storage/emulated/0/Download/foo'",
null, null, null, null)) {
@@ -505,86 +513,6 @@
}
}
- @Test
- public void testAddUserId() throws Exception {
- try (DatabaseHelper helper = new DatabaseHelperR(sIsolatedContext, TEST_UPGRADE_DB)) {
- SQLiteDatabase db = helper.getWritableDatabaseForTest();
- {
- // 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);
- }
- }
-
- try (DatabaseHelper helper = new DatabaseHelperS(sIsolatedContext, TEST_UPGRADE_DB)) {
- SQLiteDatabase db = helper.getWritableDatabaseForTest();
- // 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);
-
- try (Cursor cr = db.query("files", new String[]{FileColumns._USER_ID}, 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 the _user_id
- assertThat(cr.getInt(0)).isEqualTo(UserHandle.myUserId());
- }
- }
- }
- }
-
- /**
- * 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)) {
- 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);
- }
-
- // Upgrade the database by changing the version to dbVersionHigher
- try (DatabaseHelper helper = dbVersionHigher.getConstructor(Context.class, String.class)
- .newInstance(sIsolatedContext, TEST_DOWNGRADE_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);
- }
-
- // 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);
- }
- }
-
private static String normalize(String sql) {
return sql != null ? sql.replace(", ", ",") : null;
}
@@ -604,7 +532,7 @@
private static class DatabaseHelperO extends DatabaseHelper {
public DatabaseHelperO(Context context, String name) {
super(context, name, DatabaseHelper.VERSION_O,
- false, false, Column.class, null, null, null, null);
+ false, false, false, Column.class, null, null, null, null);
}
@Override
@@ -616,7 +544,7 @@
private static class DatabaseHelperP extends DatabaseHelper {
public DatabaseHelperP(Context context, String name) {
super(context, name, DatabaseHelper.VERSION_P,
- false, false, Column.class, null, null, null, null);
+ false, false, false, Column.class, null, null, null, null);
}
@Override
@@ -628,7 +556,7 @@
private static class DatabaseHelperQ extends DatabaseHelper {
public DatabaseHelperQ(Context context, String name) {
super(context, name, DatabaseHelper.VERSION_Q,
- false, false, Column.class, null, null, null, null);
+ false, false, false, Column.class, null, null, null, null);
}
@Override
@@ -640,7 +568,7 @@
private static class DatabaseHelperR extends DatabaseHelper {
public DatabaseHelperR(Context context, String name) {
super(context, name, DatabaseHelper.VERSION_R,
- false, false, Column.class, null, null,
+ false, false, false, Column.class, null, null,
MediaProvider.MIGRATION_LISTENER, null);
}
@@ -653,21 +581,7 @@
private static class DatabaseHelperS extends DatabaseHelper {
public DatabaseHelperS(Context context, String name) {
super(context, name, DatabaseHelper.VERSION_S,
- false, false, Column.class, null, null,
- MediaProvider.MIGRATION_LISTENER, null);
- }
-
-
- @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, null, null,
+ false, false, false, Column.class, null, null,
MediaProvider.MIGRATION_LISTENER, null);
}
}
@@ -1244,197 +1158,4 @@
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 1aed875..9b0b169 100644
--- a/tests/src/com/android/providers/media/IdleServiceTest.java
+++ b/tests/src/com/android/providers/media/IdleServiceTest.java
@@ -90,11 +90,6 @@
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.waitForIdle(resolver);
-
final File dir = Environment.getExternalStorageDirectory();
final File mediaDir = context.getExternalMediaDirs()[0];
@@ -135,59 +130,17 @@
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 {
+ public void testExtendTrashedItemExpiresOverOneWeek() 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);
- runIdleMaintenance(resolver);
-
- assertExpiredItemIsExtended(resolver, uri1);
- assertExpiredItemIsExtended(resolver, uri2);
- assertExpiredItemIsExtended(resolver, uri3);
- }
-
- 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 long dateExpires = (System.currentTimeMillis() - 10 * DateUtils.DAY_IN_MILLIS) / 1000;
final String expiredFileName = String.format(Locale.US, ".%s-%d-%s",
- FileUtils.PREFIX_TRASHED, dateExpires, displayName + ".jpg");
- final File file = MediaScannerTest.stage(R.raw.test_image, new File(mDir, expiredFileName));
+ 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);
MediaStore.waitForIdle(resolver);
@@ -195,23 +148,24 @@
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);
}
- return uri;
- }
- @Test
- public void testExtendTrashedItemExpiresOverOneWeek() throws Exception {
- final Context context = InstrumentationRegistry.getTargetContext();
- final ContentResolver resolver = context.getContentResolver();
- final long dateExpires = (System.currentTimeMillis() - 10 * DateUtils.DAY_IN_MILLIS) / 1000;
- final Uri uri = createExpiredTrashedItem(resolver, dateExpires);
-
+ final long expectedExtendedTimestamp =
+ (System.currentTimeMillis() + FileUtils.DEFAULT_DURATION_EXTENDED) / 1000 - 1;
runIdleMaintenance(resolver);
- assertExpiredItemIsExtended(resolver, uri);
+ 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);
+ }
}
@Test
@@ -221,13 +175,25 @@
// 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);
+ 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);
- runIdleMaintenance(resolver);
+ MediaStore.waitForIdle(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);
+ }
+
+ runIdleMaintenance(resolver);
+
try (Cursor cursor = resolver.query(uri, projection, queryArgs,
null /* cancellationSignal */)) {
assertThat(cursor.getCount()).isEqualTo(0);
diff --git a/tests/src/com/android/providers/media/LocalCallingIdentityTest.java b/tests/src/com/android/providers/media/LocalCallingIdentityTest.java
index 1abca94..e30ed92 100644
--- a/tests/src/com/android/providers/media/LocalCallingIdentityTest.java
+++ b/tests/src/com/android/providers/media/LocalCallingIdentityTest.java
@@ -90,7 +90,7 @@
final Context context = InstrumentationRegistry.getContext();
final PackageManager pm = context.getPackageManager();
- final LocalCallingIdentity ident = LocalCallingIdentity.fromExternal(context, null,
+ final LocalCallingIdentity ident = LocalCallingIdentity.fromExternal(context,
pm.getPackageUid(MediaProviderTest.PERMISSIONLESS_APP, 0));
assertEquals(MediaProviderTest.PERMISSIONLESS_APP,
diff --git a/tests/src/com/android/providers/media/MediaDocumentsProviderTest.java b/tests/src/com/android/providers/media/MediaDocumentsProviderTest.java
index f9754e8..2db98b9 100644
--- a/tests/src/com/android/providers/media/MediaDocumentsProviderTest.java
+++ b/tests/src/com/android/providers/media/MediaDocumentsProviderTest.java
@@ -63,8 +63,7 @@
public void setUp() {
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.adoptShellPermissionIdentity(Manifest.permission.LOG_COMPAT_CHANGE,
- Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
- Manifest.permission.INTERACT_ACROSS_USERS);
+ Manifest.permission.READ_COMPAT_CHANGE_CONFIG);
}
@After
diff --git a/tests/src/com/android/providers/media/MediaProviderForFuseTest.java b/tests/src/com/android/providers/media/MediaProviderForFuseTest.java
index 7de9834..e4d2e52 100644
--- a/tests/src/com/android/providers/media/MediaProviderForFuseTest.java
+++ b/tests/src/com/android/providers/media/MediaProviderForFuseTest.java
@@ -58,8 +58,7 @@
InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
Manifest.permission.LOG_COMPAT_CHANGE,
Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
- Manifest.permission.UPDATE_APP_OPS_STATS,
- Manifest.permission.INTERACT_ACROSS_USERS);
+ Manifest.permission.UPDATE_APP_OPS_STATS);
final Context context = InstrumentationRegistry.getTargetContext();
sIsolatedContext = new IsolatedContext(context, "modern", /*asFuseThread*/ true);
@@ -96,11 +95,12 @@
file.createNewFile();
// We can write our file
- FileOpenResult result = sMediaProvider.onFileOpenForFuse(
- 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]);
+ Truth.assertThat(sMediaProvider.isOpenAllowedForFuse(
+ file.getPath(), sTestUid, true)).isEqualTo(0);
+
+ // We should have no redaction
+ Truth.assertThat(sMediaProvider.getRedactionRangesForFuse(
+ file.getPath(), sTestUid, 0)).isEqualTo(new long[0]);
// We can rename our file
final File renamed = new File(sTestDir, "renamed" + System.nanoTime() + ".jpg");
diff --git a/tests/src/com/android/providers/media/MediaProviderTest.java b/tests/src/com/android/providers/media/MediaProviderTest.java
index aa0a697..7348462 100644
--- a/tests/src/com/android/providers/media/MediaProviderTest.java
+++ b/tests/src/com/android/providers/media/MediaProviderTest.java
@@ -44,14 +44,12 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Environment;
-import android.os.UserHandle;
import android.provider.MediaStore;
import android.provider.MediaStore.Audio.AudioColumns;
import android.provider.MediaStore.Files.FileColumns;
@@ -81,12 +79,10 @@
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.PrintWriter;
-import java.sql.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;
@@ -94,8 +90,11 @@
public class MediaProviderTest {
static final String TAG = "MediaProviderTest";
- // The test app without permissions
- static final String PERMISSIONLESS_APP = "com.android.providers.media.testapp.withoutperms";
+ /**
+ * 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.
+ */
+ static final String PERMISSIONLESS_APP = "com.android.egg";
private static Context sIsolatedContext;
private static ContentResolver sIsolatedResolver;
@@ -104,9 +103,7 @@
public static void setUp() {
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.adoptShellPermissionIdentity(Manifest.permission.LOG_COMPAT_CHANGE,
- Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
- Manifest.permission.READ_DEVICE_CONFIG,
- Manifest.permission.INTERACT_ACROSS_USERS);
+ Manifest.permission.READ_COMPAT_CHANGE_CONFIG);
final Context context = InstrumentationRegistry.getTargetContext();
sIsolatedContext = new IsolatedContext(context, "modern", /*asFuseThread*/ false);
@@ -494,6 +491,8 @@
getPathOwnerPackageName("/storage/emulated/0/Android/obb/com.example/foo.jpg"));
assertEquals("com.example",
getPathOwnerPackageName("/storage/emulated/0/Android/media/com.example/foo.jpg"));
+ assertEquals("com.example",
+ getPathOwnerPackageName("/storage/emulated/0/Android/sandbox/com.example/foo.jpg"));
}
@Test
@@ -516,24 +515,6 @@
}
@Test
- public void testBuildData_withUserId() throws Exception {
- final Uri uri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
- final ContentValues values = new ContentValues();
- values.put(MediaColumns.DISPLAY_NAME, "test_userid");
- 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._USER_ID},
- null, null)) {
- assertNotNull(c);
- assertEquals(1, c.getCount());
- assertTrue(c.moveToFirst());
- assertEquals("test_userid.png", c.getString(0));
- assertEquals(UserHandle.myUserId(), 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",
@@ -624,10 +605,6 @@
}
};
- final ProviderInfo info = sIsolatedContext.getPackageManager()
- .resolveContentProvider(MediaStore.AUTHORITY, PackageManager.GET_META_DATA);
- // Attach providerInfo, to make sure mCallingIdentity can be populated
- provider.attachInfo(sIsolatedContext, info);
final Uri uri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
final ContentValues values = new ContentValues();
@@ -937,19 +914,36 @@
assertTrue(isDownload("/storage/emulated/0/Download/test.pdf"));
assertTrue(isDownload("/storage/emulated/0/Download/dir/foo.mp4"));
assertTrue(isDownload("/storage/0000-0000/Download/foo.txt"));
+ assertTrue(isDownload(
+ "/storage/emulated/0/Android/sandbox/com.example/Download/colors.png"));
+ assertTrue(isDownload(
+ "/storage/emulated/0/Android/sandbox/shared-com.uid.shared/Download/colors.png"));
+ assertTrue(isDownload(
+ "/storage/0000-0000/Android/sandbox/com.example/Download/colors.png"));
+ assertTrue(isDownload(
+ "/storage/0000-0000/Android/sandbox/shared-com.uid.shared/Download/colors.png"));
+
assertFalse(isDownload("/storage/emulated/0/Pictures/colors.png"));
assertFalse(isDownload("/storage/emulated/0/Pictures/Download/colors.png"));
assertFalse(isDownload("/storage/emulated/0/Android/data/com.example/Download/foo.txt"));
+ assertFalse(isDownload(
+ "/storage/emulated/0/Android/sandbox/com.example/dir/Download/foo.txt"));
assertFalse(isDownload("/storage/emulated/0/Download"));
+ assertFalse(isDownload("/storage/emulated/0/Android/sandbox/com.example/Download"));
+ assertFalse(isDownload(
+ "/storage/0000-0000/Android/sandbox/shared-com.uid.shared/Download"));
}
@Test
public void testIsDownloadDir() throws Exception {
assertTrue(isDownloadDir("/storage/emulated/0/Download"));
+ assertTrue(isDownloadDir("/storage/emulated/0/Android/sandbox/com.example/Download"));
assertFalse(isDownloadDir("/storage/emulated/0/Download/colors.png"));
assertFalse(isDownloadDir("/storage/emulated/0/Download/dir/"));
+ assertFalse(isDownloadDir(
+ "/storage/emulated/0/Android/sandbox/com.example/Download/dir/foo.txt"));
}
@Test
@@ -1010,6 +1004,7 @@
for (String top : new String[] {
"/storage/emulated/0",
+ "/storage/emulated/0/Android/sandbox/com.example",
}) {
values = computeDataValues(top + "/IMG1024.JPG");
assertVolume(values, MediaStore.VOLUME_EXTERNAL_PRIMARY);
@@ -1050,10 +1045,6 @@
return Build.VERSION_CODES.CUR_DEVELOPMENT;
}
};
- final ProviderInfo info = sIsolatedContext.getPackageManager()
- .resolveContentProvider(MediaStore.AUTHORITY, PackageManager.GET_META_DATA);
- // Attach providerInfo, to make sure mCallingIdentity can be populated
- provider.attachInfo(sIsolatedContext, info);
provider.ensureFileColumns(uri, values);
assertMimetype(values, "image/png");
@@ -1362,7 +1353,7 @@
@Test
public void testNestedTransaction_applyBatch() throws Exception {
- final Uri[] uris = new Uri[]{
+ final Uri[] uris = new Uri[] {
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL, 0),
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY, 0),
};
@@ -1371,102 +1362,4 @@
ops.add(ContentProviderOperation.newDelete(uris[1]).build());
sIsolatedResolver.applyBatch(MediaStore.AUTHORITY, ops);
}
-
- @Test
- public void testRedactionForInvalidUris() throws Exception {
- try (ContentProviderClient cpc = sIsolatedResolver
- .acquireContentProviderClient(MediaStore.AUTHORITY)) {
- MediaProvider mp = (MediaProvider) cpc.getLocalContentProvider();
- final String volumeName = MediaStore.VOLUME_EXTERNAL;
- assertNull(mp.getRedactedUri(MediaStore.Images.Media.getContentUri(volumeName)));
- assertNull(mp.getRedactedUri(MediaStore.Video.Media.getContentUri(volumeName)));
- assertNull(mp.getRedactedUri(MediaStore.Audio.Media.getContentUri(volumeName)));
- assertNull(mp.getRedactedUri(MediaStore.Audio.Albums.getContentUri(volumeName)));
- assertNull(mp.getRedactedUri(MediaStore.Audio.Artists.getContentUri(volumeName)));
- assertNull(mp.getRedactedUri(MediaStore.Audio.Genres.getContentUri(volumeName)));
- assertNull(mp.getRedactedUri(MediaStore.Audio.Playlists.getContentUri(volumeName)));
- assertNull(mp.getRedactedUri(MediaStore.Downloads.getContentUri(volumeName)));
- assertNull(mp.getRedactedUri(MediaStore.Files.getContentUri(volumeName)));
-
- // Check with a very large value - which shouldn't be present normally (at least for
- // tests).
- assertNull(mp.getRedactedUri(
- MediaStore.Images.Media.getContentUri(volumeName, Long.MAX_VALUE)));
- }
- }
-
- @Test
- public void testRedactionForInvalidAndValidUris() throws Exception {
- final String volumeName = MediaStore.VOLUME_EXTERNAL;
- final List<Uri> uris = new ArrayList<>();
- uris.add(MediaStore.Images.Media.getContentUri(volumeName));
- uris.add(MediaStore.Video.Media.getContentUri(volumeName));
-
- final File dir = Environment
- .getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
- final File[] files = new File[]{
- stage(R.raw.test_audio, new File(dir, "test" + System.nanoTime() + ".mp3")),
- stage(R.raw.test_video_xmp,
- new File(dir, "test" + System.nanoTime() + ".mp4")),
- stage(R.raw.lg_g4_iso_800_jpg,
- new File(dir, "test" + System.nanoTime() + ".jpg"))
- };
-
- try (ContentProviderClient cpc = sIsolatedResolver
- .acquireContentProviderClient(MediaStore.AUTHORITY)) {
- MediaProvider mp = (MediaProvider) cpc.getLocalContentProvider();
- for (File file : files) {
- uris.add(MediaStore.scanFile(sIsolatedResolver, file));
- }
-
- List<Uri> redactedUris = mp.getRedactedUri(uris);
- assertEquals(uris.size(), redactedUris.size());
- assertNull(redactedUris.get(0));
- assertNull(redactedUris.get(1));
- assertNotNull(redactedUris.get(2));
- assertNotNull(redactedUris.get(3));
- assertNotNull(redactedUris.get(4));
- } finally {
- for (File file : files) {
- file.delete();
- }
- }
- }
-
- @Test
- public void testRedactionForFileExtension() throws Exception {
- testRedactionForFileExtension(R.raw.test_audio, ".mp3");
- testRedactionForFileExtension(R.raw.test_video_xmp, ".mp4");
- testRedactionForFileExtension(R.raw.lg_g4_iso_800_jpg, ".jpg");
- }
-
- private void testRedactionForFileExtension(int resId, String extension) throws Exception {
- final File dir = Environment
- .getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
- final File file = new File(dir, "test" + System.nanoTime() + extension);
-
- stage(resId, file);
-
- final List<Uri> uris = new ArrayList<>();
- uris.add(MediaStore.scanFile(sIsolatedResolver, file));
-
-
- try (ContentProviderClient cpc = sIsolatedResolver
- .acquireContentProviderClient(MediaStore.AUTHORITY)) {
- final MediaProvider mp = (MediaProvider) cpc.getLocalContentProvider();
-
- final String[] projection = new String[]{MediaColumns.DISPLAY_NAME, MediaColumns.DATA};
- for (Uri uri : mp.getRedactedUri(uris)) {
- try (Cursor c = sIsolatedResolver.query(uri, projection, null, null)) {
- assertNotNull(c);
- assertEquals(1, c.getCount());
- assertTrue(c.moveToFirst());
- assertTrue(c.getString(0).endsWith(extension));
- assertTrue(c.getString(1).endsWith(extension));
- }
- }
- } finally {
- file.delete();
- }
- }
}
diff --git a/tests/src/com/android/providers/media/PermissionActivityTest.java b/tests/src/com/android/providers/media/PermissionActivityTest.java
index 6eb738e..7bbce62 100644
--- a/tests/src/com/android/providers/media/PermissionActivityTest.java
+++ b/tests/src/com/android/providers/media/PermissionActivityTest.java
@@ -16,30 +16,6 @@
package com.android.providers.media;
-import static android.Manifest.permission.ACCESS_MEDIA_LOCATION;
-import static android.Manifest.permission.MANAGE_APP_OPS_MODES;
-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.UPDATE_APP_OPS_STATS;
-
-import static androidx.test.InstrumentationRegistry.getContext;
-
-import static com.android.providers.media.PermissionActivity.VERB_FAVORITE;
-import static com.android.providers.media.PermissionActivity.VERB_TRASH;
-import static com.android.providers.media.PermissionActivity.VERB_UNFAVORITE;
-import static com.android.providers.media.PermissionActivity.VERB_WRITE;
-import static com.android.providers.media.PermissionActivity.shouldShowActionDialog;
-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.checkPermissionReadStorage;
-import static com.android.providers.media.util.TestUtils.adoptShellPermission;
-import static com.android.providers.media.util.TestUtils.dropShellPermission;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.AppOpsManager;
import android.app.Instrumentation;
import android.content.ClipData;
import android.content.ContentValues;
@@ -48,22 +24,16 @@
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
-import android.text.TextUtils;
-import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SdkSuppress;
import androidx.test.runner.AndroidJUnit4;
import com.android.providers.media.scan.MediaScannerTest;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.File;
-import java.util.HashSet;
-import java.util.concurrent.TimeoutException;
/**
* We already have solid coverage of this logic in {@code CtsProviderTestCases},
@@ -72,43 +42,6 @@
*/
@RunWith(AndroidJUnit4.class)
public class PermissionActivityTest {
- private static final String TEST_APP_PACKAGE_NAME =
- "com.android.providers.media.testapp.permission";
-
- private static final String OP_ACCESS_MEDIA_LOCATION =
- AppOpsManager.permissionToOp(ACCESS_MEDIA_LOCATION);
- private static final String OP_MANAGE_MEDIA =
- AppOpsManager.permissionToOp(MANAGE_MEDIA);
- private static final String OP_MANAGE_EXTERNAL_STORAGE =
- AppOpsManager.permissionToOp(MANAGE_EXTERNAL_STORAGE);
- private static final String OP_READ_EXTERNAL_STORAGE =
- AppOpsManager.permissionToOp(READ_EXTERNAL_STORAGE);
-
- // 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}
- private static final String[] DEFAULT_OP_PERMISSION_LIST = new String[] {
- OP_MANAGE_EXTERNAL_STORAGE,
- OP_MANAGE_MEDIA
- };
-
- // The list is used to restore the permissions after the test is finished.
- // The default value for these app ops is {@link AppOpsManager#MODE_ALLOWED}
- private static final String[] ALLOWED_OP_PERMISSION_LIST = new String[] {
- OP_ACCESS_MEDIA_LOCATION,
- OP_READ_EXTERNAL_STORAGE
- };
-
- private static final long TIMEOUT_MILLIS = 3000;
- private static final long SLEEP_MILLIS = 30;
-
- private static final int TEST_APP_PID = -1;
- private int mTestAppUid = -1;
-
- @Before
- public void setUp() throws Exception {
- mTestAppUid = getContext().getPackageManager().getPackageUid(TEST_APP_PACKAGE_NAME, 0);
- }
-
@Test
public void testSimple() throws Exception {
final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
@@ -119,155 +52,6 @@
activity.startActivityForResult(createIntent(), 42);
}
- @Test
- public void testShouldShowActionDialog_favorite_false() throws Exception {
- assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid,
- TEST_APP_PACKAGE_NAME, null, VERB_FAVORITE)).isFalse();
- }
-
- @Test
- public void testShouldShowActionDialog_unfavorite_false() throws Exception {
- assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid,
- TEST_APP_PACKAGE_NAME, null, VERB_UNFAVORITE)).isFalse();
- }
-
- @Test
- @SdkSuppress(minSdkVersion = 31, codeName = "S")
- public void testShouldShowActionDialog_noRESAndMES_true() throws Exception {
- final String[] enableAppOpsList = {OP_MANAGE_MEDIA};
- final String[] disableAppOpsList = {OP_MANAGE_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE};
- adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
-
- try {
- setupPermissions(mTestAppUid, enableAppOpsList, disableAppOpsList);
-
- assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid,
- TEST_APP_PACKAGE_NAME, null, VERB_TRASH)).isTrue();
- } finally {
- restoreDefaultAppOpPermissions(mTestAppUid);
- 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};
- final String[] disableAppOpsList = {OP_MANAGE_MEDIA};
- adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
-
- try {
- setupPermissions(mTestAppUid, enableAppOpsList, disableAppOpsList);
-
- assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid,
- TEST_APP_PACKAGE_NAME, null, VERB_TRASH)).isTrue();
- } finally {
- restoreDefaultAppOpPermissions(mTestAppUid);
- dropShellPermission();
- }
- }
-
- @Test
- @SdkSuppress(minSdkVersion = 31, codeName = "S")
- public void testShouldShowActionDialog_hasPermissionWithRES_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);
-
- assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid,
- TEST_APP_PACKAGE_NAME, null, VERB_TRASH)).isFalse();
- } finally {
- restoreDefaultAppOpPermissions(mTestAppUid);
- dropShellPermission();
- }
- }
-
- @Test
- @SdkSuppress(minSdkVersion = 31, codeName = "S")
- public void testShouldShowActionDialog_hasPermissionWithMES_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);
-
- assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid,
- TEST_APP_PACKAGE_NAME, null, VERB_TRASH)).isFalse();
- } finally {
- restoreDefaultAppOpPermissions(mTestAppUid);
- dropShellPermission();
- }
- }
-
- @Test
- @SdkSuppress(minSdkVersion = 31, codeName = "S")
- public void testShouldShowActionDialog_writeNoACCESS_MEDIA_LOCATION_true() throws Exception {
- final String[] enableAppOpsList =
- {OP_MANAGE_EXTERNAL_STORAGE, OP_MANAGE_MEDIA, OP_READ_EXTERNAL_STORAGE};
- final String[] disableAppOpsList = {OP_ACCESS_MEDIA_LOCATION};
- adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
-
- try {
- setupPermissions(mTestAppUid, enableAppOpsList, disableAppOpsList);
-
- assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid,
- TEST_APP_PACKAGE_NAME, null, VERB_WRITE)).isTrue();
- } finally {
- restoreDefaultAppOpPermissions(mTestAppUid);
- dropShellPermission();
- }
- }
-
- @Test
- @SdkSuppress(minSdkVersion = 31, codeName = "S")
- public void testShouldShowActionDialog_writeHasACCESS_MEDIA_LOCATION_false() throws Exception {
- final String[] enableAppOpsList = {
- OP_ACCESS_MEDIA_LOCATION,
- OP_MANAGE_EXTERNAL_STORAGE,
- OP_MANAGE_MEDIA,
- OP_READ_EXTERNAL_STORAGE};
- final String[] disableAppOpsList = new String[]{};
- adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
-
- try {
- setupPermissions(mTestAppUid, enableAppOpsList, disableAppOpsList);
-
- assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid,
- TEST_APP_PACKAGE_NAME, null, VERB_WRITE)).isFalse();
- } finally {
- restoreDefaultAppOpPermissions(mTestAppUid);
- dropShellPermission();
- }
- }
-
- private static void setupPermissions(int uid, @NonNull String[] enableAppOpsList,
- @NonNull String[] disableAppOpsList) throws Exception {
- for (String op : enableAppOpsList) {
- modifyAppOp(uid, op, AppOpsManager.MODE_ALLOWED);
- }
-
- for (String op : disableAppOpsList) {
- modifyAppOp(uid, op, AppOpsManager.MODE_ERRORED);
- }
-
- pollForAppOpPermissions(TEST_APP_PID, uid, enableAppOpsList, /* hasPermission= */ true);
- pollForAppOpPermissions(TEST_APP_PID, uid, disableAppOpsList, /* hasPermission= */ false);
- }
-
- private static void restoreDefaultAppOpPermissions(int uid) {
- for (String op : DEFAULT_OP_PERMISSION_LIST) {
- modifyAppOp(uid, op, AppOpsManager.MODE_DEFAULT);
- }
-
- for (String op : ALLOWED_OP_PERMISSION_LIST) {
- modifyAppOp(uid, op, AppOpsManager.MODE_ALLOWED);
- }
- }
-
private static Intent createIntent() throws Exception {
final Context context = InstrumentationRegistry.getContext();
@@ -283,52 +67,4 @@
intent.putExtra(MediaStore.EXTRA_CONTENT_VALUES, new ContentValues());
return intent;
}
-
- private static void modifyAppOp(int uid, @NonNull String op, int mode) {
- getContext().getSystemService(AppOpsManager.class).setUidMode(op, uid, mode);
- }
-
- private static void pollForAppOpPermissions(int pid, 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)) {
- checkedOpSet.add(op);
- continue;
- }
- }
- Thread.sleep(SLEEP_MILLIS);
- current = System.currentTimeMillis();
- }
-
- if (checkedOpSet.size() != opList.length) {
- throw new TimeoutException("Check AppOp permissions with " + uid + " timeout");
- }
- }
-
- private static boolean checkPermission(@NonNull String op, int pid, int uid,
- @NonNull String packageName, boolean expected) {
- final Context context = getContext();
-
- if (TextUtils.equals(op, OP_READ_EXTERNAL_STORAGE)) {
- return expected == checkPermissionReadStorage(context, pid, uid, packageName,
- /* attributionTag= */ null);
- } else if (TextUtils.equals(op, OP_MANAGE_EXTERNAL_STORAGE)) {
- return expected == checkPermissionManager(context, pid, uid, packageName,
- /* attributionTag= */ null);
- } else if (TextUtils.equals(op, OP_MANAGE_MEDIA)) {
- return expected == checkPermissionManageMedia(context, pid, uid, packageName,
- /* attributionTag= */ null);
- } else if (TextUtils.equals(op, OP_ACCESS_MEDIA_LOCATION)) {
- return expected == checkPermissionAccessMediaLocation(context, pid, uid,
- packageName, /* attributionTag= */ null);
- } else {
- throw new IllegalArgumentException("checkPermission is not supported for op: " + op);
- }
- }
}
diff --git a/tests/src/com/android/providers/media/PickerProviderMediaGenerator.java b/tests/src/com/android/providers/media/PickerProviderMediaGenerator.java
deleted file mode 100644
index 9015f42..0000000
--- a/tests/src/com/android/providers/media/PickerProviderMediaGenerator.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media;
-
-import static android.provider.CloudMediaProviderContract.MediaColumns.DATE_TAKEN_MS;
-import static android.provider.CloudMediaProviderContract.MediaColumns.DURATION_MS;
-import static android.provider.CloudMediaProviderContract.MediaColumns.ID;
-import static android.provider.CloudMediaProviderContract.MediaColumns.MEDIA_STORE_URI;
-import static android.provider.CloudMediaProviderContract.MediaColumns.MIME_TYPE;
-import static android.provider.CloudMediaProviderContract.MediaColumns.SIZE_BYTES;
-import static android.provider.CloudMediaProviderContract.MediaColumns;
-import static android.provider.CloudMediaProviderContract.MediaInfo;
-
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.provider.CloudMediaProvider;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * 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[] MEDIA_PROJECTION = new String[] {
- ID,
- MEDIA_STORE_URI,
- MIME_TYPE,
- DATE_TAKEN_MS,
- SIZE_BYTES,
- DURATION_MS,
- };
-
- private static final String[] DELETED_MEDIA_PROJECTION = new String[] { ID };
-
- public static class MediaGenerator {
- private final Set<TestMedia> mMedia = new HashSet<>();
- private final Set<TestMedia> mDeletedMedia = new HashSet<>();
- private String mVersion;
- private long mGeneration;
-
- public Cursor getMedia(long generation) {
- return getCursor(mMedia, generation, /* isDeleted */ false);
- }
-
- public Cursor getDeletedMedia(long generation) {
- return getCursor(mDeletedMedia, generation, /* isDeleted */ true);
- }
-
- public void addMedia(String localId, String cloudId) {
- mDeletedMedia.remove(createPlaceholderMedia(localId, cloudId));
- mMedia.add(createTestMedia(localId, cloudId));
- }
-
- public void deleteMedia(String localId, String cloudId) {
- if (mMedia.remove(createPlaceholderMedia(localId, cloudId))) {
- mDeletedMedia.add(createTestMedia(localId, cloudId));
- }
- }
-
- public void resetAll() {
- mMedia.clear();
- mDeletedMedia.clear();
- }
-
- public void setVersion(String version) {
- mVersion = version;
- }
-
- public String getVersion() {
- return mVersion;
- }
-
- public long getGeneration() {
- return mGeneration;
- }
-
- public long getCount() {
- return mMedia.size();
- }
-
- private TestMedia createTestMedia(String localId, String cloudId) {
- // Increase generation
- return new TestMedia(localId, cloudId, ++mGeneration);
- }
-
- 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(Set<TestMedia> mediaSet, long generation,
- boolean isDeleted) {
- final MatrixCursor matrix;
- if (isDeleted) {
- matrix = new MatrixCursor(DELETED_MEDIA_PROJECTION);
- } else {
- matrix = new MatrixCursor(MEDIA_PROJECTION);
- }
-
- Set<TestMedia> result = new HashSet<>();
- for (TestMedia media : mediaSet) {
- if (media.generation > generation) {
- matrix.addRow(media.toArray(isDeleted));
- }
- }
- return matrix;
- }
- }
-
- private static class TestMedia {
- public final String localId;
- public final String cloudId;
- public final long dateTakenMs;
- public final long generation;
-
- public TestMedia(String localId, String cloudId, long generation) {
- this.localId = localId;
- this.cloudId = cloudId;
- this.dateTakenMs = System.currentTimeMillis();
- this.generation = generation;
- }
-
- public String[] toArray(boolean isDeleted) {
- if (isDeleted) {
- return new String[] {getId()};
- }
-
- return new String[] {
- getId(),
- localId == null ? null : "content://media/external/files/" + localId,
- "image/jpeg",
- String.valueOf(dateTakenMs),
- /* size_bytes */ String.valueOf(4096),
- /* duration_ms */ String.valueOf(0)
- };
- }
-
- @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;
- }
- }
-
- 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/ResolvePlaylistTest.java b/tests/src/com/android/providers/media/ResolvePlaylistTest.java
index 0f6c50a..0bd2850 100644
--- a/tests/src/com/android/providers/media/ResolvePlaylistTest.java
+++ b/tests/src/com/android/providers/media/ResolvePlaylistTest.java
@@ -58,8 +58,7 @@
final Context context = InstrumentationRegistry.getTargetContext();
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.adoptShellPermissionIdentity(Manifest.permission.LOG_COMPAT_CHANGE,
- Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
- Manifest.permission.INTERACT_ACROSS_USERS);
+ Manifest.permission.READ_COMPAT_CHANGE_CONFIG);
mDir = new File(context.getExternalMediaDirs()[0], "test_" + System.nanoTime());
mDir.mkdirs();
diff --git a/tests/src/com/android/providers/media/TranscodeHelperTest.java b/tests/src/com/android/providers/media/TranscodeHelperTest.java
deleted file mode 100644
index 3aab7eb..0000000
--- a/tests/src/com/android/providers/media/TranscodeHelperTest.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.media.ApplicationMediaCapabilities;
-import android.media.MediaFormat;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.Process;
-import android.provider.DeviceConfig.OnPropertiesChangedListener;
-import android.provider.MediaStore;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SdkSuppress;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Random;
-
-@RunWith(AndroidJUnit4.class)
-@SdkSuppress(minSdkVersion = 31, codeName = "S")
-public class TranscodeHelperTest {
- private static final String SOME_VALID_FILE_PATH =
- "/storage/emulated/0/" + Environment.DIRECTORY_DCIM + "/Camera/some_filename.mp4";
-
- private final MediaProvider mDefaultMediaProvider = new MediaProvider() {
- @Override
- public String getStringDeviceConfig(String key, String defaultValue) {
- return defaultValue;
- }
-
- @Override
- public boolean getBooleanDeviceConfig(String key, boolean defaultValue) {
- return defaultValue;
- }
-
- @Override
- public int getIntDeviceConfig(String key, int defaultValue) {
- return defaultValue;
- }
-
- @Override
- public void addOnPropertiesChangedListener(OnPropertiesChangedListener listener) {
- // Ignore
- }
- };
-
- private final TranscodeHelperImpl mUnderTest = new TranscodeHelperImpl(
- InstrumentationRegistry.getTargetContext(), mDefaultMediaProvider);
-
- @Test
- public void testSupportsValidTranscodePath() {
- List<String> filePaths = Arrays.asList(
- "/storage/emulated/0/" + Environment.DIRECTORY_DCIM + "/Camera/filename.mp4",
- "/storage/emulated/1/" + Environment.DIRECTORY_DCIM + "/Camera/filename.mp4",
- "/storage/emulated/0/dcim/camera/filename.mp4");
-
- for (String path : filePaths) {
- assertThat(mUnderTest.supportsTranscode(path)).isTrue();
- }
- }
-
- @Test
- public void testDoesNotSupportsInvalidTranscodePath() {
- List<String> filePaths = Arrays.asList(
- "/storage/emulated/ab/" + Environment.DIRECTORY_DCIM + "/Camera/filename.mp4",
- "/storage/emulated/0/" + Environment.DIRECTORY_DCIM + "/Camera/dir/filename.mp4",
- "/storage/emulate/" + Environment.DIRECTORY_DCIM + "/Camera/filename.mp4",
- "/storage/emulated/" + Environment.DIRECTORY_DCIM + "/Camera/filename.jpeg",
- "/storage/emulated/0/dcmi/Camera/dir/filename.mp4");
-
- for (String path : filePaths) {
- assertThat(mUnderTest.supportsTranscode(path)).isFalse();
- }
- }
-
- @Test
- public void testDoesNotTranscodeForMediaProvider() {
- int transcodeReason = mUnderTest.shouldTranscode(SOME_VALID_FILE_PATH,
- TranscodeHelperImpl.getMyUid(),
- null);
- assertThat(transcodeReason).isEqualTo(0);
-
- Random random = new Random(System.currentTimeMillis());
- Bundle bundle = new Bundle();
- bundle.putInt(MediaStore.EXTRA_MEDIA_CAPABILITIES_UID, TranscodeHelperImpl.getMyUid());
- int randomAppUid = random.nextInt(
- Process.LAST_APPLICATION_UID - Process.FIRST_APPLICATION_UID + 1)
- + Process.FIRST_APPLICATION_UID;
- transcodeReason = mUnderTest.shouldTranscode(SOME_VALID_FILE_PATH, randomAppUid, bundle);
- assertThat(transcodeReason).isEqualTo(0);
- }
-
- @Test
- public void testDoesNotTranscodeForSystemProcesses() {
- Random random = new Random(System.currentTimeMillis());
- int randomSystemProcessUid = random.nextInt(Process.FIRST_APPLICATION_UID);
- int transcodeReason = mUnderTest.shouldTranscode(SOME_VALID_FILE_PATH,
- randomSystemProcessUid, null);
- assertThat(transcodeReason).isEqualTo(0);
- }
-
- @Test
- public void testDoesNotTranscodeIfAppAcceptsOriginalFormat() {
- Random random = new Random(System.currentTimeMillis());
- Bundle bundle = new Bundle();
- bundle.putBoolean(MediaStore.EXTRA_ACCEPT_ORIGINAL_MEDIA_FORMAT, true);
- int randomAppUid = random.nextInt(
- Process.LAST_APPLICATION_UID - Process.FIRST_APPLICATION_UID + 1)
- + Process.FIRST_APPLICATION_UID;
- int transcodeReason = mUnderTest.doesAppNeedTranscoding(randomAppUid, bundle,
- TranscodeHelperImpl.FLAG_HEVC, 0);
- assertThat(transcodeReason).isEqualTo(0);
- }
-
- @Test
- public void testDoesNotTranscodeIfAppExtraMediaCapabilitiesHevc_supported() {
- Random random = new Random(System.currentTimeMillis());
- ApplicationMediaCapabilities capabilities =
- new ApplicationMediaCapabilities.Builder().addSupportedVideoMimeType(
- MediaFormat.MIMETYPE_VIDEO_HEVC).build();
- Bundle bundle = new Bundle();
- bundle.putParcelable(MediaStore.EXTRA_MEDIA_CAPABILITIES, capabilities);
- int randomAppUid = random.nextInt(
- Process.LAST_APPLICATION_UID - Process.FIRST_APPLICATION_UID + 1)
- + Process.FIRST_APPLICATION_UID;
- int transcodeReason = mUnderTest.doesAppNeedTranscoding(randomAppUid, bundle,
- TranscodeHelperImpl.FLAG_HEVC, 0);
- assertThat(transcodeReason).isEqualTo(0);
- }
-
- @Test
- public void testTranscodesIfAppExtraMediaCapabilitiesHevc_unsupported() {
- Random random = new Random(System.currentTimeMillis());
- ApplicationMediaCapabilities capabilities =
- new ApplicationMediaCapabilities.Builder().addUnsupportedVideoMimeType(
- MediaFormat.MIMETYPE_VIDEO_HEVC).build();
- Bundle bundle = new Bundle();
- bundle.putParcelable(MediaStore.EXTRA_MEDIA_CAPABILITIES, capabilities);
- int randomAppUid = random.nextInt(
- Process.LAST_APPLICATION_UID - Process.FIRST_APPLICATION_UID + 1)
- + Process.FIRST_APPLICATION_UID;
- int transcodeReason = mUnderTest.doesAppNeedTranscoding(randomAppUid, bundle,
- TranscodeHelperImpl.FLAG_HEVC, 0);
- assertThat(transcodeReason).isEqualTo(
- MediaProviderStatsLog.TRANSCODING_DATA__ACCESS_REASON__APP_EXTRA);
- }
-}
diff --git a/tests/src/com/android/providers/media/UnreliableVolumeTest.java b/tests/src/com/android/providers/media/UnreliableVolumeTest.java
deleted file mode 100644
index 12871ef..0000000
--- a/tests/src/com/android/providers/media/UnreliableVolumeTest.java
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media;
-
-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
deleted file mode 100644
index 595d3d8..0000000
--- a/tests/src/com/android/providers/media/cloudproviders/CloudProviderNoIntentFilter.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.cloudproviders;
-
-import static android.provider.CloudMediaProviderContract.MediaInfo;
-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 java.io.FileNotFoundException;
-
-/**
- * Implements a placeholder {@link CloudMediaProvider}.
- */
-public class CloudProviderNoIntentFilter extends CloudMediaProvider {
- @Override
- public boolean onCreate() {
- return true;
- }
-
- @Override
- public Cursor onQueryMedia(String mediaId) {
- throw new UnsupportedOperationException("onQueryMedia by id not supported");
- }
-
- @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 onOpenThumbnail(String mediaId, Point size,
- CancellationSignal signal) throws FileNotFoundException {
- throw new UnsupportedOperationException("onOpenThumbnail not supported");
- }
-
- @Override
- public ParcelFileDescriptor onOpenMedia(String mediaId, CancellationSignal signal)
- throws FileNotFoundException {
- throw new UnsupportedOperationException("onOpenMedia not supported");
- }
-
- @Override
- public Bundle onGetMediaInfo(Bundle extras) {
- throw new UnsupportedOperationException("onGetMediaInfo not supported");
- }
-}
diff --git a/tests/src/com/android/providers/media/cloudproviders/CloudProviderNoPermission.java b/tests/src/com/android/providers/media/cloudproviders/CloudProviderNoPermission.java
deleted file mode 100644
index 9cd84e7..0000000
--- a/tests/src/com/android/providers/media/cloudproviders/CloudProviderNoPermission.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.cloudproviders;
-
-import static android.provider.CloudMediaProviderContract.MediaInfo;
-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 java.io.FileNotFoundException;
-
-/**
- * Implements a placeholder {@link CloudMediaProvider}.
- */
-public class CloudProviderNoPermission extends CloudMediaProvider {
- @Override
- public boolean onCreate() {
- return true;
- }
-
- @Override
- public Cursor onQueryMedia(String mediaId) {
- throw new UnsupportedOperationException("onQueryMedia by id not supported");
- }
-
- @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 onOpenThumbnail(String mediaId, Point size,
- CancellationSignal signal) throws FileNotFoundException {
- throw new UnsupportedOperationException("onOpenThumbnail not supported");
- }
-
- @Override
- public ParcelFileDescriptor onOpenMedia(String mediaId, CancellationSignal signal)
- throws FileNotFoundException {
- throw new UnsupportedOperationException("onOpenMedia not supported");
- }
-
- @Override
- public Bundle onGetMediaInfo(Bundle extras) {
- throw new UnsupportedOperationException("onGetMediaInfo not supported");
- }
-}
diff --git a/tests/src/com/android/providers/media/cloudproviders/CloudProviderPrimary.java b/tests/src/com/android/providers/media/cloudproviders/CloudProviderPrimary.java
deleted file mode 100644
index e129b6f..0000000
--- a/tests/src/com/android/providers/media/cloudproviders/CloudProviderPrimary.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.cloudproviders;
-
-import static android.provider.CloudMediaProviderContract.MediaInfo;
-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.MediaProvider;
-import com.android.providers.media.PickerProviderMediaGenerator;
-
-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(String mediaId) {
- throw new UnsupportedOperationException("onQueryMedia by id not supported");
- }
-
- @Override
- public Cursor onQueryMedia(Bundle extras) {
- return mMediaGenerator.getMedia(getGeneration(extras));
- }
-
- @Override
- public Cursor onQueryDeletedMedia(Bundle extras) {
- return mMediaGenerator.getDeletedMedia(getGeneration(extras));
- }
-
- @Override
- public AssetFileDescriptor onOpenThumbnail(String mediaId, Point size,
- CancellationSignal signal) throws FileNotFoundException {
- throw new UnsupportedOperationException("onOpenThumbnail not supported");
- }
-
- @Override
- public ParcelFileDescriptor onOpenMedia(String mediaId, CancellationSignal signal)
- throws FileNotFoundException {
- throw new UnsupportedOperationException("onOpenMedia not supported");
- }
-
- @Override
- public Bundle onGetMediaInfo(Bundle extras) {
- Bundle bundle = new Bundle();
- bundle.putString(MediaInfo.MEDIA_VERSION, mMediaGenerator.getVersion());
- bundle.putLong(MediaInfo.MEDIA_GENERATION, mMediaGenerator.getGeneration());
- bundle.putLong(MediaInfo.MEDIA_COUNT, mMediaGenerator.getCount());
-
- return bundle;
- }
-
- private static long getGeneration(Bundle extras) {
- return extras == null ? 0 : extras.getLong(MediaInfo.MEDIA_GENERATION, 0);
- }
-}
diff --git a/tests/src/com/android/providers/media/cloudproviders/CloudProviderSecondary.java b/tests/src/com/android/providers/media/cloudproviders/CloudProviderSecondary.java
deleted file mode 100644
index dbd9b93..0000000
--- a/tests/src/com/android/providers/media/cloudproviders/CloudProviderSecondary.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.cloudproviders;
-
-import static android.provider.CloudMediaProviderContract.MediaInfo;
-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.MediaProvider;
-import com.android.providers.media.PickerProviderMediaGenerator;
-
-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(String mediaId) {
- throw new UnsupportedOperationException("onQueryMedia by id not supported");
- }
-
- @Override
- public Cursor onQueryMedia(Bundle extras) {
- return mMediaGenerator.getMedia(getGeneration(extras));
- }
-
- @Override
- public Cursor onQueryDeletedMedia(Bundle extras) {
- return mMediaGenerator.getDeletedMedia(getGeneration(extras));
- }
-
- @Override
- public AssetFileDescriptor onOpenThumbnail(String mediaId, Point size,
- CancellationSignal signal) throws FileNotFoundException {
- throw new UnsupportedOperationException("onOpenThumbnail not supported");
- }
-
- @Override
- public ParcelFileDescriptor onOpenMedia(String mediaId, CancellationSignal signal)
- throws FileNotFoundException {
- throw new UnsupportedOperationException("onOpenMedia not supported");
- }
-
- @Override
- public Bundle onGetMediaInfo(Bundle extras) {
- Bundle bundle = new Bundle();
- bundle.putString(MediaInfo.MEDIA_VERSION, mMediaGenerator.getVersion());
- bundle.putLong(MediaInfo.MEDIA_GENERATION, mMediaGenerator.getGeneration());
- bundle.putLong(MediaInfo.MEDIA_COUNT, mMediaGenerator.getCount());
-
- return bundle;
- }
-
- private static long getGeneration(Bundle extras) {
- return extras == null ? 0 : extras.getLong(MediaInfo.MEDIA_GENERATION, 0);
- }
-}
diff --git a/tests/src/com/android/providers/media/metrics/StorageAccessMetricsTest.java b/tests/src/com/android/providers/media/metrics/StorageAccessMetricsTest.java
deleted file mode 100644
index 5be69ff..0000000
--- a/tests/src/com/android/providers/media/metrics/StorageAccessMetricsTest.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.metrics;
-
-import static com.android.providers.media.metrics.StorageAccessMetrics.PackageStorageAccessStats;
-import static com.android.providers.media.metrics.StorageAccessMetrics.UID_SAMPLES_COUNT_LIMIT;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.provider.MediaStore;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.List;
-
-@RunWith(AndroidJUnit4.class)
-public class StorageAccessMetricsTest {
-
- private static StorageAccessMetrics storageAccessMetrics;
-
- @Before
- public void setUp() {
- storageAccessMetrics = new StorageAccessMetrics();
- }
-
- @Test
- public void testLogMimeType() {
- storageAccessMetrics.logMimeType(3, "my-mime-type");
- storageAccessMetrics.logMimeType(3, null);
- storageAccessMetrics.logMimeType(3, "my-mime-type-2");
- storageAccessMetrics.logMimeType(3, "my-mime-type-2");
- storageAccessMetrics.logMimeType(3, "my-mime-type-3");
- storageAccessMetrics.logMimeType(3, "my-mime-type-2");
- List<PackageStorageAccessStats> statsList =
- storageAccessMetrics.getSampleStats();
-
- assertThat(statsList).hasSize(1);
-
- PackageStorageAccessStats stats = statsList.get(0);
- assertThat(stats.getUid()).isEqualTo(3);
- assertThat(stats.mTotalAccesses).isEqualTo(0);
- assertThat(stats.mFilePathAccesses).isEqualTo(0);
- assertThat(stats.mSecondaryStorageAccesses).isEqualTo(0);
- assertThat(stats.mMimeTypes.stream().toArray())
- .asList()
- .containsExactly("my-mime-type", "my-mime-type-2", "my-mime-type-3");
- }
-
- @Test
- public void testIncrementAccessViaFuse() {
- storageAccessMetrics.logAccessViaFuse(3, "filename.txt");
- List<PackageStorageAccessStats> statsList =
- storageAccessMetrics.getSampleStats();
-
- assertThat(statsList).hasSize(1);
-
- PackageStorageAccessStats stats = statsList.get(0);
- assertThat(stats.getUid()).isEqualTo(3);
- assertThat(stats.mTotalAccesses).isEqualTo(0);
- assertThat(stats.mFilePathAccesses).isEqualTo(1);
- assertThat(stats.mSecondaryStorageAccesses).isEqualTo(0);
- assertThat(stats.mMimeTypes.stream().toArray())
- .asList()
- .containsExactly("text/plain");
- }
-
- @Test
- public void testIncrementAccessViaMediaProvider_externalVolumes() {
- storageAccessMetrics.logAccessViaMediaProvider(3, MediaStore.VOLUME_EXTERNAL);
- storageAccessMetrics.logAccessViaMediaProvider(
- 3, MediaStore.VOLUME_EXTERNAL_PRIMARY);
- List<PackageStorageAccessStats> statsList =
- storageAccessMetrics.getSampleStats();
-
- assertThat(statsList).hasSize(1);
-
- PackageStorageAccessStats stats = statsList.get(0);
- assertThat(stats.getUid()).isEqualTo(3);
- assertThat(stats.mTotalAccesses).isEqualTo(2);
- assertThat(stats.mFilePathAccesses).isEqualTo(0);
- assertThat(stats.mSecondaryStorageAccesses).isEqualTo(0);
- assertThat(stats.mMimeTypes.size()).isEqualTo(0);
- }
-
- @Test
- public void testIncrementAccessViaMediaProvider_ignoredVolumes() {
- storageAccessMetrics.logAccessViaMediaProvider(3, MediaStore.VOLUME_INTERNAL);
- storageAccessMetrics.logAccessViaMediaProvider(3, MediaStore.VOLUME_DEMO);
- storageAccessMetrics.logAccessViaMediaProvider(3, MediaStore.MEDIA_SCANNER_VOLUME);
- List<PackageStorageAccessStats> statsList =
- storageAccessMetrics.getSampleStats();
-
- assertThat(statsList).isEmpty();
- }
-
- @Test
- public void testIncrementAccessViaMediaProvider_secondaryVolumes() {
- storageAccessMetrics.logAccessViaMediaProvider(3, "my-volume");
- List<PackageStorageAccessStats> statsList =
- storageAccessMetrics.getSampleStats();
-
- assertThat(statsList).hasSize(1);
-
- PackageStorageAccessStats stats = statsList.get(0);
- assertThat(stats.getUid()).isEqualTo(3);
- assertThat(stats.mTotalAccesses).isEqualTo(1);
- assertThat(stats.mFilePathAccesses).isEqualTo(0);
- assertThat(stats.mSecondaryStorageAccesses).isEqualTo(1);
- assertThat(stats.mMimeTypes.size()).isEqualTo(0);
- }
-
- @Test
- public void testUidCountGreaterThanLimit() {
- int i = 0;
- for (; i < UID_SAMPLES_COUNT_LIMIT; i++) {
- storageAccessMetrics.logAccessViaFuse(i, "myfile.txt");
- }
- // Add 1 more
- storageAccessMetrics.logAccessViaFuse(i, "myfile.txt");
- // Pull stats
- List<PackageStorageAccessStats> statsList =
- storageAccessMetrics.getSampleStats();
-
- assertThat(statsList).hasSize(UID_SAMPLES_COUNT_LIMIT);
- }
-}
diff --git a/tests/src/com/android/providers/media/metrics/TranscodeMetricsTest.java b/tests/src/com/android/providers/media/metrics/TranscodeMetricsTest.java
deleted file mode 100644
index 8b635df..0000000
--- a/tests/src/com/android/providers/media/metrics/TranscodeMetricsTest.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.metrics;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.util.StatsEvent;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.List;
-
-@RunWith(AndroidJUnit4.class)
-public class TranscodeMetricsTest {
- private static final int UNKNOWN_ATOM_TAG = -1;
-
- private static final TranscodeMetrics.TranscodingStatsData EMPTY_STATS_DATA =
- new TranscodeMetrics.TranscodingStatsData(
- "something" /* mRequestorPackage */,
- 0 /* mAccessType */,
- 0 /* mFileSizeBytes */,
- 0 /* mTranscodeResult */,
- 0 /* mTranscodeDurationMillis */,
- 0 /* mFileDurationMillis */,
- 0 /* mFrameRate */,
- (short) 0 /* mAccessReason */);
-
- @After
- public void tearDown() {
- // this is to reset the saved data in TranscodeMetrics.
- TranscodeMetrics.pullStatsEvents();
- }
-
- @Test
- public void testSaveStatsData_doesNotGoBeyondHardLimit() {
- for (int i = 0; i < TranscodeMetrics.getStatsDataCountHardLimit() + 5; ++i) {
- TranscodeMetrics.saveStatsData(EMPTY_STATS_DATA);
- }
- assertThat(TranscodeMetrics.getSavedStatsDataCount()).isEqualTo(
- TranscodeMetrics.getStatsDataCountHardLimit());
- }
-
- @Test
- public void testSaveStatsData_totalStatsDataCountEqualsPassedData() {
- int totalRequestsToPass = TranscodeMetrics.getStatsDataCountHardLimit() + 5;
- for (int i = 0; i < totalRequestsToPass; ++i) {
- TranscodeMetrics.saveStatsData(EMPTY_STATS_DATA);
- }
- assertThat(TranscodeMetrics.getTotalStatsDataCount()).isEqualTo(totalRequestsToPass);
- }
-
- @Test
- public void testSaveStatsData_savedStatsDataCountEqualsPassedData_withinHardLimit() {
- int totalRequestsToPass = TranscodeMetrics.getStatsDataCountHardLimit() - 5;
- for (int i = 0; i < totalRequestsToPass; ++i) {
- TranscodeMetrics.saveStatsData(EMPTY_STATS_DATA);
- }
- assertThat(TranscodeMetrics.getSavedStatsDataCount()).isEqualTo(totalRequestsToPass);
- }
-
- @Test
- public void testHandleStatsEventDataRequest_resetsData() {
- for (int i = 0; i < TranscodeMetrics.getStatsDataCountHardLimit(); ++i) {
- TranscodeMetrics.saveStatsData(EMPTY_STATS_DATA);
- }
-
- List<StatsEvent> statsEvents = TranscodeMetrics.pullStatsEvents();
-
- assertThat(TranscodeMetrics.getSavedStatsDataCount()).isEqualTo(0);
- assertThat(TranscodeMetrics.getTotalStatsDataCount()).isEqualTo(0);
- }
-
- @Test
- public void testHandleStatsEventDataRequest_fillsExactlySampleLimit_excessData() {
- for (int i = 0; i < TranscodeMetrics.getStatsDataCountHardLimit(); ++i) {
- TranscodeMetrics.saveStatsData(EMPTY_STATS_DATA);
- }
-
- List<StatsEvent> statsEvents = TranscodeMetrics.pullStatsEvents();
-
- assertThat(statsEvents.size()).isEqualTo(TranscodeMetrics.getStatsDataSampleLimit());
- }
-
- @Test
- public void testHandleStatsEventDataRequest_fillsExactlyAsSaved_dataWithinSampleLimit() {
- int totalRequestsToPass = TranscodeMetrics.getStatsDataSampleLimit() - 5;
- for (int i = 0; i < totalRequestsToPass; ++i) {
- TranscodeMetrics.saveStatsData(EMPTY_STATS_DATA);
- }
-
- List<StatsEvent> statsEvents = TranscodeMetrics.pullStatsEvents();
-
- assertThat(statsEvents.size()).isEqualTo(totalRequestsToPass);
- }
-}
diff --git a/tests/src/com/android/providers/media/photopicker/ItemsProviderTest.java b/tests/src/com/android/providers/media/photopicker/ItemsProviderTest.java
deleted file mode 100644
index 597b604..0000000
--- a/tests/src/com/android/providers/media/photopicker/ItemsProviderTest.java
+++ /dev/null
@@ -1,793 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker;
-
-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 android.Manifest;
-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;
-
-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 static Context sIsolatedContext;
- private static ContentResolver sIsolatedResolver;
- private static ItemsProvider sItemsProvider;
-
- @Before
- public void setUp() {
- InstrumentationRegistry.getInstrumentation().getUiAutomation()
- .adoptShellPermissionIdentity(Manifest.permission.LOG_COMPAT_CHANGE,
- Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
- 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();
- sItemsProvider = new ItemsProvider(sIsolatedContext);
-
- // Wait for MediaStore to be Idle to reduce flakes caused by database updates
- MediaStore.waitForIdle(sIsolatedResolver);
- }
-
- /**
- * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
- * {@link Category#CATEGORY_CAMERA}.
- */
- @Test
- public void testGetCategories_camera() throws Exception {
- Cursor c = sItemsProvider.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(Category.CATEGORY_CAMERA, /* numberOfItems */ 1);
- } finally {
- imageFile.delete();
- }
- }
-
- /**
- * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
- * {@link Category#CATEGORY_CAMERA}.
- */
- @Test
- public void testGetCategories_not_camera() throws Exception {
- Cursor c = sItemsProvider.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(Category.CATEGORY_CAMERA, /* numberOfItems */ 0);
- } finally {
- nonCameraImageFile.delete();
- }
- }
-
- /**
- * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
- * {@link Category#CATEGORY_VIDEOS}.
- */
- @Test
- public void testGetCategories_videos() throws Exception {
- Cursor c = sItemsProvider.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(Category.CATEGORY_VIDEOS, /* numberOfItems */ 1);
- } finally {
- videoFile.delete();
- }
- }
-
- /**
- * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
- * {@link Category#CATEGORY_VIDEOS}.
- */
- @Test
- public void testGetCategories_not_videos() throws Exception {
- Cursor c = sItemsProvider.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(Category.CATEGORY_VIDEOS, /* numberOfItems */ 0);
- } finally {
- imageFile.delete();
- }
- }
-
- /**
- * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
- * {@link Category#CATEGORY_SCREENSHOTS}.
- */
- @Test
- public void testGetCategories_screenshots() throws Exception {
- Cursor c = sItemsProvider.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);
- try {
- assertGetCategoriesMatchSingle(Category.CATEGORY_SCREENSHOTS, /* numberOfItems */ 1);
- } finally {
- imageFile.delete();
- }
- }
-
- /**
- * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
- * {@link Category#CATEGORY_SCREENSHOTS}.
- */
- @Test
- public void testGetCategories_not_screenshots() throws Exception {
- Cursor c = sItemsProvider.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(Category.CATEGORY_SCREENSHOTS, /* numberOfItems */ 0);
- } finally {
- imageFile.delete();
- }
- }
-
- /**
- * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
- * {@link Category#CATEGORY_FAVORITES}.
- */
- @Test
- public void testGetCategories_favorites() throws Exception {
- Cursor c = sItemsProvider.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(Category.CATEGORY_FAVORITES, /* numberOfItems */1);
- } finally {
- imageFile.delete();
- }
- }
-
- /**
- * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
- * {@link Category#CATEGORY_FAVORITES}.
- */
- @Test
- public void testGetCategories_not_favorites() throws Exception {
- Cursor c = sItemsProvider.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(Category.CATEGORY_FAVORITES, /* numberOfItems */ 0);
- } finally {
- nonFavImageFile.delete();
- }
- }
-
- /**
- * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
- * {@link Category#CATEGORY_DOWNLOADS}.
- */
- @Test
- public void testGetCategories_downloads() throws Exception {
- Cursor c = sItemsProvider.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(Category.CATEGORY_DOWNLOADS, /* numberOfItems */ 1);
- } finally {
- imageFile.delete();
- }
- }
-
- /**
- * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
- * {@link Category#CATEGORY_DOWNLOADS}.
- */
- @Test
- public void testGetCategories_not_downloads() throws Exception {
- Cursor c = sItemsProvider.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(Category.CATEGORY_DOWNLOADS, /* numberOfItems */ 0);
- } finally {
- nonDownloadsImageFile.delete();
- }
- }
-
- /**
- * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
- * {@link Category#CATEGORY_CAMERA} and {@link Category#CATEGORY_VIDEOS}.
- */
- @Test
- public void testGetCategories_camera_and_videos() throws Exception {
- Cursor c = sItemsProvider.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(Category.CATEGORY_CAMERA, Category.CATEGORY_VIDEOS,
- /* numberOfItemsInCamera */ 1,
- /* numberOfItemsInVideos */ 1);
- } finally {
- videoFile.delete();
- }
- }
-
- /**
- * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
- * {@link Category#CATEGORY_SCREENSHOTS} and {@link Category#CATEGORY_FAVORITES}.
- */
- @Test
- public void testGetCategories_screenshots_and_favorites() throws Exception {
- Cursor c = sItemsProvider.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(Category.CATEGORY_SCREENSHOTS,
- Category.CATEGORY_FAVORITES,
- /* numberOfItemsInScreenshots */ 1,
- /* numberOfItemsInFavorites */ 1);
- } finally {
- imageFile.delete();
- }
- }
-
- /**
- * Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
- * {@link Category#CATEGORY_DOWNLOADS} and {@link Category#CATEGORY_FAVORITES}.
- */
- @Test
- public void testGetCategories_downloads_and_favorites() throws Exception {
- Cursor c = sItemsProvider.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(Category.CATEGORY_DOWNLOADS,
- Category.CATEGORY_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 {
- Cursor res = sItemsProvider.getItems(/* category */ null, /* offset */ 0,
- /* limit */ -1, /* mimeType */ null, /* userId */ null);
- assertThat(res).isNotNull();
- final int initialCountOfItems = res.getCount();
-
- // 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 {
- res = sItemsProvider.getItems(/* category */ null, /* offset */ 0, /* limit */ -1,
- /* mimeType */ null, /* userId */ null);
- assertThat(res).isNotNull();
- final int laterCountOfItems = res.getCount();
-
- assertThat(laterCountOfItems).isEqualTo(initialCountOfItems + 2);
-
- assertThatOnlyImagesVideos(res);
- assertThatAllImagesVideos(res.getCount());
- } finally {
- imageFile.delete();
- videoFile.delete();
- }
- }
-
- /**
- * Tests {@link {@link ItemsProvider#getItems(String, int, int, String, UserId)}} does not
- * return hidden images/videos.
- */
- @Test
- public void testGetItems_nonMedia() throws Exception {
- Cursor res = sItemsProvider.getItems(/* category */ null, /* offset */ 0,
- /* limit */ -1, /* mimeType */ null, /* userId */ null);
- assertThat(res).isNotNull();
- final int initialCountOfItems = res.getCount();
-
- // 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 {
- res = sItemsProvider.getItems(/* category */ null, /* offset */ 0, /* limit */ -1,
- /* mimeType */ null, /* userId */ null);
- assertThat(res).isNotNull();
- final int laterCountOfItems = res.getCount();
-
- assertThat(laterCountOfItems).isEqualTo(initialCountOfItems);
- } 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 {
- Cursor res = sItemsProvider.getItems(/* category */ null, /* offset */ 0,
- /* limit */ -1, /* mimeType */ "image/*", /* userId */ null);
- assertThat(res).isNotNull();
- final int initialCountOfItems = res.getCount();
-
- // 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 {
- res = sItemsProvider.getItems(/* category */ null, /* offset */ 0, /* limit */ -1,
- /* mimeType */ "image/*", /* userId */ null);
- assertThat(res).isNotNull();
- final int laterCountOfItems = res.getCount();
-
- assertThat(laterCountOfItems).isEqualTo(initialCountOfItems + 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 {
- Cursor res = sItemsProvider.getItems(/* category */ null, /* offset */ 0,
- /* limit */ -1, /* mimeType */ "image/png", /* userId */ null);
- assertThat(res).isNotNull();
- final int initialCountOfItems = res.getCount();
-
- // Create a jpg file image. Tests negative use case, this should not be returned below.
- File imageFile = assertCreateNewImage();
- try {
- res = sItemsProvider.getItems(/* category */ null, /* offset */ 0, /* limit */ -1,
- /* mimeType */ "image/png", /* userId */ null);
- assertThat(res).isNotNull();
- final int laterCountOfItems = res.getCount();
-
- assertThat(laterCountOfItems).isEqualTo(initialCountOfItems);
- } 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 {
- Cursor res = sItemsProvider.getItems(/* category */ null, /* offset */ 0,
- /* limit */ -1, /* mimeType */ "image/*", /* userId */ null);
- assertThat(res).isNotNull();
- final int initialCountOfItems = res.getCount();
-
- // 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 {
- res = sItemsProvider.getItems(/* category */ null, /* offset */ 0, /* limit */ -1,
- /* mimeType */ "image/*", /* userId */ null);
- assertThat(res).isNotNull();
- final int laterCountOfItems = res.getCount();
-
- assertThat(laterCountOfItems).isEqualTo(initialCountOfItems);
- } 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 {
- Cursor res = sItemsProvider.getItems(/* category */ null, /* offset */ 0,
- /* limit */ -1, /* mimeType */ "video/*", /* userId */ null);
- assertThat(res).isNotNull();
- final int initialCountOfItems = res.getCount();
-
- // 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 {
- res = sItemsProvider.getItems(/* category */ null, /* offset */ 0, /* limit */ -1,
- /* mimeType */ "video/*", /* userId */ null);
- assertThat(res).isNotNull();
- final int laterCountOfItems = res.getCount();
-
- assertThat(laterCountOfItems).isEqualTo(initialCountOfItems + 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 {
- Cursor res = sItemsProvider.getItems(/* category */ null, /* offset */ 0,
- /* limit */ -1, /* mimeType */ "video/mp4", /* userId */ null);
- assertThat(res).isNotNull();
- final int initialCountOfItems = res.getCount();
-
- // Create a mp4 video file. Tests positive use case, this should be returned below.
- File videoFile = assertCreateNewVideo();
- try {
- res = sItemsProvider.getItems(/* category */ null, /* offset */ 0, /* limit */ -1,
- /* mimeType */ "video/mp4", /* userId */ null);
- assertThat(res).isNotNull();
- final int laterCountOfItems = res.getCount();
-
- assertThat(laterCountOfItems).isEqualTo(initialCountOfItems + 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 {
- Cursor res = sItemsProvider.getItems(/* category */ null, /* offset */ 0,
- /* limit */ -1, /* mimeType */ "video/*", /* userId */ null);
- assertThat(res).isNotNull();
- final int initialCountOfItems = res.getCount();
-
- // 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 {
- res = sItemsProvider.getItems(/* category */ null, /* offset */ 0, /* limit */ -1,
- /* mimeType */ "video/*", /* userId */ null);
- assertThat(res).isNotNull();
- final int laterCountOfItems = res.getCount();
-
- assertThat(laterCountOfItems).isEqualTo(initialCountOfItems);
- } finally {
- imageFileHidden.delete();
- videoFileHidden.delete();
- hiddenDir.delete();
- }
- }
-
- private void assertGetCategoriesMatchSingle(String expectedCategoryName,
- int expectedNumberOfItems) throws Exception {
- if (expectedNumberOfItems == 0) {
- assertCategoriesNoMatch(expectedCategoryName);
- return;
- }
-
- Cursor c = sItemsProvider.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(Category.CategoryColumns.NAME);
- final int numOfItemsColumnIndex = c.getColumnIndexOrThrow(
- Category.CategoryColumns.NUMBER_OF_ITEMS);
- final int coverIdIndex = c.getColumnIndexOrThrow(Category.CategoryColumns.COVER_ID);
-
- final String categoryName = c.getString(nameColumnIndex);
- final int numOfItems = c.getInt(numOfItemsColumnIndex);
- final Uri coverUri = ItemsProvider.getItemsUri(c.getString(coverIdIndex),
- /* authority */ null, UserId.CURRENT_USER);
-
- assertThat(categoryName).isEqualTo(expectedCategoryName);
- assertThat(numOfItems).isEqualTo(expectedNumberOfItems);
- assertCategoryUriIsValid(coverUri);
- }
-
- private void assertCategoryUriIsValid(Uri uri) throws Exception {
- final AssetFileDescriptor fd1 = sIsolatedResolver.openTypedAssetFile(uri, "image/*", null,
- null);
- assertThat(fd1).isNotNull();
- final ParcelFileDescriptor fd2 = sIsolatedResolver.openFileDescriptor(uri, "r");
- assertThat(fd2).isNotNull();
- }
-
- private void assertCategoriesNoMatch(String expectedCategoryName) {
- Cursor c = sItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
- while (c != null && c.moveToNext()) {
- final int nameColumnIndex = c.getColumnIndexOrThrow(Category.CategoryColumns.NAME);
- final String categoryName = c.getString(nameColumnIndex);
- assertThat(categoryName).isNotEqualTo(expectedCategoryName);
- }
- }
-
- private void assertGetCategoriesMatchMultiple(String category1, String category2,
- int numberOfItems1, int numberOfItems2) {
- Cursor c = sItemsProvider.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(Category.CategoryColumns.NAME);
- final int numOfItemsColumnIndex = c.getColumnIndexOrThrow(
- Category.CategoryColumns.NUMBER_OF_ITEMS);
-
- 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(sIsolatedResolver, file);
- final ContentValues values = new ContentValues();
- values.put(MediaStore.MediaColumns.IS_FAVORITE, 1);
- // Assert that 1 row corresponding to this file is updated.
- assertThat(sIsolatedResolver.update(uri, values, null)).isEqualTo(1);
- // Wait for MediaStore to be Idle to reduce flakes caused by database updates
- MediaStore.waitForIdle(sIsolatedResolver);
- }
-
- 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 = sIsolatedResolver.query(
- MediaStore.Images.Media.getContentUri(VOLUME_EXTERNAL), null, null, null)) {
- assertThat(c.moveToFirst()).isTrue();
- return c.getCount();
- }
- }
-
- private int getCountOfMediaStoreVideos() {
- try (Cursor c = sIsolatedResolver.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 dir, String fileName) throws Exception {
- if (!dir.exists()) {
- dir.mkdirs();
- }
- assertThat(dir.exists()).isTrue();
- final File file = new File(dir, fileName);
- assertThat(file.createNewFile()).isTrue();
-
- MediaStore.scanFile(sIsolatedResolver, file);
- return file;
- }
-
- 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 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(sIsolatedResolver, nomedia);
-
- return dir;
- }
-}
diff --git a/tests/src/com/android/providers/media/photopicker/LocalProvider.java b/tests/src/com/android/providers/media/photopicker/LocalProvider.java
deleted file mode 100644
index d18bc0b..0000000
--- a/tests/src/com/android/providers/media/photopicker/LocalProvider.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker;
-
-import static android.provider.CloudMediaProviderContract.MediaInfo;
-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.MediaProvider;
-import com.android.providers.media.PickerProviderMediaGenerator;
-
-import java.io.FileNotFoundException;
-
-/**
- * Implements the a local {@link CloudMediaProvider} interface over items generated with
- * {@link MediaGenerator}
- */
-public class LocalProvider extends CloudMediaProvider {
- private 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(String mediaId) {
- throw new UnsupportedOperationException("onQueryMedia by id not supported");
- }
-
- @Override
- public Cursor onQueryMedia(Bundle extras) {
- return mMediaGenerator.getMedia(getGeneration(extras));
- }
-
- @Override
- public Cursor onQueryDeletedMedia(Bundle extras) {
- return mMediaGenerator.getDeletedMedia(getGeneration(extras));
- }
-
- @Override
- public AssetFileDescriptor onOpenThumbnail(String mediaId, Point size,
- CancellationSignal signal) throws FileNotFoundException {
- throw new UnsupportedOperationException("onOpenThumbnail not supported");
- }
-
- @Override
- public ParcelFileDescriptor onOpenMedia(String mediaId, CancellationSignal signal)
- throws FileNotFoundException {
- throw new UnsupportedOperationException("onOpenMedia not supported");
- }
-
- @Override
- public Bundle onGetMediaInfo(Bundle extras) {
- Bundle bundle = new Bundle();
- bundle.putString(MediaInfo.MEDIA_VERSION, mMediaGenerator.getVersion());
- bundle.putLong(MediaInfo.MEDIA_GENERATION, mMediaGenerator.getGeneration());
- bundle.putLong(MediaInfo.MEDIA_COUNT, mMediaGenerator.getCount());
-
- return bundle;
- }
-
- private static long getGeneration(Bundle extras) {
- return extras == null ? 0 : extras.getLong(MediaInfo.MEDIA_GENERATION, 0);
- }
-}
diff --git a/tests/src/com/android/providers/media/photopicker/PickerSyncControllerTest.java b/tests/src/com/android/providers/media/photopicker/PickerSyncControllerTest.java
deleted file mode 100644
index b753769..0000000
--- a/tests/src/com/android/providers/media/photopicker/PickerSyncControllerTest.java
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker;
-
-import static com.android.providers.media.PickerProviderMediaGenerator.MediaGenerator;
-import static com.android.providers.media.photopicker.data.PickerDbFacade.KEY_CLOUD_ID;
-import static com.android.providers.media.photopicker.data.PickerDbFacade.KEY_LOCAL_ID;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.os.SystemClock;
-import android.util.Pair;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.providers.media.PickerProviderMediaGenerator;
-import com.android.providers.media.photopicker.data.PickerDbFacade;
-import com.android.providers.media.photopicker.data.model.Item;
-import com.android.providers.media.util.BackgroundThread;
-
-import java.util.List;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@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 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 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_AND_LOCAL_1
- = Pair.create(LOCAL_ID_1, CLOUD_ID_1);
-
- private static final String VERSION_1 = "1";
- private static final String VERSION_2 = "2";
-
- private static final long SYNC_DELAY_MS = 1000;
-
- private Context mContext;
- private PickerDbFacade mFacade;
- private PickerSyncController mController;
-
- @Before
- public void setUp() {
- mLocalMediaGenerator.resetAll();
- mCloudPrimaryMediaGenerator.resetAll();
- mCloudSecondaryMediaGenerator.resetAll();
-
- mLocalMediaGenerator.setVersion(VERSION_1);
- mCloudPrimaryMediaGenerator.setVersion(VERSION_1);
- mCloudSecondaryMediaGenerator.setVersion(VERSION_1);
-
- mContext = InstrumentationRegistry.getTargetContext();
- mFacade = new PickerDbFacade(mContext, LOCAL_PROVIDER_AUTHORITY);
- mController = new PickerSyncController(mContext, mFacade, LOCAL_PROVIDER_AUTHORITY,
- /* syncDelay */ 0);
-
- mFacade.resetMedia(LOCAL_PROVIDER_AUTHORITY);
- mFacade.resetMedia(null);
- }
-
- @After
- public void tearDown() {
- // Set cloud provider to null to avoid trying to sync it during other tests
- // that might be using an IsolatedContext
- mController.setCloudProvider(null);
- }
-
- @Test
- public void testSyncPickerLocalOnly() {
- // 1. Do nothing
- mController.syncPicker();
- assertEmptyCursor();
-
- // 2. Add local only media
- addMedia(mLocalMediaGenerator, LOCAL_ONLY_1);
- addMedia(mLocalMediaGenerator, LOCAL_ONLY_2);
-
- mController.syncPicker();
- 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.syncPicker();
-
- 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.syncPicker();
-
- try (Cursor cr = queryMedia()) {
- assertThat(cr.getCount()).isEqualTo(1);
-
- assertCursor(cr, LOCAL_ID_2, LOCAL_PROVIDER_AUTHORITY);
- }
-
- // 5. Bump version
- mLocalMediaGenerator.setVersion(VERSION_2);
- mController.syncPicker();
-
- assertEmptyCursor();
- }
-
-
- @Test
- public void testSyncPickerCloudOnly() {
- // 1. Add media before setting primary cloud provider
- addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_1);
- addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_2);
- mController.syncPicker();
- assertEmptyCursor();
-
- // 2. Set secondary cloud provider
- mController.setCloudProvider(CLOUD_SECONDARY_PROVIDER_AUTHORITY);
- mController.syncPicker();
- assertEmptyCursor();
-
- // 3. Set primary cloud provider
- mController.setCloudProvider(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
- mController.syncPicker();
-
- 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
- mController.setCloudProvider(CLOUD_SECONDARY_PROVIDER_AUTHORITY);
- mController.syncPicker();
- assertEmptyCursor();
-
- // 5. Set primary cloud provider once again
- mController.setCloudProvider(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
- mController.syncPicker();
- 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
- mController.setCloudProvider(/* authority */ null);
- mController.syncPicker();
- assertEmptyCursor();
- }
-
- @Test
- public void testCloudResetSync() {
- mController.setCloudProvider(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
-
- // 1. Do nothing
- mController.syncPicker();
- assertEmptyCursor();
-
- // 2. Add cloud-only item
- addMedia(mCloudPrimaryMediaGenerator, CLOUD_ONLY_1);
-
- mController.syncPicker();
- try (Cursor cr = queryMedia()) {
- assertThat(cr.getCount()).isEqualTo(1);
-
- assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
- }
-
- // 3. Set invalid cloud version
- mCloudPrimaryMediaGenerator.setVersion(/* version */ null);
- mController.syncPicker();
- assertEmptyCursor();
-
- // 4. Set valid cloud version
- mCloudPrimaryMediaGenerator.setVersion(VERSION_1);
- mController.syncPicker();
-
- try (Cursor cr = queryMedia()) {
- assertThat(cr.getCount()).isEqualTo(1);
-
- assertCursor(cr, CLOUD_ID_1, CLOUD_PRIMARY_PROVIDER_AUTHORITY);
- }
- }
-
- @Test
- public void testSyncPickerCloudAndLocal() {
- // 1. Do nothing
- assertEmptyCursor();
-
- // 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);
-
- mController.setCloudProvider(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
- mController.syncPicker();
-
- 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.syncPicker();
-
- 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.syncPicker();
-
- 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.syncPicker();
-
- 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.syncPicker();
-
- assertEmptyCursor();
- }
-
- @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.syncPicker();
- assertThat(mFacade.getCloudProvider()).isEqualTo(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
-
- // 3. Can clear cloud provider
- assertThat(mController.setCloudProvider(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.syncPicker();
- 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.syncPicker();
- assertThat(mFacade.getCloudProvider()).isEqualTo(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
-
- // Invalid cloud provider is ignored
- assertThat(mController.setCloudProvider(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.syncPicker();
- assertThat(mFacade.getCloudProvider()).isEqualTo(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
- }
-
- @Test
- public void testGetSupportedCloudProviders() {
- List<String> providers = mController.getSupportedCloudProviders();
-
- assertThat(providers).containsExactly(CLOUD_PRIMARY_PROVIDER_AUTHORITY,
- CLOUD_SECONDARY_PROVIDER_AUTHORITY);
- }
-
- @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();
- BackgroundThread.waitForIdle();
- assertEmptyCursor();
-
- // 2. Sleep for delay
- SystemClock.sleep(SYNC_DELAY_MS);
- BackgroundThread.waitForIdle();
-
- try (Cursor cr = queryMedia()) {
- assertThat(cr.getCount()).isEqualTo(1);
-
- assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
- }
- }
-
- private static void addMedia(MediaGenerator generator, Pair<String, String> media) {
- generator.addMedia(media.first, media.second);
- }
-
- private static void deleteMedia(MediaGenerator generator, Pair<String, String> media) {
- generator.deleteMedia(media.first, media.second);
- }
-
- private Cursor queryMedia() {
- return mFacade.queryMedia(new PickerDbFacade.QueryFilterBuilder(1000).build());
- }
-
- private void assertEmptyCursor() {
- try (Cursor cr = queryMedia()) {
- assertThat(cr.getCount()).isEqualTo(0);
- }
- }
-
- private static void assertCursor(Cursor cursor, String id, String authority) {
- cursor.moveToNext();
- assertThat(cursor.getString(cursor.getColumnIndex(Item.ItemColumns.ID)))
- .isEqualTo(id);
- assertThat(cursor.getString(cursor.getColumnIndex(Item.ItemColumns.AUTHORITY)))
- .isEqualTo(authority);
- }
-}
diff --git a/tests/src/com/android/providers/media/photopicker/UnreliableVolumeFacadeTest.java b/tests/src/com/android/providers/media/photopicker/UnreliableVolumeFacadeTest.java
deleted file mode 100644
index d3f4923..0000000
--- a/tests/src/com/android/providers/media/photopicker/UnreliableVolumeFacadeTest.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker;
-
-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;
- }
-}
\ No newline at end of file
diff --git a/tests/src/com/android/providers/media/photopicker/data/ExternalDbFacadeTest.java b/tests/src/com/android/providers/media/photopicker/data/ExternalDbFacadeTest.java
deleted file mode 100644
index d9409db..0000000
--- a/tests/src/com/android/providers/media/photopicker/data/ExternalDbFacadeTest.java
+++ /dev/null
@@ -1,654 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.data;
-
-import static com.android.providers.media.photopicker.data.ExternalDbFacade.TABLE_FILES;
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.provider.CloudMediaProviderContract;
-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.photopicker.data.model.Category;
-import com.android.providers.media.scan.MediaScannerTest.IsolatedContext;
-
-import java.util.ArrayList;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class ExternalDbFacadeTest {
- private static final String TAG = "ExternalDbFacadeTest";
-
- private static final long OLD_ID1 = 1;
- private static final long OLD_ID2 = 2;
-
- 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);
-
- assertThat(facade.addDeletedMedia(OLD_ID1)).isTrue();
- assertThat(facade.addDeletedMedia(OLD_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(OLD_ID1);
- assertThat(ids).contains(OLD_ID2);
- }
-
- // Filter by generation should only return OLD_ID2
- try (Cursor cursor = facade.queryDeletedMedia(/* generation */ 1)) {
- assertThat(cursor.getCount()).isEqualTo(1);
-
- cursor.moveToFirst();
- assertThat(cursor.getLong(0)).isEqualTo(OLD_ID2);
- }
-
- // Adding ids again should succeed but bump generation_modified of OLD_ID1 and OLD_ID2
- assertThat(facade.addDeletedMedia(OLD_ID1)).isTrue();
- assertThat(facade.addDeletedMedia(OLD_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 OLD_ID2 should succeed
- assertThat(facade.removeDeletedMedia(OLD_ID2)).isTrue();
- // Remove OLD_ID2 again should fail
- assertThat(facade.removeDeletedMedia(OLD_ID2)).isFalse();
-
- // Verify only OLD_ID1 left
- try (Cursor cursor = facade.queryDeletedMedia(/* generation */ 0)) {
- assertThat(cursor.getCount()).isEqualTo(1);
-
- cursor.moveToFirst();
- assertThat(cursor.getLong(0)).isEqualTo(OLD_ID1);
- }
- }
- }
-
- @Test
- public void testDeletedMedia_onInsert() throws Exception {
- try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
- ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper);
-
- 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);
-
- // Non-media -> non-media: no-op
- assertThat(facade.onFileUpdated(OLD_ID1,
- FileColumns.MEDIA_TYPE_NONE, FileColumns.MEDIA_TYPE_NONE,
- /* oldIsTrashed */ false, /* newIsTrashed */ false,
- /* oldIsPending */ false, /* newIsPending */ false)).isFalse();
- assertDeletedMediaEmpty(facade);
-
- // Media -> non-media: added to deleted_media
- assertThat(facade.onFileUpdated(OLD_ID1,
- FileColumns.MEDIA_TYPE_IMAGE, FileColumns.MEDIA_TYPE_NONE,
- /* oldIsTrashed */ false, /* newIsTrashed */ false,
- /* oldIsPending */ false, /* newIsPending */ false)).isTrue();
- assertDeletedMedia(facade, OLD_ID1);
-
- // Non-media -> non-media: no-op
- assertThat(facade.onFileUpdated(OLD_ID1,
- FileColumns.MEDIA_TYPE_NONE, FileColumns.MEDIA_TYPE_NONE,
- /* oldIsTrashed */ false, /* newIsTrashed */ false,
- /* oldIsPending */ false, /* newIsPending */ false)).isFalse();
- assertDeletedMedia(facade, OLD_ID1);
-
- // Non-media -> media: remove from deleted_media
- assertThat(facade.onFileUpdated(OLD_ID1,
- FileColumns.MEDIA_TYPE_NONE, FileColumns.MEDIA_TYPE_IMAGE,
- /* oldIsTrashed */ false, /* newIsTrashed */ false,
- /* oldIsPending */ false, /* newIsPending */ false)).isTrue();
- assertDeletedMediaEmpty(facade);
-
- // Non-media -> media: no-op
- assertThat(facade.onFileUpdated(OLD_ID1,
- FileColumns.MEDIA_TYPE_NONE, FileColumns.MEDIA_TYPE_NONE,
- /* oldIsTrashed */ false, /* newIsTrashed */ false,
- /* oldIsPending */ false, /* newIsPending */ false)).isFalse();
- assertDeletedMediaEmpty(facade);
- }
- }
-
- @Test
- public void testDeletedMedia_onUpdate_trashed() throws Exception {
- try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
- ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper);
-
- // Was trashed but is now neither trashed nor pending
- assertThat(facade.onFileUpdated(OLD_ID1,
- FileColumns.MEDIA_TYPE_IMAGE, FileColumns.MEDIA_TYPE_IMAGE,
- /* oldIsTrashed */ true, /* newIsTrashed */ false,
- /* oldIsPending */ false, /* newIsPending */ false)).isTrue();
- assertDeletedMediaEmpty(facade);
-
- // Was not trashed but is now trashed
- assertThat(facade.onFileUpdated(OLD_ID1,
- FileColumns.MEDIA_TYPE_IMAGE, FileColumns.MEDIA_TYPE_IMAGE,
- /* oldIsTrashed */ false, /* newIsTrashed */ true,
- /* oldIsPending */ false, /* newIsPending */ false)).isTrue();
- assertDeletedMedia(facade, OLD_ID1);
-
- // Was trashed but is now neither trashed nor pending
- assertThat(facade.onFileUpdated(OLD_ID1,
- FileColumns.MEDIA_TYPE_IMAGE, FileColumns.MEDIA_TYPE_IMAGE,
- /* oldIsTrashed */ true, /* newIsTrashed */ false,
- /* oldIsPending */ false, /* newIsPending */ false)).isTrue();
- assertDeletedMediaEmpty(facade);
- }
- }
-
- @Test
- public void testDeletedMedia_onUpdate_pending() throws Exception {
- try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
- ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper);
-
- // Was pending but is now neither trashed nor pending
- assertThat(facade.onFileUpdated(OLD_ID1,
- FileColumns.MEDIA_TYPE_IMAGE, FileColumns.MEDIA_TYPE_IMAGE,
- /* oldIsTrashed */ false, /* newIsTrashed */ false,
- /* oldIsPending */ true, /* newIsPending */ false)).isTrue();
- assertDeletedMediaEmpty(facade);
-
- // Was not pending but is now pending
- assertThat(facade.onFileUpdated(OLD_ID1,
- FileColumns.MEDIA_TYPE_IMAGE, FileColumns.MEDIA_TYPE_IMAGE,
- /* oldIsTrashed */ false, /* newIsTrashed */ false,
- /* oldIsPending */ false, /* newIsPending */ true)).isTrue();
- assertDeletedMedia(facade, OLD_ID1);
-
- // Was pending but is now neither trashed nor pending
- assertThat(facade.onFileUpdated(OLD_ID1,
- FileColumns.MEDIA_TYPE_IMAGE, FileColumns.MEDIA_TYPE_IMAGE,
- /* oldIsTrashed */ false, /* newIsTrashed */ false,
- /* oldIsPending */ true, /* newIsPending */ false)).isTrue();
- assertDeletedMediaEmpty(facade);
- }
- }
-
- @Test
- public void testDeletedMedia_onDelete() throws Exception {
- try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
- ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper);
-
- assertThat(facade.onFileDeleted(OLD_ID1, FileColumns.MEDIA_TYPE_NONE)).isFalse();
- assertDeletedMediaEmpty(facade);
-
- assertThat(facade.onFileDeleted(OLD_ID1, FileColumns.MEDIA_TYPE_IMAGE)).isTrue();
- assertDeletedMedia(facade, OLD_ID1);
-
- assertThat(facade.onFileDeleted(OLD_ID1, FileColumns.MEDIA_TYPE_NONE)).isFalse();
- assertDeletedMedia(facade, OLD_ID1);
- }
- }
-
- @Test
- public void testQueryMediaGeneration_match() throws Exception {
- try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
- ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper);
-
- // 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 = facade.queryMediaGeneration(/* generation */ 0,
- /* albumId */ null)) {
- assertThat(cursor.getCount()).isEqualTo(2);
-
- cursor.moveToFirst();
- assertMediaColumns(facade, cursor, OLD_ID1, DATE_TAKEN_MS2);
-
- cursor.moveToNext();
- assertMediaColumns(facade, cursor, OLD_ID2, DATE_TAKEN_MS1);
- }
-
- try (Cursor cursor = facade.queryMediaGeneration(GENERATION_MODIFIED1,
- /* albumId */ null)) {
- assertThat(cursor.getCount()).isEqualTo(1);
-
- cursor.moveToFirst();
- assertMediaColumns(facade, cursor, OLD_ID2, DATE_TAKEN_MS1);
- }
- }
- }
-
- @Test
- public void testQueryMediaGeneration_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);
-
- helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cvPending));
- helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cvTrashed));
-
- try (Cursor cursor = facade.queryMediaGeneration(/* generation */ 0,
- /* albumId */ null)) {
- assertThat(cursor.getCount()).isEqualTo(0);
- }
- }
- }
-
- @Test
- public void testQueryMediaGeneration_withDateModified() throws Exception {
- try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
- ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper);
- 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 = facade.queryMediaGeneration(/* generation */ 0,
- /* albumId */ null)) {
- assertThat(cursor.getCount()).isEqualTo(2);
-
- cursor.moveToFirst();
- assertMediaColumns(facade, cursor, OLD_ID1, dateModifiedSeconds2 * 1000);
-
- cursor.moveToNext();
- assertMediaColumns(facade, cursor, OLD_ID2, dateModifiedSeconds1 * 1000);
- }
-
- try (Cursor cursor = facade.queryMediaGeneration(GENERATION_MODIFIED1,
- /* albumId */ null)) {
- assertThat(cursor.getCount()).isEqualTo(1);
-
- cursor.moveToFirst();
- assertMediaColumns(facade, cursor, OLD_ID2, dateModifiedSeconds1 * 1000);
- }
- }
- }
-
- @Test
- public void testQueryMediaId_match() throws Exception {
- try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
- ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper);
-
- ContentValues cv = getContentValues(DATE_TAKEN_MS1, GENERATION_MODIFIED1);
- helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cv));
-
- try (Cursor cursor = facade.queryMediaId(OLD_ID1)) {
- assertThat(cursor.getCount()).isEqualTo(1);
-
- cursor.moveToFirst();
- assertMediaColumns(facade, cursor, OLD_ID1, DATE_TAKEN_MS1);
- }
- }
- }
-
- @Test
- public void testQueryMediaId_noMatch() throws Exception {
- try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
- ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper);
-
- ContentValues cvPending = getContentValues(DATE_TAKEN_MS1, GENERATION_MODIFIED1);
- cvPending.put(MediaColumns.IS_PENDING, 1);
- helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cvPending));
-
- ContentValues cvTrashed = getContentValues(DATE_TAKEN_MS2, GENERATION_MODIFIED2);
- cvTrashed.put(MediaColumns.IS_TRASHED, 1);
- helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cvTrashed));
-
- try (Cursor cursor = facade.queryMediaId(OLD_ID1)) {
- assertThat(cursor.getCount()).isEqualTo(0);
- }
- }
- }
-
- @Test
- public void testQueryMediaId_withDateModified() throws Exception {
- try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
- ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper);
-
- long dateModifiedSeconds = DATE_TAKEN_MS1 / 1000;
- ContentValues cv = new ContentValues();
- cv.put(MediaColumns.SIZE, SIZE);
- cv.put(MediaColumns.DATE_MODIFIED, dateModifiedSeconds);
- cv.put(FileColumns.MIME_TYPE, IMAGE_MIME_TYPE);
- cv.put(FileColumns.MEDIA_TYPE, FileColumns.MEDIA_TYPE_VIDEO);
- cv.put(MediaColumns.DURATION, DURATION_MS);
- cv.put(MediaColumns.GENERATION_MODIFIED, GENERATION_MODIFIED1);
- helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cv));
-
- try (Cursor cursor = facade.queryMediaId(OLD_ID1)) {
- assertThat(cursor.getCount()).isEqualTo(1);
-
- cursor.moveToFirst();
- assertMediaColumns(facade, cursor, OLD_ID1, dateModifiedSeconds * 1000);
- }
- }
- }
-
- @Test
- public void testQueryMediaId_withFavorite() throws Exception {
- try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
- ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper);
-
- ContentValues cv = getContentValues(DATE_TAKEN_MS1, GENERATION_MODIFIED1);
- cv.put(MediaColumns.IS_FAVORITE, 1);
- helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cv));
-
- try (Cursor cursor = facade.queryMediaId(OLD_ID1)) {
- assertThat(cursor.getCount()).isEqualTo(1);
-
- cursor.moveToFirst();
- assertMediaColumns(facade, cursor, OLD_ID1, DATE_TAKEN_MS1, /* isFavorite */ 1);
- }
- }
- }
-
- @Test
- public void testGetMediaInfo() throws Exception {
- try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
- ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper);
-
- 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));
-
- try (Cursor cursor = facade.getMediaInfo(/* generation */ 0)) {
- assertThat(cursor.getCount()).isEqualTo(1);
-
- cursor.moveToFirst();
- assertMediaInfo(facade, cursor, /* count */ 2, /* generation */ 2);
- }
-
- try (Cursor cursor = facade.getMediaInfo(GENERATION_MODIFIED1)) {
- assertThat(cursor.getCount()).isEqualTo(1);
-
- cursor.moveToFirst();
- assertMediaInfo(facade, cursor, /* count */ 1, GENERATION_MODIFIED2);
- }
-
- try (Cursor cursor = facade.getMediaInfo(GENERATION_MODIFIED2)) {
- assertThat(cursor.getCount()).isEqualTo(1);
-
- cursor.moveToFirst();
- assertMediaInfo(facade, cursor, /* count */ 0, /* generation */ 0);
- }
- }
- }
-
- @Test
- public void testQueryAlbumsEmpty() throws Exception {
- try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
- ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper);
-
- ContentValues cv = getContentValues(DATE_TAKEN_MS1, GENERATION_MODIFIED1);
- helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cv));
-
- try (Cursor cursor = facade.queryMediaGeneration(/* generation */ 0,
- /* albumId */ null)) {
- assertThat(cursor.getCount()).isEqualTo(1);
- }
-
- try (Cursor cursor = facade.queryAlbums()) {
- assertThat(cursor.getCount()).isEqualTo(0);
- }
- }
- }
-
- @Test
- public void testQueryAlbums() throws Exception {
- try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
- ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, 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));
-
- // Insert in video ablum
- ContentValues cv4 = getContentValues(DATE_TAKEN_MS4, GENERATION_MODIFIED4);
- cv4.put(FileColumns.MIME_TYPE, VIDEO_MIME_TYPE);
- cv4.put(FileColumns.MEDIA_TYPE, FileColumns.MEDIA_TYPE_VIDEO);
- helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cv4));
-
- // Insert again in video ablum
- ContentValues cv5 = getContentValues(DATE_TAKEN_MS5, GENERATION_MODIFIED5);
- cv5.put(FileColumns.MIME_TYPE, VIDEO_MIME_TYPE);
- cv5.put(FileColumns.MEDIA_TYPE, FileColumns.MEDIA_TYPE_VIDEO);
- helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cv5));
-
- try (Cursor cursor = facade.queryMediaGeneration(/* generation */ 0,
- /* albumId */ null)) {
- assertThat(cursor.getCount()).isEqualTo(5);
- }
-
- try (Cursor cursor = facade.queryAlbums()) {
- assertThat(cursor.getCount()).isEqualTo(4);
-
- // We verify the order of the albums:
- // Camera, Videos, Screenshots and Downloads
- cursor.moveToNext();
- assertAlbumColumns(facade,
- cursor,
- Category.getCategoryName(sIsolatedContext, Category.CATEGORY_CAMERA),
- /* mediaCoverId */ "1",
- DATE_TAKEN_MS1,
- /* count */ 1);
-
- cursor.moveToNext();
- assertAlbumColumns(facade,
- cursor,
- Category.getCategoryName(sIsolatedContext, Category.CATEGORY_VIDEOS),
- /* mediaCoverId */ "5",
- DATE_TAKEN_MS5,
- /* count */ 2);
-
- cursor.moveToNext();
- assertAlbumColumns(facade,
- cursor,
- Category.getCategoryName(sIsolatedContext, Category.CATEGORY_SCREENSHOTS),
- /* mediaCoverId */ "2",
- DATE_TAKEN_MS2,
- /* count */ 1);
-
- cursor.moveToNext();
- assertAlbumColumns(facade,
- cursor,
- Category.getCategoryName(sIsolatedContext, Category.CATEGORY_DOWNLOADS),
- /* mediaCoverId */ "3",
- DATE_TAKEN_MS3,
- /* count */ 1);
- }
- }
- }
-
- 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) {
- int idIndex = cursor.getColumnIndex(CloudMediaProviderContract.MediaColumns.ID);
- int dateTakenIndex = cursor.getColumnIndex(
- CloudMediaProviderContract.MediaColumns.DATE_TAKEN_MS);
- int sizeIndex = cursor.getColumnIndex(CloudMediaProviderContract.MediaColumns.SIZE_BYTES);
- int mimeTypeIndex = cursor.getColumnIndex(
- CloudMediaProviderContract.MediaColumns.MIME_TYPE);
- int durationIndex = cursor.getColumnIndex(
- CloudMediaProviderContract.MediaColumns.DURATION_MS);
- 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(IMAGE_MIME_TYPE);
- assertThat(cursor.getLong(durationIndex)).isEqualTo(DURATION_MS);
- assertThat(cursor.getInt(isFavoriteIndex)).isEqualTo(isFavorite);
- }
-
- private static void assertAlbumColumns(ExternalDbFacade facade, Cursor cursor,
- String displayName, String mediaCoverId, 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_MS);
- int countIndex = cursor.getColumnIndex(CloudMediaProviderContract.AlbumColumns.MEDIA_COUNT);
-
- assertThat(cursor.getString(displayNameIndex)).isEqualTo(displayName);
- assertThat(cursor.getString(idIndex)).isEqualTo(mediaCoverId);
- assertThat(cursor.getLong(dateTakenIndex)).isEqualTo(dateTakenMs);
- assertThat(cursor.getLong(countIndex)).isEqualTo(count);
- }
-
- private static void assertMediaInfo(ExternalDbFacade facade, Cursor cursor,
- long count, long generation) {
- int countIndex = cursor.getColumnIndex(CloudMediaProviderContract.MediaInfo.MEDIA_COUNT);
- int generationIndex = cursor.getColumnIndex(
- CloudMediaProviderContract.MediaInfo.MEDIA_GENERATION);
-
- assertThat(cursor.getLong(countIndex)).isEqualTo(count);
- assertThat(cursor.getLong(generationIndex)).isEqualTo(generation);
- }
-
- 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);
- }
- }
-}
diff --git a/tests/src/com/android/providers/media/photopicker/data/PickerDatabaseHelperTest.java b/tests/src/com/android/providers/media/photopicker/data/PickerDatabaseHelperTest.java
deleted file mode 100644
index 45fc8e7..0000000
--- a/tests/src/com/android/providers/media/photopicker/data/PickerDatabaseHelperTest.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.data;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.Manifest;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-
-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 SQLITE_MASTER_ORDER_BY = "type,name,tbl_name";
- private static final String TEST_PICKER_DB = "test_picker";
- static final String MEDIA_TABLE = "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_DATE_TAKEN_MS = "date_taken_ms";
- 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 long LOCAL_ID = 50;
- private static final long SIZE_BYTES = 7000;
- private static final long DATE_TAKEN_MS = 1623852851911L;
- private static final String CLOUD_ID = "asdfghjkl;";
- private static final String MIME_TYPE = "video/mp4";
- 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 testColumns() throws Exception {
- String[] projection = new String[] {
- KEY_LOCAL_ID,
- KEY_CLOUD_ID,
- KEY_IS_VISIBLE,
- KEY_DATE_TAKEN_MS,
- KEY_SIZE_BYTES,
- KEY_DURATION_MS,
- KEY_MIME_TYPE
- };
-
- 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(SIZE_BYTES);
- assertThat(cr.getLong(5)).isEqualTo(DURATION_MS);
- assertThat(cr.getString(6)).isEqualTo(MIME_TYPE);
- }
- }
- }
- }
-
- @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 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);
- }
- }
-
- @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);
- }
- }
-
- @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);
- }
- }
-
- @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);
- }
- }
-
- 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_DURATION_MS, DURATION_MS);
- values.put(KEY_MIME_TYPE, MIME_TYPE);
- 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
deleted file mode 100644
index 0c19b86..0000000
--- a/tests/src/com/android/providers/media/photopicker/data/PickerDbFacadeTest.java
+++ /dev/null
@@ -1,793 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.data;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.Manifest;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.os.Bundle;
-import android.provider.CloudMediaProviderContract.MediaColumns;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.providers.media.photopicker.data.model.Item;
-import com.android.providers.media.scan.MediaScannerTest.IsolatedContext;
-
-import java.io.File;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@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 DURATION_MS = 5;
- private static final String LOCAL_ID = "50";
- private static final String MEDIA_STORE_URI = "content://media/external/file/" + LOCAL_ID;
- private static final String CLOUD_ID = "asdfghjkl;";
- private static final String MIME_TYPE = "video/mp4";
-
- private static final String LOCAL_PROVIDER = "com.local.provider";
- private static final String CLOUD_PROVIDER = "com.cloud.provider";
-
- private PickerDbFacade mFacade;
-
- @Before
- public void setUp() {
- Context context = InstrumentationRegistry.getTargetContext();
- File dbPath = context.getDatabasePath(PickerDatabaseHelper.PICKER_DATABASE_NAME);
- dbPath.delete();
- mFacade = new PickerDbFacade(context, LOCAL_PROVIDER);
- mFacade.setCloudProvider(CLOUD_PROVIDER);
- }
-
- @Test
- public void testAddLocalOnly() throws Exception {
- Cursor cursor1 = getLocalMediaCursor(LOCAL_ID, DATE_TAKEN_MS + 1);
- Cursor cursor2 = getLocalMediaCursor(LOCAL_ID, DATE_TAKEN_MS + 2);
-
- assertThat(mFacade.addMedia(cursor1, LOCAL_PROVIDER)).isEqualTo(1);
-
- try (Cursor cr = queryMediaAll()) {
- assertThat(cr.getCount()).isEqualTo(1);
- cr.moveToFirst();
- assertCursor(cr, LOCAL_ID, DATE_TAKEN_MS + 1);
- }
-
- // Test updating the same row
- assertThat(mFacade.addMedia(cursor2, LOCAL_PROVIDER)).isEqualTo(1);
-
- try (Cursor cr = queryMediaAll()) {
- assertThat(cr.getCount()).isEqualTo(1);
- cr.moveToFirst();
- assertCursor(cr, LOCAL_ID, DATE_TAKEN_MS + 2);
- }
- }
-
- @Test
- public void testAddCloudPlusLocal() throws Exception {
- Cursor cursor = getCloudMediaCursor(CLOUD_ID, LOCAL_ID, DATE_TAKEN_MS);
-
- assertThat(mFacade.addMedia(cursor, CLOUD_PROVIDER)).isEqualTo(1);
-
- try (Cursor cr = queryMediaAll()) {
- assertThat(cr.getCount()).isEqualTo(1);
- cr.moveToFirst();
- assertCursor(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);
-
- assertThat(mFacade.addMedia(cursor1, CLOUD_PROVIDER)).isEqualTo(1);
-
- try (Cursor cr = queryMediaAll()) {
- assertThat(cr.getCount()).isEqualTo(1);
- cr.moveToFirst();
- assertCursor(cr, CLOUD_ID, DATE_TAKEN_MS + 1);
- }
-
- // Test updating the same row
- assertThat(mFacade.addMedia(cursor2, CLOUD_PROVIDER)).isEqualTo(1);
-
- try (Cursor cr = queryMediaAll()) {
- assertThat(cr.getCount()).isEqualTo(1);
- cr.moveToFirst();
- assertCursor(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);
-
- assertThat(mFacade.addMedia(localCursor, LOCAL_PROVIDER)).isEqualTo(1);
- assertThat(mFacade.addMedia(cloudCursor, CLOUD_PROVIDER)).isEqualTo(1);
-
- try (Cursor cr = queryMediaAll()) {
- assertThat(cr.getCount()).isEqualTo(1);
- cr.moveToFirst();
- assertCursor(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);
-
- assertThat(mFacade.addMedia(cloudCursor, CLOUD_PROVIDER)).isEqualTo(1);
- assertThat(mFacade.addMedia(localCursor, LOCAL_PROVIDER)).isEqualTo(1);
-
- try (Cursor cr = queryMediaAll()) {
- assertThat(cr.getCount()).isEqualTo(1);
- cr.moveToFirst();
- assertCursor(cr, LOCAL_ID, DATE_TAKEN_MS + 1);
- }
- }
-
- @Test
- public void testRemoveLocal() throws Exception {
- Cursor localCursor = getLocalMediaCursor(LOCAL_ID, DATE_TAKEN_MS);
-
- assertThat(mFacade.addMedia(localCursor, LOCAL_PROVIDER)).isEqualTo(1);
-
- try (Cursor cr = queryMediaAll()) {
- assertThat(cr.getCount()).isEqualTo(1);
- }
-
- assertThat(mFacade.removeMedia(getDeletedMediaCursor(LOCAL_ID), 0, LOCAL_PROVIDER))
- .isEqualTo(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);
-
- assertThat(mFacade.addMedia(localCursor, LOCAL_PROVIDER)).isEqualTo(1);
- assertThat(mFacade.addMedia(cloudCursor, CLOUD_PROVIDER)).isEqualTo(1);
-
- try (Cursor cr = queryMediaAll()) {
- assertThat(cr.getCount()).isEqualTo(1);
- cr.moveToFirst();
- assertCursor(cr, LOCAL_ID, DATE_TAKEN_MS);
- }
-
- assertThat(mFacade.removeMedia(getDeletedMediaCursor(LOCAL_ID), 0, LOCAL_PROVIDER))
- .isEqualTo(1);
-
- try (Cursor cr = queryMediaAll()) {
- assertThat(cr.getCount()).isEqualTo(1);
- cr.moveToFirst();
- assertCursor(cr, CLOUD_ID, DATE_TAKEN_MS);
- }
- }
-
- @Test
- public void testRemoveCloud() throws Exception {
- Cursor cloudCursor = getCloudMediaCursor(CLOUD_ID, LOCAL_ID, DATE_TAKEN_MS);
-
- assertThat(mFacade.addMedia(cloudCursor, CLOUD_PROVIDER)).isEqualTo(1);
-
- try (Cursor cr = queryMediaAll()) {
- assertThat(cr.getCount()).isEqualTo(1);
- }
-
- assertThat(mFacade.removeMedia(getDeletedMediaCursor(CLOUD_ID), 0, CLOUD_PROVIDER))
- .isEqualTo(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);
-
- assertThat(mFacade.addMedia(cloudCursor1, CLOUD_PROVIDER)).isEqualTo(1);
- assertThat(mFacade.addMedia(cloudCursor2, CLOUD_PROVIDER)).isEqualTo(1);
-
- try (Cursor cr = queryMediaAll()) {
- assertThat(cr.getCount()).isEqualTo(1);
- cr.moveToFirst();
- assertCursor(cr, CLOUD_ID + "1", DATE_TAKEN_MS + 1);
- }
-
- assertThat(mFacade.removeMedia(getDeletedMediaCursor(CLOUD_ID + "1"), 0, CLOUD_PROVIDER))
- .isEqualTo(1);
-
- try (Cursor cr = queryMediaAll()) {
- assertThat(cr.getCount()).isEqualTo(1);
- cr.moveToFirst();
- assertCursor(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);
-
- assertThat(mFacade.addMedia(cloudCursor, CLOUD_PROVIDER)).isEqualTo(1);
- assertThat(mFacade.addMedia(localCursor, LOCAL_PROVIDER)).isEqualTo(1);
-
- try (Cursor cr = queryMediaAll()) {
- assertThat(cr.getCount()).isEqualTo(1);
- cr.moveToFirst();
- assertCursor(cr, LOCAL_ID, DATE_TAKEN_MS);
- }
-
- assertThat(mFacade.removeMedia(getDeletedMediaCursor(CLOUD_ID), 0, CLOUD_PROVIDER))
- .isEqualTo(1);
-
- try (Cursor cr = queryMediaAll()) {
- assertThat(cr.getCount()).isEqualTo(1);
- cr.moveToFirst();
- assertCursor(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);
-
- assertThat(mFacade.addMedia(localCursor1, LOCAL_PROVIDER)).isEqualTo(1);
- assertThat(mFacade.addMedia(localCursor2, LOCAL_PROVIDER)).isEqualTo(1);
-
- try (Cursor cr = queryMediaAll()) {
- assertThat(cr.getCount()).isEqualTo(1);
- cr.moveToFirst();
- assertCursor(cr, LOCAL_ID, DATE_TAKEN_MS + 2);
- }
-
- assertThat(mFacade.removeMedia(getDeletedMediaCursor(LOCAL_ID), 0, LOCAL_PROVIDER))
- .isEqualTo(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);
-
- assertThat(mFacade.addMedia(cloudCursor1, CLOUD_PROVIDER)).isEqualTo(1);
- assertThat(mFacade.addMedia(cloudCursor2, CLOUD_PROVIDER)).isEqualTo(1);
-
- try (Cursor cr = queryMediaAll()) {
- assertThat(cr.getCount()).isEqualTo(1);
- cr.moveToFirst();
- assertCursor(cr, CLOUD_ID, DATE_TAKEN_MS + 2);
- }
-
- assertThat(mFacade.removeMedia(getDeletedMediaCursor(CLOUD_ID), 0, CLOUD_PROVIDER))
- .isEqualTo(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);
-
- assertThat(mFacade.addMedia(localCursor, LOCAL_PROVIDER)).isEqualTo(1);
- assertThat(mFacade.addMedia(cloudCursor1, CLOUD_PROVIDER)).isEqualTo(1);
- assertThat(mFacade.addMedia(cloudCursor2, CLOUD_PROVIDER)).isEqualTo(1);
-
- try (Cursor cr = queryMediaAll()) {
- assertThat(cr.getCount()).isEqualTo(1);
- cr.moveToFirst();
- assertCursor(cr, LOCAL_ID, DATE_TAKEN_MS);
- }
-
- assertThat(mFacade.removeMedia(getDeletedMediaCursor(LOCAL_ID), 0, LOCAL_PROVIDER))
- .isEqualTo(1);
-
- try (Cursor cr = queryMediaAll()) {
- assertThat(cr.getCount()).isEqualTo(1);
- cr.moveToFirst();
- assertCursor(cr, CLOUD_ID, DATE_TAKEN_MS + 2);
- }
-
- assertThat(mFacade.removeMedia(getDeletedMediaCursor(CLOUD_ID), 0, CLOUD_PROVIDER))
- .isEqualTo(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);
-
- assertThat(mFacade.addMedia(localCursor, LOCAL_PROVIDER)).isEqualTo(1);
- assertThat(mFacade.addMedia(cloudCursor1, CLOUD_PROVIDER)).isEqualTo(1);
- assertThat(mFacade.addMedia(cloudCursor2, CLOUD_PROVIDER)).isEqualTo(1);
-
- try (Cursor cr = queryMediaAll()) {
- assertThat(cr.getCount()).isEqualTo(1);
- cr.moveToFirst();
- assertCursor(cr, LOCAL_ID, DATE_TAKEN_MS);
- }
-
- assertThat(mFacade.resetMedia(LOCAL_PROVIDER)).isEqualTo(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);
-
- assertThat(mFacade.addMedia(localCursor, LOCAL_PROVIDER)).isEqualTo(1);
- assertThat(mFacade.addMedia(cloudCursor, CLOUD_PROVIDER)).isEqualTo(1);
-
- try (Cursor cr = queryMediaAll()) {
- assertThat(cr.getCount()).isEqualTo(1);
- cr.moveToFirst();
- assertCursor(cr, LOCAL_ID, DATE_TAKEN_MS);
- }
-
- assertThat(mFacade.resetMedia(CLOUD_PROVIDER)).isEqualTo(1);
-
- try (Cursor cr = queryMediaAll()) {
- assertThat(cr.getCount()).isEqualTo(1);
- cr.moveToFirst();
- assertCursor(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);
-
- assertThat(mFacade.addMedia(localCursor, LOCAL_PROVIDER)).isEqualTo(1);
- assertThat(mFacade.addMedia(cloudCursor, CLOUD_PROVIDER)).isEqualTo(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.queryMedia(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.queryMedia(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);
-
- assertThat(mFacade.addMedia(cursor1, LOCAL_PROVIDER)).isEqualTo(1);
- assertThat(mFacade.addMedia(cursor2, LOCAL_PROVIDER)).isEqualTo(1);
-
- PickerDbFacade.QueryFilterBuilder qfbBefore = new PickerDbFacade.QueryFilterBuilder(5);
- qfbBefore.setDateTakenBeforeMs(DATE_TAKEN_MS);
- qfbBefore.setId(2);
- try (Cursor cr = mFacade.queryMedia(qfbBefore.build())) {
- assertThat(cr.getCount()).isEqualTo(1);
-
- cr.moveToFirst();
- assertCursor(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.queryMedia(qfbAfter.build())) {
- assertThat(cr.getCount()).isEqualTo(1);
-
- cr.moveToFirst();
- assertCursor(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);
-
- assertThat(mFacade.addMedia(cursor1, LOCAL_PROVIDER)).isEqualTo(1);
- assertThat(mFacade.addMedia(cursor2, CLOUD_PROVIDER)).isEqualTo(1);
- assertThat(mFacade.addMedia(cursor3, LOCAL_PROVIDER)).isEqualTo(1);
-
- PickerDbFacade.QueryFilterBuilder qfbBefore = new PickerDbFacade.QueryFilterBuilder(1);
- qfbBefore.setDateTakenBeforeMs(DATE_TAKEN_MS + 1);
- qfbBefore.setId(0);
- try (Cursor cr = mFacade.queryMedia(qfbBefore.build())) {
- assertThat(cr.getCount()).isEqualTo(1);
-
- cr.moveToFirst();
- assertCursor(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.queryMedia(qfbAfter.build())) {
- assertThat(cr.getCount()).isEqualTo(1);
-
- cr.moveToFirst();
- assertCursor(cr, LOCAL_ID + "3", DATE_TAKEN_MS);
- }
-
- try (Cursor cr = mFacade.queryMedia(new PickerDbFacade.QueryFilterBuilder(1).build())) {
- assertThat(cr.getCount()).isEqualTo(1);
-
- cr.moveToFirst();
- assertCursor(cr, LOCAL_ID + "3", DATE_TAKEN_MS);
- }
- }
-
- @Test
- public void testQueryWithSizeFilter() throws Exception {
- Cursor cursor1 = getMediaCursor(LOCAL_ID, DATE_TAKEN_MS,
- /* mediaStoreUri */ null, /* sizeBytes */ 1, MIME_TYPE, /* isFavorite */ false);
- Cursor cursor2 = getMediaCursor(CLOUD_ID, DATE_TAKEN_MS,
- /* mediaStoreUri */ null, /* sizeBytes */ 2, MIME_TYPE, /* isFavorite */ false);
-
- assertThat(mFacade.addMedia(cursor1, LOCAL_PROVIDER)).isEqualTo(1);
- assertThat(mFacade.addMedia(cursor2, CLOUD_PROVIDER)).isEqualTo(1);
-
- // Verify all
- PickerDbFacade.QueryFilterBuilder qfbAll = new PickerDbFacade.QueryFilterBuilder(1000);
- qfbAll.setSizeBytes(10);
- try (Cursor cr = mFacade.queryMedia(qfbAll.build())) {
- assertThat(cr.getCount()).isEqualTo(2);
- }
-
- qfbAll.setSizeBytes(1);
- try (Cursor cr = mFacade.queryMedia(qfbAll.build())) {
- assertThat(cr.getCount()).isEqualTo(1);
-
- cr.moveToFirst();
- assertCursor(cr, LOCAL_ID);
- }
-
- // 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.queryMedia(qfbAfter.build())) {
- assertThat(cr.getCount()).isEqualTo(2);
- }
-
- qfbAfter.setDateTakenAfterMs(DATE_TAKEN_MS - 1);
- qfbAfter.setId(0);
- qfbAfter.setSizeBytes(1);
- try (Cursor cr = mFacade.queryMedia(qfbAfter.build())) {
- assertThat(cr.getCount()).isEqualTo(1);
-
- cr.moveToFirst();
- assertCursor(cr, LOCAL_ID);
- }
-
- // 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.queryMedia(qfbBefore.build())) {
- assertThat(cr.getCount()).isEqualTo(2);
- }
-
- qfbBefore.setDateTakenBeforeMs(DATE_TAKEN_MS + 1);
- qfbBefore.setId(0);
- qfbBefore.setSizeBytes(1);
- try (Cursor cr = mFacade.queryMedia(qfbBefore.build())) {
- assertThat(cr.getCount()).isEqualTo(1);
-
- cr.moveToFirst();
- assertCursor(cr, LOCAL_ID);
- }
- }
-
- @Test
- public void testQueryWithMimeTypeFilter() throws Exception {
- Cursor cursor1 = getMediaCursor(LOCAL_ID, DATE_TAKEN_MS,
- /* mediaStoreUri */ null, SIZE_BYTES, "video/webm", /* isFavorite */ false);
- Cursor cursor2 = getMediaCursor(CLOUD_ID, DATE_TAKEN_MS,
- /* mediaStoreUri */ null, SIZE_BYTES, "video/mp4", /* isFavorite */ false);
-
- assertThat(mFacade.addMedia(cursor1, LOCAL_PROVIDER)).isEqualTo(1);
- assertThat(mFacade.addMedia(cursor2, CLOUD_PROVIDER)).isEqualTo(1);
-
- // Verify all
- PickerDbFacade.QueryFilterBuilder qfbAll = new PickerDbFacade.QueryFilterBuilder(1000);
- qfbAll.setMimeType("*/*");
- try (Cursor cr = mFacade.queryMedia(qfbAll.build())) {
- assertThat(cr.getCount()).isEqualTo(2);
- }
-
- qfbAll.setMimeType("video/mp4");
- try (Cursor cr = mFacade.queryMedia(qfbAll.build())) {
- assertThat(cr.getCount()).isEqualTo(1);
-
- cr.moveToFirst();
- assertCursor(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.queryMedia(qfbAfter.build())) {
- assertThat(cr.getCount()).isEqualTo(2);
- }
-
- qfbAfter.setDateTakenAfterMs(DATE_TAKEN_MS - 1);
- qfbAfter.setId(0);
- qfbAfter.setMimeType("video/webm");
- try (Cursor cr = mFacade.queryMedia(qfbAfter.build())) {
- assertThat(cr.getCount()).isEqualTo(1);
-
- cr.moveToFirst();
- assertCursor(cr, LOCAL_ID);
- }
-
- // 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.queryMedia(qfbBefore.build())) {
- assertThat(cr.getCount()).isEqualTo(2);
- }
-
- qfbBefore.setDateTakenBeforeMs(DATE_TAKEN_MS + 1);
- qfbBefore.setId(0);
- qfbBefore.setMimeType("video/mp4");
- try (Cursor cr = mFacade.queryMedia(qfbBefore.build())) {
- assertThat(cr.getCount()).isEqualTo(1);
-
- cr.moveToFirst();
- assertCursor(cr, CLOUD_ID, DATE_TAKEN_MS);
- }
- }
-
- @Test
- public void testQueryWithSizeAndMimeTypeFilter() throws Exception {
- Cursor cursor1 = getMediaCursor(LOCAL_ID, DATE_TAKEN_MS,
- /* mediaStoreUri */ null, /* sizeBytes */ 2, "video/webm", /* isFavorite */ false);
- Cursor cursor2 = getMediaCursor(CLOUD_ID, DATE_TAKEN_MS,
- /* mediaStoreUri */ null, /* sizeBytes */ 1, "video/mp4", /* isFavorite */ false);
-
- assertThat(mFacade.addMedia(cursor1, LOCAL_PROVIDER)).isEqualTo(1);
- assertThat(mFacade.addMedia(cursor2, CLOUD_PROVIDER)).isEqualTo(1);
-
- // mime_type and size filter matches all
- PickerDbFacade.QueryFilterBuilder qfbAll = new PickerDbFacade.QueryFilterBuilder(1000);
- qfbAll.setMimeType("*/*");
- qfbAll.setSizeBytes(10);
- try (Cursor cr = mFacade.queryMedia(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.queryMedia(qfbAll.build())) {
- assertThat(cr.getCount()).isEqualTo(0);
- }
- }
-
- @Test
- public void testSetCloudProvider() throws Exception {
- Cursor localCursor = getLocalMediaCursor(LOCAL_ID, DATE_TAKEN_MS);
- Cursor cloudCursor = getCloudMediaCursor(CLOUD_ID, null, DATE_TAKEN_MS);
-
- assertThat(mFacade.addMedia(localCursor, LOCAL_PROVIDER)).isEqualTo(1);
- assertThat(mFacade.addMedia(cloudCursor, CLOUD_PROVIDER)).isEqualTo(1);
-
- try (Cursor cr = queryMediaAll()) {
- assertThat(cr.getCount()).isEqualTo(2);
-
- cr.moveToFirst();
- assertCursor(cr, CLOUD_ID, DATE_TAKEN_MS);
-
- cr.moveToNext();
- assertCursor(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();
- assertCursor(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();
- assertCursor(cr, CLOUD_ID, DATE_TAKEN_MS);
-
- cr.moveToNext();
- assertCursor(cr, LOCAL_ID, DATE_TAKEN_MS);
- }
- }
-
- @Test
- public void testFavourites() throws Exception {
- Cursor localCursor1 = getMediaCursor(LOCAL_ID + "1", DATE_TAKEN_MS,
- /* mediaStoreUri */ null, SIZE_BYTES, MIME_TYPE, /* isFavorite */ true);
- Cursor localCursor2 = getMediaCursor(LOCAL_ID + "2", DATE_TAKEN_MS,
- /* mediaStoreUri */ null, SIZE_BYTES, MIME_TYPE, /* isFavorite */ false);
- Cursor cloudCursor1 = getMediaCursor(CLOUD_ID + "1", DATE_TAKEN_MS,
- /* mediaStoreUri */ null, SIZE_BYTES, MIME_TYPE, /* isFavorite */ true);
- Cursor cloudCursor2 = getMediaCursor(CLOUD_ID + "2", DATE_TAKEN_MS,
- /* mediaStoreUri */ null, SIZE_BYTES, MIME_TYPE, /* isFavorite */ false);
-
- assertThat(mFacade.addMedia(localCursor1, LOCAL_PROVIDER)).isEqualTo(1);
- assertThat(mFacade.addMedia(localCursor2, LOCAL_PROVIDER)).isEqualTo(1);
- assertThat(mFacade.addMedia(cloudCursor1, CLOUD_PROVIDER)).isEqualTo(1);
- assertThat(mFacade.addMedia(cloudCursor2, CLOUD_PROVIDER)).isEqualTo(1);
-
- PickerDbFacade.QueryFilterBuilder qfb =
- new PickerDbFacade.QueryFilterBuilder(/* limit */ 1000);
- try (Cursor cr = mFacade.queryMedia(qfb.build())) {
- assertThat(cr.getCount()).isEqualTo(4);
- }
-
- qfb.setIsFavorite(true);
- try (Cursor cr = mFacade.queryMedia(qfb.build())) {
- assertThat(cr.getCount()).isEqualTo(2);
- cr.moveToFirst();
- assertCursor(cr, CLOUD_ID + 1, DATE_TAKEN_MS);
-
- cr.moveToNext();
- assertCursor(cr, LOCAL_ID + 1, DATE_TAKEN_MS);
- }
- }
-
- private Cursor queryMediaAll() {
- return mFacade.queryMedia(new PickerDbFacade.QueryFilterBuilder(1000).build());
- }
-
- // 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, String mediaStoreUri,
- long sizeBytes, String mimeType, boolean isFavorite) {
- String[] projectionKey = new String[] {
- MediaColumns.ID,
- MediaColumns.MEDIA_STORE_URI,
- MediaColumns.DATE_TAKEN_MS,
- MediaColumns.SIZE_BYTES,
- MediaColumns.MIME_TYPE,
- MediaColumns.DURATION_MS,
- MediaColumns.IS_FAVORITE
- };
-
- String[] projectionValue = new String[] {
- id,
- mediaStoreUri,
- String.valueOf(dateTakenMs),
- String.valueOf(sizeBytes),
- mimeType,
- String.valueOf(DURATION_MS),
- String.valueOf(isFavorite ? 1 : 0)
- };
-
- MatrixCursor c = new MatrixCursor(projectionKey);
- c.addRow(projectionValue);
- return c;
- }
-
- private static Cursor getLocalMediaCursor(String localId, long dateTakenMs) {
- return getMediaCursor(localId, dateTakenMs, toMediaStoreUri(localId), SIZE_BYTES,
- MIME_TYPE, /* isFavorite */ false);
- }
-
- private static Cursor getCloudMediaCursor(String cloudId, String localId,
- long dateTakenMs) {
- return getMediaCursor(cloudId, dateTakenMs, toMediaStoreUri(localId), SIZE_BYTES,
- MIME_TYPE, /* isFavorite */ false);
- }
-
- private static String toMediaStoreUri(String localId) {
- if (localId == null) {
- return null;
- }
- return "content://media/external/file/" + localId;
- }
-
- private static void assertCursor(Cursor cursor, String id) {
- assertThat(cursor.getString(cursor.getColumnIndex(Item.ItemColumns.ID)))
- .isEqualTo(id);
- assertThat(cursor.getString(cursor.getColumnIndex(Item.ItemColumns.AUTHORITY)))
- .isEqualTo(id.startsWith(LOCAL_ID) ? LOCAL_PROVIDER : CLOUD_PROVIDER);
- }
-
- private static void assertCursor(Cursor cursor, String id, long dateTakenMs) {
- assertCursor(cursor, id);
- assertThat(cursor.getString(cursor.getColumnIndex(Item.ItemColumns.MIME_TYPE)))
- .isEqualTo(MIME_TYPE);
- assertThat(cursor.getLong(cursor.getColumnIndex(Item.ItemColumns.DATE_TAKEN)))
- .isEqualTo(dateTakenMs);
- assertThat(cursor.getLong(cursor.getColumnIndex(Item.ItemColumns.SIZE)))
- .isEqualTo(SIZE_BYTES);
- assertThat(cursor.getLong(cursor.getColumnIndex(Item.ItemColumns.DURATION)))
- .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
deleted file mode 100644
index ecf46cc..0000000
--- a/tests/src/com/android/providers/media/photopicker/data/PickerResultTest.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.data;
-
-import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.content.ClipData;
-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.data.model.Item;
-import com.android.providers.media.photopicker.data.model.ItemTest;
-import com.android.providers.media.photopicker.data.model.UserId;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.File;
-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(Context, List)} with single item
- * @throws Exception
- */
- @Test
- public void testGetResultSingle() throws Exception {
- List<Item> items = null;
- try {
- items = createItemSelection(1);
- final Intent intent = PickerResult.getPickerResponseIntent(mContext, items);
-
- final Uri result = intent.getData();
- assertPickerUri(result);
- assertThat(mContext.getContentResolver().getType(result)).isEqualTo("image/jpeg");
- } finally {
- deleteFiles(items);
- }
- }
-
- /**
- * Tests {@link PickerResult#getPickerResponseIntent(Context, List)} with multiple items
- * @throws Exception
- */
- @Test
- public void testGetResultMultiple() throws Exception {
- ArrayList<Item> items = null;
- try {
- final int itemCount = 3;
- items = createItemSelection(itemCount);
- final Intent intent = PickerResult.getPickerResponseIntent(mContext, items);
-
- final ClipData clipData = intent.getClipData();
- final int count = clipData.getItemCount();
- assertThat(count).isEqualTo(itemCount);
- for (int i = 0; i < count; i++) {
- assertPickerUri(clipData.getItemAt(i).getUri());
- }
- } finally {
- deleteFiles(items);
- }
- }
-
- private void assertPickerUri(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
- final Uri imageUri = assertCreateNewImage();
- clearMediaOwner(imageUri, mContext.getUserId());
-
- // Create an item for the selection, since PickerResult only uses Item#getContentUri(),
- // no need to create actual item, and can mock the class.
- final Item imageItem = mock(Item.class);
- when(imageItem.getContentUri()).thenReturn(imageUri);
- when(imageItem.getId()).thenReturn(imageUri.getLastPathSegment());
-
- return imageItem;
- }
-
- 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();
- return MediaStore.scanFile(mContext.getContentResolver(), file);
- }
-
- 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/UserIdManagerTest.java b/tests/src/com/android/providers/media/photopicker/data/UserIdManagerTest.java
deleted file mode 100644
index 93d3cc8..0000000
--- a/tests/src/com/android/providers/media/photopicker/data/UserIdManagerTest.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.data;
-
-import static com.google.common.truth.Truth.assertThat;
-
-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 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);
- }
-
- // 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));
- 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));
- 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));
- 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));
- 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));
- 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));
- 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));
- 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);
- 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
deleted file mode 100644
index 80132a8..0000000
--- a/tests/src/com/android/providers/media/photopicker/data/model/CategoryTest.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.data.model;
-
-import static com.android.providers.media.photopicker.data.model.Category.CATEGORY_CAMERA;
-import static com.android.providers.media.photopicker.data.model.Category.CATEGORY_DEFAULT;
-import static com.android.providers.media.photopicker.data.model.Category.CATEGORY_DOWNLOADS;
-import static com.android.providers.media.photopicker.data.model.Category.CATEGORY_FAVORITES;
-import static com.android.providers.media.photopicker.data.model.Category.CATEGORY_SCREENSHOTS;
-import static com.android.providers.media.photopicker.data.model.Category.CATEGORY_VIDEOS;
-import static com.android.providers.media.photopicker.data.model.Category.CategoryColumns;
-import static com.android.providers.media.photopicker.data.model.Category.CategoryType;
-
-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 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 int itemCount = 10;
- final String categoryName = "Album";
- final String coverId = "52";
- final String categoryType = CATEGORY_SCREENSHOTS;
- final Cursor cursor = generateCursorForCategory(categoryName, coverId, itemCount,
- categoryType);
- cursor.moveToFirst();
-
- final Category category = new Category(cursor, UserId.CURRENT_USER);
-
- assertThat(category.getCategoryName(/* context= */ null)).isEqualTo(categoryName);
- assertThat(category.getItemCount()).isEqualTo(itemCount);
- assertThat(category.getCoverUri()).isEqualTo(ItemsProvider.getItemsUri(coverId,
- /* authority */ null, UserId.CURRENT_USER));
- assertThat(category.getCategoryType()).isEqualTo(categoryType);
- }
-
- /**
- * If the {@code category} is not in {@link Category#CATEGORIES}, return {@code null}.
- */
- @Test
- public void testGetCategoryName_notInList_returnNull() {
- final Context context = InstrumentationRegistry.getTargetContext();
- final String categoryName = Category.getCategoryName(context, CATEGORY_DEFAULT);
-
- assertThat(categoryName).isNull();
- }
-
- @Test
- public void testGetCategoryName_camera() {
- final Context context = InstrumentationRegistry.getTargetContext();
- final String categoryName = Category.getCategoryName(context, CATEGORY_CAMERA);
-
- assertThat(categoryName).isEqualTo("Camera");
- }
-
- @Test
- public void testGetCategoryName_downloads() {
- final Context context = InstrumentationRegistry.getTargetContext();
- final String categoryName = Category.getCategoryName(context, CATEGORY_DOWNLOADS);
-
- assertThat(categoryName).isEqualTo("Downloads");
- }
-
- @Test
- public void testGetCategoryName_favorites() {
- final Context context = InstrumentationRegistry.getTargetContext();
- final String categoryName = Category.getCategoryName(context, CATEGORY_FAVORITES);
-
- assertThat(categoryName).isEqualTo("Favorites");
- }
-
- @Test
- public void testGetCategoryName_screenshots() {
- final Context context = InstrumentationRegistry.getTargetContext();
- final String categoryName = Category.getCategoryName(context, CATEGORY_SCREENSHOTS);
-
- assertThat(categoryName).isEqualTo("Screenshots");
- }
-
- @Test
- public void testGetCategoryName_videos() {
- final Context context = InstrumentationRegistry.getTargetContext();
- final String categoryName = Category.getCategoryName(context, CATEGORY_VIDEOS);
-
- assertThat(categoryName).isEqualTo("Videos");
- }
-
- @Test
- public void testGetDefaultCategory() {
- final Category category = Category.getDefaultCategory();
-
- assertThat(category.getCategoryType()).isEqualTo(CATEGORY_DEFAULT);
- }
-
- @Test
- public void testOrderOfCategories() {
- final List<String> categoryList = Category.CATEGORIES_LIST;
-
- // Favorites, Camera, Videos, ScreenShots, Downloads
- assertThat(categoryList.get(0)).isEqualTo(CATEGORY_FAVORITES);
- assertThat(categoryList.get(1)).isEqualTo(CATEGORY_CAMERA);
- assertThat(categoryList.get(2)).isEqualTo(CATEGORY_VIDEOS);
- assertThat(categoryList.get(3)).isEqualTo(CATEGORY_SCREENSHOTS);
- assertThat(categoryList.get(4)).isEqualTo(CATEGORY_DOWNLOADS);
- }
-
- private static Cursor generateCursorForCategory(String categoryName, String coverId,
- int itemCount, @CategoryType String categoryType) {
- final MatrixCursor cursor = new MatrixCursor(CategoryColumns.getAllColumns());
- cursor.addRow(new Object[] {categoryName, coverId, itemCount, categoryType});
- 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
deleted file mode 100644
index 230afdd..0000000
--- a/tests/src/com/android/providers/media/photopicker/data/model/ItemTest.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.data.model;
-
-import static com.android.providers.media.photopicker.data.model.Item.ItemColumns;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.net.Uri;
-import android.os.UserHandle;
-import android.provider.MediaStore;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class ItemTest {
-
- @Test
- public void testConstructor() {
- final String id = "1";
- final long dateTaken = 12345678l;
- final String mimeType = "image/png";
- final long duration = 1000;
- final Cursor cursor = generateCursorForItem(id, mimeType, dateTaken, duration);
- cursor.moveToFirst();
-
- final Item item = new Item(cursor, UserId.CURRENT_USER);
-
- assertThat(item.getId()).isEqualTo(id);
- assertThat(item.getDateTaken()).isEqualTo(dateTaken);
- assertThat(item.getMimeType()).isEqualTo(mimeType);
- assertThat(item.getDuration()).isEqualTo(duration);
- assertThat(item.getContentUri()).isEqualTo(Uri.parse("content://media/external/file/1"));
-
- assertThat(item.isDate()).isFalse();
- assertThat(item.isImage()).isTrue();
- assertThat(item.isVideo()).isFalse();
- assertThat(item.isGif()).isFalse();
- }
-
- @Test
- public void testConstructor_differentUser() {
- final String id = "1";
- final long dateTaken = 12345678l;
- final String mimeType = "image/png";
- final long duration = 1000;
- final Cursor cursor = generateCursorForItem(id, mimeType, dateTaken, duration);
- 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.getMimeType()).isEqualTo(mimeType);
- assertThat(item.getDuration()).isEqualTo(duration);
- assertThat(item.getContentUri()).isEqualTo(Uri.parse("content://10@media/external/file/1"));
-
- assertThat(item.isDate()).isFalse();
- assertThat(item.isImage()).isTrue();
- assertThat(item.isVideo()).isFalse();
- assertThat(item.isGif()).isFalse();
- }
-
- @Test
- public void testIsImage() {
- final String id = "1";
- final long dateTaken = 12345678l;
- final String mimeType = "image/png";
- final long duration = 1000;
- final Item item = generateItem(id, mimeType, dateTaken, duration);
-
- assertThat(item.isImage()).isTrue();
- assertThat(item.isDate()).isFalse();
- assertThat(item.isVideo()).isFalse();
- assertThat(item.isGif()).isFalse();
- }
-
- @Test
- public void testIsVideo() {
- final String id = "1";
- final long dateTaken = 12345678l;
- final String mimeType = "video/mpeg";
- final long duration = 1000;
- final Item item = generateItem(id, mimeType, dateTaken, duration);
-
- assertThat(item.isVideo()).isTrue();
- assertThat(item.isDate()).isFalse();
- assertThat(item.isImage()).isFalse();
- assertThat(item.isGif()).isFalse();
- }
-
- @Test
- public void testIsGif() {
- final String id = "1";
- final long dateTaken = 12345678l;
- final String mimeType = "image/gif";
- final long duration = 1000;
- final Item item = generateItem(id, mimeType, dateTaken, duration);
-
- assertThat(item.isGif()).isTrue();
- assertThat(item.isDate()).isFalse();
- assertThat(item.isImage()).isFalse();
- assertThat(item.isVideo()).isFalse();
- }
-
- @Test
- public void testCreateDateItem() {
- final long dateTaken = 12345678l;
-
- final Item item = Item.createDateItem(dateTaken);
-
- assertThat(item.getDateTaken()).isEqualTo(dateTaken);
- assertThat(item.isDate()).isTrue();
- }
-
- private static Cursor generateCursorForItem(String id, String mimeType, long dateTaken,
- long duration) {
- final MatrixCursor cursor = new MatrixCursor(ItemColumns.ALL_COLUMNS);
- cursor.addRow(new Object[] {id, mimeType, dateTaken, /* dateModified */ dateTaken,
- duration});
- return cursor;
- }
-
- /**
- * Generate the {@link Item}
- * @param id the id
- * @param mimeType the mime type
- * @param dateTaken the time of date taken
- * @param duration the duration
- * @return the Item
- */
- public static Item generateItem(String id, String mimeType, long dateTaken, long duration) {
- return new Item(id, mimeType, dateTaken, duration,
- MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL, Long.parseLong(id)));
- }
-}
diff --git a/tests/src/com/android/providers/media/photopicker/util/DateTimeUtilsTest.java b/tests/src/com/android/providers/media/photopicker/util/DateTimeUtilsTest.java
deleted file mode 100644
index e87c88b..0000000
--- a/tests/src/com/android/providers/media/photopicker/util/DateTimeUtilsTest.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.util;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.time.LocalDate;
-import java.time.ZoneId;
-import java.time.ZoneOffset;
-
-@RunWith(AndroidJUnit4.class)
-public class DateTimeUtilsTest {
-
- private Context mContext;
- 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();
-
- @Before
- public void setUp() {
- mContext = InstrumentationRegistry.getTargetContext();
- }
-
- @Test
- public void testGetDateTimeString_today() throws Exception {
- final long when = generateDateTimeMillis(FAKE_DATE);
-
- final String result = DateTimeUtils.getDateTimeString(mContext, when, FAKE_DATE);
-
- assertThat(result).isEqualTo(DateTimeUtils.getTodayString());
- }
-
- @Test
- public void testGetDateTimeString_yesterday() throws Exception {
- final LocalDate whenDate = FAKE_DATE.minusDays(1);
- final long when = generateDateTimeMillis(whenDate);
-
- final String result = DateTimeUtils.getDateTimeString(mContext, when, FAKE_DATE);
-
- assertThat(result).isEqualTo(DateTimeUtils.getYesterdayString());
- }
-
- @Test
- public void testGetDateTimeString_weekday() throws Exception {
- final LocalDate whenDate = FAKE_DATE.minusDays(3);
- final long when = generateDateTimeMillis(whenDate);
-
- final String result = DateTimeUtils.getDateTimeString(mContext, when, FAKE_DATE);
-
- assertThat(result).isEqualTo("Saturday");
- }
-
- @Test
- public void testGetDateTimeString_weekdayAndDate() throws Exception {
- final LocalDate whenDate = FAKE_DATE.minusMonths(1);
- final long when = generateDateTimeMillis(whenDate);
-
- final String result = DateTimeUtils.getDateTimeString(mContext, when, FAKE_DATE);
-
- assertThat(result).isEqualTo("Sun, Jun 7");
- }
-
- @Test
- public void testGetDateTimeString_weekdayDateAndYear() throws Exception {
- final LocalDate whenDate = FAKE_DATE.minusYears(1);
- long when = generateDateTimeMillis(whenDate);
-
- final String result = DateTimeUtils.getDateTimeString(mContext, when, FAKE_DATE);
-
- assertThat(result).isEqualTo("Sun, Jul 7, 2019");
- }
-
- @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();
- }
-}
-
diff --git a/tests/src/com/android/providers/media/photopicker/viewmodel/InstantTaskExecutorRule.java b/tests/src/com/android/providers/media/photopicker/viewmodel/InstantTaskExecutorRule.java
deleted file mode 100644
index b35075e..0000000
--- a/tests/src/com/android/providers/media/photopicker/viewmodel/InstantTaskExecutorRule.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.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
deleted file mode 100644
index 8d76e57..0000000
--- a/tests/src/com/android/providers/media/photopicker/viewmodel/PickerViewModelTest.java
+++ /dev/null
@@ -1,547 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.viewmodel;
-
-import static com.android.providers.media.photopicker.data.model.Category.CATEGORY_DOWNLOADS;
-
-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.database.Cursor;
-import android.database.MatrixCursor;
-import android.net.Uri;
-import android.provider.MediaStore;
-import android.text.format.DateUtils;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-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.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;
-import java.util.Map;
-
-@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";
-
- @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);
- mPickerViewModel = new PickerViewModel(mApplication);
- mItemsProvider = new TestItemsProvider(context);
- mPickerViewModel.setItemsProvider(mItemsProvider);
- }
-
- @Test
- public void testAddSelectedItem() throws Exception {
- final String id = "1";
- final Item item = generateFakeImageItem(id);
-
- mPickerViewModel.addSelectedItem(item);
-
- final Item selectedItem = mPickerViewModel.getSelectedItems().getValue().get(
- item.getContentUri());
-
- 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() throws Exception {
- final String id = "1";
- final Item item = generateFakeImageItem(id);
- Map<Uri, Item> selectedItems = mPickerViewModel.getSelectedItems().getValue();
-
- assertThat(selectedItems.size()).isEqualTo(0);
-
- mPickerViewModel.addSelectedItem(item);
-
- selectedItems = mPickerViewModel.getSelectedItems().getValue();
- assertThat(selectedItems.size()).isEqualTo(1);
-
- mPickerViewModel.deleteSelectedItem(item);
-
- selectedItems = mPickerViewModel.getSelectedItems().getValue();
- assertThat(selectedItems.size()).isEqualTo(0);
- }
-
- @Test
- public void testClearSelectedItem() throws Exception {
- final String id1 = "1";
- final Item item1 = generateFakeImageItem(id1);
- final String id2 = "2";
- final Item item2 = generateFakeImageItem(id2);
- Map<Uri, Item> selectedItems = mPickerViewModel.getSelectedItems().getValue();
-
- assertThat(selectedItems.size()).isEqualTo(0);
-
- mPickerViewModel.addSelectedItem(item1);
- mPickerViewModel.addSelectedItem(item2);
-
- selectedItems = mPickerViewModel.getSelectedItems().getValue();
- assertThat(selectedItems.size()).isEqualTo(2);
-
- mPickerViewModel.clearSelectedItems();
-
- selectedItems = mPickerViewModel.getSelectedItems().getValue();
- assertThat(selectedItems.size()).isEqualTo(0);
- }
-
- @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 Item lastItem = ItemTest.generateItem(lastItemId, FAKE_IMAGE_MIME_TYPE,
- dateTakenMs, /* 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;
- mItemsProvider.setItems(generateFakeImageItemList(itemCount));
- mPickerViewModel.updateCategoryItems(CATEGORY_DOWNLOADS);
- // We use ForegroundThread to execute the loadItems in updateCategoryItems(), wait for the
- // thread idle
- ForegroundThread.waitForIdle();
-
- final List<Item> itemList = mPickerViewModel.getCategoryItems(
- CATEGORY_DOWNLOADS).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;
- mItemsProvider.setItems(generateFakeImageItemList(itemCount));
- mPickerViewModel.updateCategoryItems(CATEGORY_DOWNLOADS);
- // We use ForegroundThread to execute the loadItems in updateCategoryItems(), wait for the
- // thread idle
- ForegroundThread.waitForIdle();
-
- final List<Item> itemList = mPickerViewModel.getCategoryItems(
- CATEGORY_DOWNLOADS).getValue();
-
- // Original item count + 3 date items
- assertThat(itemList.size()).isEqualTo(itemCount + 3);
-
- final int updatedItemCount = 5;
- mItemsProvider.setItems(generateFakeImageItemList(updatedItemCount));
-
- // trigger updateCategoryItems in getCategoryItems first and wait the idle
- mPickerViewModel.getCategoryItems(CATEGORY_DOWNLOADS).getValue();
-
- // 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 = mPickerViewModel.getCategoryItems(
- CATEGORY_DOWNLOADS).getValue();
-
- // Original item count + 5 date items
- assertThat(updatedItemList.size()).isEqualTo(updatedItemCount + 5);
- }
-
- @Test
- public void testGetCategories() throws Exception {
- 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.getCategoryType()).isEqualTo(
- fakeFirstCategory.getCategoryType());
- assertThat(firstCategory.getCategoryName(/* context= */ null)).isEqualTo(
- fakeFirstCategory.getCategoryName(/* context= */ null));
- assertThat(firstCategory.getItemCount()).isEqualTo(fakeFirstCategory.getItemCount());
- assertThat(firstCategory.getCoverUri()).isEqualTo(fakeFirstCategory.getCoverUri());
- // Verify the second category
- final Category secondCategory = categoryList.get(1);
- assertThat(secondCategory.getCategoryType()).isEqualTo(
- fakeSecondCategory.getCategoryType());
- assertThat(secondCategory.getCategoryName(/* context= */ null)).isEqualTo(
- fakeSecondCategory.getCategoryName(/* context= */ null));
- assertThat(secondCategory.getItemCount()).isEqualTo(fakeSecondCategory.getItemCount());
- assertThat(secondCategory.getCoverUri()).isEqualTo(fakeSecondCategory.getCoverUri());
- }
- }
-
- @Test
- public void testParseValuesFromIntent_allowMultiple() throws Exception {
- final Intent intent = new Intent();
- intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
-
- mPickerViewModel.parseValuesFromIntent(intent);
-
- assertThat(mPickerViewModel.canSelectMultiple()).isTrue();
- }
-
- @Test
- public void testParseValuesFromIntent_noAllowMultiple()
- throws Exception {
- final Intent intent = new Intent();
- intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);
-
- mPickerViewModel.parseValuesFromIntent(intent);
-
- assertThat(mPickerViewModel.canSelectMultiple()).isFalse();
- }
-
- @Test
- public void testParseValuesFromIntent_setDefaultFalseForAllowMultiple()
- throws Exception {
- final Intent intent = new Intent();
-
- mPickerViewModel.parseValuesFromIntent(intent);
-
- assertThat(mPickerViewModel.canSelectMultiple()).isFalse();
- }
-
- @Test
- public void testParseValuesFromIntent_validMimeType()
- throws Exception {
- final Intent intent = new Intent();
- intent.setType("image/png");
-
- mPickerViewModel.parseValuesFromIntent(intent);
-
- assertThat(mPickerViewModel.hasMimeTypeFilter()).isTrue();
- }
-
- @Test
- public void testParseValuesFromIntent_ignoreInvalidMimeType()
- throws Exception {
- final Intent intent = new Intent();
- intent.setType("audio/*");
-
- mPickerViewModel.parseValuesFromIntent(intent);
-
- assertThat(mPickerViewModel.hasMimeTypeFilter()).isFalse();
- }
-
- @Test
- public void testParseValuesFromIntent_noMimeType_defaultFalse()
- throws Exception {
- final Intent intent = new Intent();
-
- mPickerViewModel.parseValuesFromIntent(intent);
-
- assertThat(mPickerViewModel.hasMimeTypeFilter()).isFalse();
- }
-
- @Test
- public void testParseValuesFromIntent_noAllowMultiple_defaultLimit()
- throws Exception {
- final int maxLimit = 20;
- final Intent intent = new Intent();
- intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, maxLimit);
-
- mPickerViewModel.parseValuesFromIntent(intent);
-
- assertThat(mPickerViewModel.canSelectMultiple()).isFalse();
- assertThat(mPickerViewModel.getMaxSelectionLimit()).isNotEqualTo(maxLimit);
- }
-
- @Test
- public void testParseValuesFromIntent_validMaxSelectionLimit() throws Exception {
- final int maxLimit = 20;
- final Intent intent = new Intent();
- intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
- intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, maxLimit);
-
- mPickerViewModel.parseValuesFromIntent(intent);
-
- assertThat(mPickerViewModel.getMaxSelectionLimit()).isEqualTo(maxLimit);
- }
-
- @Test
- public void testParseValuesFromIntent_negativeMaxSelectionLimit_throwsException()
- throws Exception {
- final int maxLimit = -1;
- final Intent intent = new Intent();
- intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
- intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, maxLimit);
-
- try {
- mPickerViewModel.parseValuesFromIntent(intent);
- fail("The maximum selection limit is not allowed to be negative");
- } catch (Exception expected) {
- // expected
- }
- }
-
- @Test
- public void testParseValuesFromIntent_tooLargeMaxSelectionLimit_defaultValue()
- throws Exception {
- final int maxLimit = 10000;
- final Intent intent = new Intent();
- intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
- intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, maxLimit);
-
- mPickerViewModel.parseValuesFromIntent(intent);
-
- assertThat(mPickerViewModel.getMaxSelectionLimit()).isNotEqualTo(maxLimit);
- }
-
- @Test
- public void testIsSelectionAllowed_exceedsMaxSelectionLimit_selectionNotAllowed()
- throws Exception {
- final int maxLimit = 2;
- final Intent intent = new Intent();
- intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
- intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, maxLimit);
- mPickerViewModel.parseValuesFromIntent(intent);
-
- assertThat(mPickerViewModel.isSelectionAllowed()).isTrue();
-
- final String id1 = "1";
- final Item item1 = generateFakeImageItem(id1);
- mPickerViewModel.addSelectedItem(item1);
-
- assertThat(mPickerViewModel.isSelectionAllowed()).isTrue();
-
- final String id2 = "2";
- final Item item2 = generateFakeImageItem(id2);
- mPickerViewModel.addSelectedItem(item2);
-
- assertThat(mPickerViewModel.isSelectionAllowed()).isFalse();
- }
-
- private static Item generateFakeImageItem(String id) {
- final long dateTakenMs = System.currentTimeMillis() + Long.parseLong(id)
- * DateUtils.DAY_IN_MILLIS;
-
- return ItemTest.generateItem(id, FAKE_IMAGE_MIME_TYPE, dateTakenMs, /* 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(Category.CategoryColumns.getAllColumns());
- final int itemCount = 5;
- for (int i = 0; i < num; i++) {
- cursor.addRow(new Object[]{
- FAKE_CATEGORY_NAME + i,
- FAKE_ID + String.valueOf(i),
- itemCount + i,
- CATEGORY_DOWNLOADS});
- }
- 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(@Nullable @Category.CategoryType String category, int offset,
- int limit, @Nullable String mimeType, @Nullable UserId userId) throws
- IllegalArgumentException, IllegalStateException {
- final String[] columns = Item.ItemColumns.ALL_COLUMNS;
- final MatrixCursor c = new MatrixCursor(columns);
-
- for (Item item : mItemList) {
- c.addRow(new String[] {
- item.getId(),
- item.getMimeType(),
- String.valueOf(item.getDateTaken()),
- String.valueOf(item.getDateTaken()),
- String.valueOf(item.getDuration()),
- });
- }
-
- return c;
- }
-
- @Nullable
- public Cursor getCategories(@Nullable String mimeType, @Nullable UserId userId) {
- if (mCategoriesCursor != null) {
- return mCategoriesCursor;
- }
-
- final MatrixCursor c = new MatrixCursor(Category.CategoryColumns.getAllColumns());
- return c;
- }
-
- public void setItems(@NonNull List<Item> itemList) {
- mItemList = itemList;
- }
-
- public void setCategoriesCursor(@NonNull Cursor cursor) {
- mCategoriesCursor = cursor;
- }
- }
-}
diff --git a/tests/src/com/android/providers/media/scan/LegacyMediaScannerTest.java b/tests/src/com/android/providers/media/scan/LegacyMediaScannerTest.java
index cf9cb39..af13fb8 100644
--- a/tests/src/com/android/providers/media/scan/LegacyMediaScannerTest.java
+++ b/tests/src/com/android/providers/media/scan/LegacyMediaScannerTest.java
@@ -54,7 +54,7 @@
} catch (UnsupportedOperationException expected) {
}
try {
- scanner.onDetachVolume(null);
+ scanner.onDetachVolume(MediaStore.VOLUME_EXTERNAL_PRIMARY);
fail();
} catch (UnsupportedOperationException expected) {
}
diff --git a/tests/src/com/android/providers/media/scan/MediaScannerTest.java b/tests/src/com/android/providers/media/scan/MediaScannerTest.java
index 62a2907..3d28820 100644
--- a/tests/src/com/android/providers/media/scan/MediaScannerTest.java
+++ b/tests/src/com/android/providers/media/scan/MediaScannerTest.java
@@ -22,7 +22,6 @@
import static org.junit.Assert.assertEquals;
-import android.Manifest;
import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
@@ -34,7 +33,6 @@
import android.os.Environment;
import android.os.SystemClock;
import android.provider.BaseColumns;
-import android.provider.DeviceConfig.OnPropertiesChangedListener;
import android.provider.MediaStore;
import android.provider.MediaStore.MediaColumns;
import android.provider.Settings;
@@ -49,7 +47,6 @@
import com.android.providers.media.MediaProvider;
import com.android.providers.media.R;
import com.android.providers.media.util.FileUtils;
-import com.android.providers.media.photopicker.PickerSyncController;
import org.junit.Before;
import org.junit.Ignore;
@@ -88,26 +85,6 @@
public boolean isFuseThread() {
return asFuseThread;
}
-
- @Override
- public boolean getBooleanDeviceConfig(String key, boolean defaultValue) {
- return defaultValue;
- }
-
- @Override
- public String getStringDeviceConfig(String key, String defaultValue) {
- return defaultValue;
- }
-
- @Override
- public int getIntDeviceConfig(String key, int defaultValue) {
- return defaultValue;
- }
-
- @Override
- public void addOnPropertiesChangedListener(OnPropertiesChangedListener listener) {
- // Ignore
- }
};
mProvider.attachInfo(this, info);
mResolver.addProvider(MediaStore.AUTHORITY, mProvider);
@@ -125,14 +102,6 @@
}
});
- mResolver.addProvider(PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY,
- new MockContentProvider() {
- @Override
- public Bundle call(String method, String request, Bundle args) {
- return Bundle.EMPTY;
- }
- });
-
MediaStore.waitForIdle(mResolver);
}
@@ -153,8 +122,6 @@
@Before
public void setUp() {
final Context context = InstrumentationRegistry.getTargetContext();
- InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
- Manifest.permission.INTERACT_ACROSS_USERS);
mLegacy = new LegacyMediaScanner(
new IsolatedContext(context, "legacy", /*asFuseThread*/ false));
diff --git a/tests/src/com/android/providers/media/scan/ModernMediaScannerTest.java b/tests/src/com/android/providers/media/scan/ModernMediaScannerTest.java
index d5e03f9..b10de97 100644
--- a/tests/src/com/android/providers/media/scan/ModernMediaScannerTest.java
+++ b/tests/src/com/android/providers/media/scan/ModernMediaScannerTest.java
@@ -38,7 +38,6 @@
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;
@@ -70,7 +69,6 @@
import android.util.Pair;
import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SdkSuppress;
import androidx.test.runner.AndroidJUnit4;
import com.android.providers.media.R;
@@ -116,8 +114,7 @@
final Context context = InstrumentationRegistry.getTargetContext();
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.adoptShellPermissionIdentity(Manifest.permission.LOG_COMPAT_CHANGE,
- Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
- Manifest.permission.INTERACT_ACROSS_USERS);
+ Manifest.permission.READ_COMPAT_CHANGE_CONFIG);
mDir = new File(context.getExternalMediaDirs()[0], "test_" + System.nanoTime());
mDir.mkdirs();
@@ -385,24 +382,22 @@
private static void assertShouldScanPathAndIsPathHidden(boolean isScannable, boolean isHidden,
File 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);
+ assertEquals(Pair.create(isScannable, isHidden), shouldScanPathAndIsPathHidden(dir));
}
@Test
public void testShouldScanPathAndIsPathHidden() {
for (String prefix : new String[] {
"/storage/emulated/0",
+ "/storage/emulated/0/Android/sandbox/com.example",
"/storage/0000-0000",
+ "/storage/0000-0000/Android/sandbox/com.example",
}) {
assertShouldScanPathAndIsPathHidden(true, false, new File(prefix));
assertShouldScanPathAndIsPathHidden(true, false, new File(prefix + "/meow"));
assertShouldScanPathAndIsPathHidden(true, false, new File(prefix + "/Android/meow"));
+ assertShouldScanPathAndIsPathHidden(true, false,
+ new File(prefix + "/Android/sandbox/meow"));
assertShouldScanPathAndIsPathHidden(true, true, new File(prefix + "/.meow/dir"));
@@ -418,9 +413,6 @@
new File(prefix + "/Movies/.thumbnails/meow"));
assertShouldScanPathAndIsPathHidden(false, false,
new File(prefix + "/Music/.thumbnails/meow"));
-
- assertShouldScanPathAndIsPathHidden(false, false,
- new File(prefix + "/.transforms/transcode"));
}
}
@@ -428,16 +420,12 @@
final File nomediaFile = new File(dir, ".nomedia");
if (!nomediaFile.getParentFile().exists()) {
- assertWithMessage("cannot create dir: " + nomediaFile.getParentFile().getAbsolutePath())
- .that(nomediaFile.getParentFile().mkdirs())
- .isTrue();
+ assertTrue(nomediaFile.getParentFile().mkdirs());
}
try {
if (!nomediaFile.exists()) {
executeShellCommand("touch " + nomediaFile.getAbsolutePath());
- assertWithMessage("cannot create nomedia file: " + nomediaFile.getAbsolutePath())
- .that(nomediaFile.exists())
- .isTrue();
+ assertTrue(nomediaFile.exists());
}
assertShouldScanPathAndIsPathHidden(true, false, dir);
} finally {
@@ -446,14 +434,12 @@
}
/**
- * b/168830497: Test that root folder, default folders and Camera folder are always visible
+ * b/168830497: Test that 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);
@@ -465,29 +451,6 @@
assertVisibleFolder(cameraDir);
}
- /**
- * 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) {
assertTrue(file.getAbsolutePath(), shouldScanDirectory(file));
}
@@ -500,24 +463,26 @@
public void testShouldScanDirectory() throws Exception {
for (String prefix : new String[] {
"/storage/emulated/0",
+ "/storage/emulated/0/Android/sandbox/com.example",
"/storage/0000-0000",
+ "/storage/0000-0000/Android/sandbox/com.example",
}) {
assertShouldScanDirectory(new File(prefix));
assertShouldScanDirectory(new File(prefix + "/meow"));
assertShouldScanDirectory(new File(prefix + "/Android"));
assertShouldScanDirectory(new File(prefix + "/Android/meow"));
+ assertShouldScanDirectory(new File(prefix + "/Android/sandbox"));
+ assertShouldScanDirectory(new File(prefix + "/Android/sandbox/meow"));
assertShouldScanDirectory(new File(prefix + "/.meow"));
assertShouldntScanDirectory(new File(prefix + "/Android/data"));
assertShouldntScanDirectory(new File(prefix + "/Android/obb"));
- assertShouldntScanDirectory(new File(prefix + "/Android/sandbox"));
assertShouldntScanDirectory(new File(prefix + "/Pictures/.thumbnails"));
assertShouldntScanDirectory(new File(prefix + "/Movies/.thumbnails"));
assertShouldntScanDirectory(new File(prefix + "/Music/.thumbnails"));
assertShouldScanDirectory(new File(prefix + "/DCIM/.thumbnails"));
- assertShouldntScanDirectory(new File(prefix + "/.transforms"));
}
}
@@ -533,7 +498,9 @@
public void testIsDirectoryHidden() throws Exception {
for (String prefix : new String[] {
"/storage/emulated/0",
+ "/storage/emulated/0/Android/sandbox/com.example",
"/storage/0000-0000",
+ "/storage/0000-0000/Android/sandbox/com.example",
}) {
assertDirectoryNotHidden(new File(prefix));
assertDirectoryNotHidden(new File(prefix + "/meow"));
@@ -705,7 +672,6 @@
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();
@@ -838,26 +804,6 @@
}
}
- @Test
- @SdkSuppress(minSdkVersion = 31, codeName = "S")
- public void testScan_audio_recording() throws Exception {
- final File music = new File(mDir, "Recordings");
- final File audio = new File(music, "audio.mp3");
-
- music.mkdirs();
- stage(R.raw.test_audio, audio);
-
- mModern.scanFile(audio, REASON_UNKNOWN);
-
- try (Cursor cursor = mIsolatedResolver
- .query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, null)) {
- assertEquals(1, cursor.getCount());
- cursor.moveToFirst();
- assertEquals(1, cursor.getInt(cursor.getColumnIndex(AudioColumns.IS_RECORDING)));
- assertEquals(0, cursor.getInt(cursor.getColumnIndex(AudioColumns.IS_MUSIC)));
- }
- }
-
/**
* Verify a narrow exception where we allow an {@code mp4} video file on
* disk to be indexed as an {@code m4a} audio file.
@@ -1240,7 +1186,7 @@
firstDirScan.dumpResults();
// Time taken : preVisitDirectory
- Timer noOpDirScan = new Timer("noOpDirScan1");
+ Timer noOpDirScan = new Timer("noOpDirScan");
for (int i = 0; i < COUNT_REPEAT; i++) {
noOpDirScan.start();
mModern.scanDirectory(mDir, REASON_UNKNOWN);
@@ -1250,21 +1196,23 @@
assertThat(noOpDirScan.getMaxDurationMillis()).isLessThan(
firstDirScan.getMaxDurationMillis());
- // Creating new file in the nomedia dir by a non-M_E_S app should not set nomedia dir dirty.
- File file = new File(mDir, "file_" + System.nanoTime());
- assertThat(file.createNewFile()).isTrue();
+ // renaming directory for non-M_E_S apps does a scan of the directory as well;
+ // so subsequent scans should be noOp as the directory is not dirty.
+ File renamedTestDir = new File(mIsolatedContext.getExternalMediaDirs()[0],
+ "renamed_test_" + System.nanoTime());
+ assertThat(mDir.renameTo(renamedTestDir)).isTrue();
- // The dir should not be dirty and subsequest scans should not scan the entire directory.
+ Timer renamedDirScan = new Timer("renamedDirScan");
+ renamedDirScan.start();
// Time taken : preVisitDirectory
- noOpDirScan = new Timer("noOpDirScan2");
- for (int i = 0; i < COUNT_REPEAT; i++) {
- noOpDirScan.start();
- mModern.scanDirectory(mDir, REASON_UNKNOWN);
- noOpDirScan.stop();
- }
- noOpDirScan.dumpResults();
- assertThat(noOpDirScan.getMaxDurationMillis()).isLessThan(
+ mModern.scanDirectory(renamedTestDir, REASON_UNKNOWN);
+ renamedDirScan.stop();
+ renamedDirScan.dumpResults();
+ assertThat(renamedDirScan.getMaxDurationMillis()).isLessThan(
firstDirScan.getMaxDurationMillis());
+
+ // This is essential for folder cleanup in tearDown
+ mDir = renamedTestDir;
}
@Test
diff --git a/tests/src/com/android/providers/media/scan/NullMediaScannerTest.java b/tests/src/com/android/providers/media/scan/NullMediaScannerTest.java
index 063f1b7..44528ba 100644
--- a/tests/src/com/android/providers/media/scan/NullMediaScannerTest.java
+++ b/tests/src/com/android/providers/media/scan/NullMediaScannerTest.java
@@ -41,6 +41,6 @@
scanner.scanFile(new File("/dev/null"), MediaScanner.REASON_UNKNOWN,
InstrumentationRegistry.getContext().getPackageName());
- scanner.onDetachVolume(null);
+ scanner.onDetachVolume(MediaStore.VOLUME_EXTERNAL_PRIMARY);
}
}
diff --git a/tests/src/com/android/providers/media/util/FileUtilsTest.java b/tests/src/com/android/providers/media/util/FileUtilsTest.java
index e53403e..abb5e7f 100644
--- a/tests/src/com/android/providers/media/util/FileUtilsTest.java
+++ b/tests/src/com/android/providers/media/util/FileUtilsTest.java
@@ -44,15 +44,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.isExternalMediaDirectory;
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;
import static com.android.providers.media.util.FileUtils.translateModePosixToString;
import static com.android.providers.media.util.FileUtils.translateModeStringToPosix;
-import static com.google.common.truth.Truth.assertThat;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
@@ -61,18 +58,16 @@
import android.content.ContentValues;
import android.os.Environment;
-import android.os.SystemProperties;
import android.provider.MediaStore;
import android.provider.MediaStore.MediaColumns;
-import android.text.TextUtils;
import androidx.test.InstrumentationRegistry;
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;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -554,62 +549,6 @@
}
@Test
- public void testExtractTopLevelDirWithRelativePathSegments() throws Exception {
- assertEquals(null,
- extractTopLevelDir(new String[] { null }));
- assertEquals("DCIM",
- extractTopLevelDir(new String[] { "DCIM" }));
- assertEquals("DCIM",
- extractTopLevelDir(new String[] { "DCIM", "My Vacation" }));
-
- assertEquals(null,
- extractTopLevelDir(new String[] { "AppClone" }, "AppClone"));
- assertEquals("DCIM",
- extractTopLevelDir(new String[] { "AppClone", "DCIM" }, "AppClone"));
- assertEquals("DCIM",
- extractTopLevelDir(new String[] { "AppClone", "DCIM", "My Vacation" }, "AppClone"));
-
- assertEquals("Test",
- extractTopLevelDir(new String[] { "Test" }, "AppClone"));
- assertEquals("Test",
- extractTopLevelDir(new String[] { "Test", "DCIM" }, "AppClone"));
- assertEquals("Test",
- extractTopLevelDir(new String[] { "Test", "DCIM", "My Vacation" }, "AppClone"));
- }
-
- @Test
- public void testExtractTopLevelDirForCrossUser() throws Exception {
- Assume.assumeTrue(FileUtils.isCrossUserEnabled());
-
- final String crossUserRoot = SystemProperties.get("external_storage.cross_user.root", null);
- Assume.assumeFalse(TextUtils.isEmpty(crossUserRoot));
-
- for (String prefix : new String[] {
- "/storage/emulated/0/",
- "/storage/0000-0000/"
- }) {
- assertEquals(null,
- extractTopLevelDir(prefix + "foo.jpg"));
- assertEquals("DCIM",
- extractTopLevelDir(prefix + "DCIM/foo.jpg"));
- assertEquals("DCIM",
- extractTopLevelDir(prefix + "DCIM/My Vacation/foo.jpg"));
-
- assertEquals(null,
- extractTopLevelDir(prefix + crossUserRoot + "/foo.jpg"));
- assertEquals("DCIM",
- extractTopLevelDir(prefix + crossUserRoot + "/DCIM/foo.jpg"));
- assertEquals("DCIM",
- extractTopLevelDir(prefix + crossUserRoot + "/DCIM/My Vacation/foo.jpg"));
-
- assertEquals("Test",
- extractTopLevelDir(prefix + "Test/DCIM/foo.jpg"));
- assertEquals("Test",
- extractTopLevelDir(prefix + "Test/DCIM/My Vacation/foo.jpg"));
- }
- }
-
- @Test
public void testExtractDisplayName() throws Exception {
for (String probe : new String[] {
"foo.bar.baz",
@@ -743,7 +682,7 @@
FileUtils.computeDateExpires(values);
final long target = (System.currentTimeMillis()
+ FileUtils.DEFAULT_DURATION_PENDING) / 1_000;
- assertThat(values.getAsLong(MediaColumns.DATE_EXPIRES))
+ Truth.assertThat(values.getAsLong(MediaColumns.DATE_EXPIRES))
.isIn(Range.closed(target - 5, target + 5));
}
@@ -767,7 +706,7 @@
FileUtils.computeDateExpires(values);
final long target = (System.currentTimeMillis()
+ FileUtils.DEFAULT_DURATION_TRASHED) / 1_000;
- assertThat(values.getAsLong(MediaColumns.DATE_EXPIRES))
+ Truth.assertThat(values.getAsLong(MediaColumns.DATE_EXPIRES))
.isIn(Range.closed(target - 5, target + 5));
}
@@ -798,25 +737,7 @@
File nomedia = new File(dirInDownload, ".nomedia");
assertTrue(nomedia.createNewFile());
- 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);
+ assertEquals(dirInDownload, FileUtils.getTopLevelNoMedia(new File(dirInDownload, "foo")));
}
@Test
@@ -830,10 +751,8 @@
File nomedia = new File(dirInTopDirInDownload, ".nomedia");
assertTrue(nomedia.createNewFile());
- assertThat(FileUtils.getTopLevelNoMedia(dirInTopDirInDownload))
- .isEqualTo(topDirInDownload);
- assertThat(FileUtils.getTopLevelNoMedia(new File(dirInTopDirInDownload, "foo")))
- .isEqualTo(topDirInDownload);
+ assertEquals(topDirInDownload,
+ FileUtils.getTopLevelNoMedia(new File(dirInTopDirInDownload, "foo")));
}
@Test
@@ -844,20 +763,20 @@
assertEquals(null,
FileUtils.getTopLevelNoMedia(new File(dirInTopDirInDownload, "foo")));
- assertThat(FileUtils.getTopLevelNoMedia(dirInTopDirInDownload))
- .isNull();
- assertThat(FileUtils.getTopLevelNoMedia(new File(dirInTopDirInDownload, "foo")))
- .isNull();
}
@Test
public void testDirectoryDirty() throws Exception {
File dirInDownload = getNewDirInDownload("testDirectoryDirty");
- // Directory without nomedia is not dirty
- assertFalse(FileUtils.isDirectoryDirty(dirInDownload));
+ // All directories are considered dirty, unless hidden
+ assertTrue(FileUtils.isDirectoryDirty(dirInDownload));
- // Creating an empty .nomedia file makes directory dirty
+ // 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
File nomedia = new File(dirInDownload, ".nomedia");
assertTrue(nomedia.createNewFile());
assertTrue(FileUtils.isDirectoryDirty(dirInDownload));
@@ -871,11 +790,6 @@
assertTrue(FileUtils.isDirectoryDirty(dirInDownload));
}
- @Test
- public void testDirectoryDirty_nullDir() throws Exception {
- assertThat(FileUtils.isDirectoryDirty(null)).isFalse();
- }
-
private File getNewDirInDownload(String name) {
File file = new File(mTestDownloadDir, name);
assertTrue(file.mkdir());
@@ -931,18 +845,7 @@
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
- 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/"
- }) {
- assertTrue(isExternalMediaDirectory(prefix + "Android/media/foo.jpg", "AppClone"));
- assertFalse(isExternalMediaDirectory(prefix + "Android/media/foo.jpg", "NotAppClone"));
- }
+ Truth.assertThat(result.length()).isAtMost(MAX_FILENAME_BYTES);
+ Truth.assertThat(result).isNotEqualTo(originalName);
}
}
diff --git a/tests/src/com/android/providers/media/util/MetricsTest.java b/tests/src/com/android/providers/media/util/MetricsTest.java
index 264cb1b..688e2e9 100644
--- a/tests/src/com/android/providers/media/util/MetricsTest.java
+++ b/tests/src/com/android/providers/media/util/MetricsTest.java
@@ -45,7 +45,7 @@
Metrics.logDeletion(volumeName, 42, packageName, 42, new int[] { 42 });
Metrics.logPermissionGranted(volumeName, 42, packageName, 42);
Metrics.logPermissionDenied(volumeName, 42, packageName, 42);
- Metrics.logSchemaChange(volumeName, 42, 42, 42, 42, "UUID");
+ Metrics.logSchemaChange(volumeName, 42, 42, 42, 42);
Metrics.logIdleMaintenance(volumeName, 42, 42, 42, 42);
}
}
diff --git a/tests/src/com/android/providers/media/util/PermissionUtilsTest.java b/tests/src/com/android/providers/media/util/PermissionUtilsTest.java
index c434bd2..1eba379 100644
--- a/tests/src/com/android/providers/media/util/PermissionUtilsTest.java
+++ b/tests/src/com/android/providers/media/util/PermissionUtilsTest.java
@@ -16,29 +16,8 @@
package com.android.providers.media.util;
-import static android.Manifest.permission.MANAGE_APP_OPS_MODES;
-import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE;
-import static android.Manifest.permission.MANAGE_MEDIA;
-import static android.Manifest.permission.UPDATE_APP_OPS_STATS;
-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;
-
-import static androidx.test.InstrumentationRegistry.getContext;
-
-import static com.android.providers.media.util.PermissionUtils.checkAppOpRequestInstallPackagesForSharedUid;
-import static com.android.providers.media.util.PermissionUtils.checkIsLegacyStorageGranted;
import static com.android.providers.media.util.PermissionUtils.checkNoIsolatedStorageGranted;
-import static com.android.providers.media.util.PermissionUtils.checkPermissionAccessMediaLocation;
-import static com.android.providers.media.util.PermissionUtils.checkPermissionAccessMtp;
import static com.android.providers.media.util.PermissionUtils.checkPermissionDelegator;
-import static com.android.providers.media.util.PermissionUtils.checkPermissionInstallPackages;
-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;
@@ -50,49 +29,20 @@
import static com.android.providers.media.util.PermissionUtils.checkPermissionWriteImages;
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;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertEquals;
-
-import android.app.AppOpsManager;
import android.content.Context;
-import android.content.Intent;
-import androidx.test.filters.SdkSuppress;
+import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import com.android.cts.install.lib.TestApp;
-
-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_WITHOUT_PERMS = new TestApp("TestAppWithoutPerms",
- "com.android.providers.media.testapp.withoutperms", 1, false,
- "MediaProviderTestAppWithoutPerms.apk");
- private static final TestApp LEGACY_TEST_APP = new TestApp("LegacyTestApp",
- "com.android.providers.media.testapp.legacy", 1, false,
- "LegacyMediaProviderTestApp.apk");
-
- // Permission checks are based on uid, so we can pass -1 pid and avoid starting the test apps.
- private static final int TEST_APP_PID = -1;
-
@Test
public void testConstructor() {
new PermissionUtils();
@@ -103,30 +53,26 @@
* we expect to be holding.
*/
@Test
- public void testSelfPermissions() {
- final Context context = getContext();
+ public void testSelfPermissions() throws Exception {
+ final Context context = InstrumentationRegistry.getContext();
final int pid = android.os.Process.myPid();
final int uid = android.os.Process.myUid();
final String packageName = context.getPackageName();
- assertThat(checkPermissionSelf(context, pid, uid)).isTrue();
- assertThat(checkPermissionShell(context, pid, uid)).isFalse();
- assertThat(checkPermissionManager(context, pid, uid, packageName, null)).isFalse();
- assertThat(checkPermissionDelegator(context, pid, uid)).isFalse();
- assertThat(checkPermissionManageMedia(context, pid, uid, packageName, null)).isFalse();
- assertThat(checkPermissionAccessMediaLocation(context, pid, uid,
- packageName, null)).isFalse();
+ assertTrue(checkPermissionSelf(context, pid, uid));
+ assertFalse(checkPermissionShell(context, pid, uid));
+ assertFalse(checkPermissionManager(context, pid, uid, packageName, null));
+ assertFalse(checkPermissionDelegator(context, pid, uid));
- assertThat(checkPermissionReadStorage(context, pid, uid, packageName, null)).isTrue();
- assertThat(checkPermissionWriteStorage(context, pid, uid, packageName, null)).isTrue();
+ assertTrue(checkPermissionReadStorage(context, pid, uid, packageName, null));
+ assertTrue(checkPermissionWriteStorage(context, pid, uid, packageName, null));
- assertThat(checkPermissionReadAudio(context, pid, uid, packageName, null)).isTrue();
- assertThat(checkPermissionWriteAudio(context, pid, uid, packageName, null)).isFalse();
- assertThat(checkPermissionReadVideo(context, pid, uid, packageName, null)).isTrue();
- assertThat(checkPermissionWriteVideo(context, pid, uid, packageName, null)).isFalse();
- assertThat(checkPermissionReadImages(context, pid, uid, packageName, null)).isTrue();
- assertThat(checkPermissionWriteImages(context, pid, uid, packageName, null)).isFalse();
- assertThat(checkPermissionInstallPackages(context, pid, uid, packageName, null)).isFalse();
+ assertTrue(checkPermissionReadAudio(context, pid, uid, packageName, null));
+ assertFalse(checkPermissionWriteAudio(context, pid, uid, packageName, null));
+ assertTrue(checkPermissionReadVideo(context, pid, uid, packageName, null));
+ assertFalse(checkPermissionWriteVideo(context, pid, uid, packageName, null));
+ assertTrue(checkPermissionReadImages(context, pid, uid, packageName, null));
+ assertFalse(checkPermissionWriteImages(context, pid, uid, packageName, null));
}
/**
@@ -134,372 +80,9 @@
*/
@Test
public void testNoIsolatedStorageIsByDefaultDenied() throws Exception {
- final Context context = getContext();
+ final Context context = InstrumentationRegistry.getContext();
final int uid = android.os.Process.myUid();
final String packageName = context.getPackageName();
- assertThat(checkNoIsolatedStorageGranted(context, uid, packageName, null)).isFalse();
- }
-
- @Test
- public void testDefaultPermissionsOnTestAppWithStoragePerms() throws Exception {
- String packageName = TEST_APP_WITH_STORAGE_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)).isTrue();
- checkReadPermissions(TEST_APP_PID, testAppUid, packageName, true);
- } finally {
- dropShellPermission();
- }
- }
-
- @Test
- public void testDefaultPermissionsOnTestAppWithoutPerms() throws Exception {
- String packageName = TEST_APP_WITHOUT_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(
- checkPermissionManager(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isFalse();
-
- assertThat(
- checkPermissionManageMedia(
- getContext(), TEST_APP_PID, testAppUid, packageName, null))
- .isFalse();
-
- assertThat(checkPermissionAccessMediaLocation(getContext(), TEST_APP_PID, testAppUid,
- packageName, null)).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();
- checkReadPermissions(TEST_APP_PID, testAppUid, packageName, false);
- } finally {
- dropShellPermission();
- }
- }
-
- @Test
- public void testDefaultPermissionsOnLegacyTestApp() throws Exception {
- String packageName = LEGACY_TEST_APP.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(
- checkPermissionManager(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isFalse();
-
- assertThat(
- checkPermissionManageMedia(
- getContext(), TEST_APP_PID, testAppUid, packageName, null))
- .isFalse();
-
- assertThat(checkPermissionAccessMediaLocation(getContext(), TEST_APP_PID, testAppUid,
- packageName, null)).isTrue();
-
- assertThat(
- checkIsLegacyStorageGranted(getContext(), testAppUid, packageName,
- null)).isTrue();
- 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();
- checkReadPermissions(TEST_APP_PID, testAppUid, packageName, true);
- } finally {
- dropShellPermission();
- }
- }
-
- @Test
- public void testManageExternalStoragePermissionsOnTestApp() throws Exception {
- final String packageName = TEST_APP_WITH_STORAGE_PERMS.getPackageName();
- final int testAppUid = getContext().getPackageManager().getPackageUid(packageName, 0);
- final String op = AppOpsManager.permissionToOp(MANAGE_EXTERNAL_STORAGE);
- adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
-
- try {
- modifyAppOp(testAppUid, op, AppOpsManager.MODE_ERRORED);
-
- assertThat(checkPermissionManager(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isFalse();
-
- modifyAppOp(testAppUid, op, AppOpsManager.MODE_ALLOWED);
-
- assertThat(checkPermissionManager(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isTrue();
- } finally {
- dropShellPermission();
- }
- }
-
- @Test
- @SdkSuppress(minSdkVersion = 31, codeName = "S")
- public void testManageMediaPermissionsOnTestApp() throws Exception {
- final String packageName = TEST_APP_WITH_STORAGE_PERMS.getPackageName();
- final int testAppUid = getContext().getPackageManager().getPackageUid(packageName, 0);
- final String op = AppOpsManager.permissionToOp(MANAGE_MEDIA);
- adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
-
- try {
- modifyAppOp(testAppUid, op, AppOpsManager.MODE_ERRORED);
-
- assertThat(
- checkPermissionManageMedia(
- getContext(), TEST_APP_PID, testAppUid, packageName, null))
- .isFalse();
-
- modifyAppOp(testAppUid, op, AppOpsManager.MODE_ALLOWED);
-
- assertThat(
- checkPermissionManageMedia(
- getContext(), TEST_APP_PID, testAppUid, packageName, null))
- .isTrue();
- } finally {
- dropShellPermission();
- }
- }
-
- @Test
- public void testSystemGalleryPermissionsOnTestApp() throws Exception {
- String packageName = TEST_APP_WITH_STORAGE_PERMS.getPackageName();
- int testAppUid = getContext().getPackageManager().getPackageUid(packageName, 0);
- adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
-
- try {
- checkPermissionsForGallery(testAppUid, TEST_APP_PID, packageName, false);
-
- final String[] SYSTEM_GALERY_APPOPS =
- {OPSTR_WRITE_MEDIA_IMAGES, OPSTR_WRITE_MEDIA_VIDEO};
- for (String op : SYSTEM_GALERY_APPOPS) {
- modifyAppOp(testAppUid, op, AppOpsManager.MODE_ALLOWED);
- }
- checkPermissionsForGallery(testAppUid, TEST_APP_PID, packageName, true);
-
- for (String op : SYSTEM_GALERY_APPOPS) {
- modifyAppOp(testAppUid, op, AppOpsManager.MODE_ERRORED);
- }
- checkPermissionsForGallery(testAppUid, TEST_APP_PID, packageName, false);
- } finally {
- dropShellPermission();
- }
- }
-
- @Test
- public void testIsolatedStoragePermissionsOnTestApp() throws Exception {
- String packageName = TEST_APP_WITH_STORAGE_PERMS.getPackageName();
- int testAppUid = getContext().getPackageManager().getPackageUid(packageName, 0);
- adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
-
- try {
- assertThat(
- checkNoIsolatedStorageGranted(getContext(), testAppUid, packageName,
- null)).isFalse();
-
- modifyAppOp(testAppUid, OPSTR_NO_ISOLATED_STORAGE, AppOpsManager.MODE_ALLOWED);
- assertThat(
- checkNoIsolatedStorageGranted(getContext(), testAppUid, packageName,
- null)).isTrue();
-
- modifyAppOp(testAppUid, OPSTR_NO_ISOLATED_STORAGE, AppOpsManager.MODE_ERRORED);
- assertThat(
- checkNoIsolatedStorageGranted(getContext(), testAppUid, packageName,
- null)).isFalse();
- } finally {
- dropShellPermission();
- }
- }
-
- @Test
- public void testReadVideoOnTestApp() 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);
-
- try {
- assertThat(
- checkPermissionReadVideo(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isTrue();
-
- modifyAppOp(testAppUid, OPSTR_READ_MEDIA_VIDEO, AppOpsManager.MODE_ERRORED);
- assertThat(
- checkPermissionReadVideo(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isFalse();
-
- modifyAppOp(testAppUid, OPSTR_READ_MEDIA_VIDEO, AppOpsManager.MODE_ALLOWED);
- assertThat(
- checkPermissionReadVideo(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isTrue();
- } finally {
- dropShellPermission();
- }
- }
-
- @Test
- public void testWriteAudioOnTestApp() 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);
-
- try {
- assertThat(
- checkPermissionWriteAudio(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isFalse();
-
- modifyAppOp(testAppUid, OPSTR_WRITE_MEDIA_AUDIO, AppOpsManager.MODE_ALLOWED);
- assertThat(
- checkPermissionWriteAudio(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isTrue();
-
- modifyAppOp(testAppUid, OPSTR_WRITE_MEDIA_AUDIO, AppOpsManager.MODE_ERRORED);
- assertThat(
- checkPermissionWriteAudio(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isFalse();
- } finally {
- dropShellPermission();
- }
- }
-
- @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);
-
- try {
- assertThat(
- checkPermissionReadAudio(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isTrue();
-
- modifyAppOp(testAppUid, OPSTR_READ_MEDIA_AUDIO, AppOpsManager.MODE_ERRORED);
- assertThat(
- checkPermissionReadAudio(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isFalse();
-
- modifyAppOp(testAppUid, OPSTR_READ_MEDIA_AUDIO, AppOpsManager.MODE_ALLOWED);
- assertThat(
- checkPermissionReadAudio(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isTrue();
- } finally {
- dropShellPermission();
- }
- }
-
- @Test
- public void testReadImagesOnTestApp() 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);
-
- try {
- assertThat(
- checkPermissionReadImages(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isTrue();
-
- modifyAppOp(testAppUid, OPSTR_READ_MEDIA_IMAGES, AppOpsManager.MODE_ERRORED);
- assertThat(
- checkPermissionReadImages(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isFalse();
-
- modifyAppOp(testAppUid, OPSTR_READ_MEDIA_IMAGES, AppOpsManager.MODE_ALLOWED);
- assertThat(
- checkPermissionReadImages(getContext(), TEST_APP_PID, testAppUid, packageName,
- null)).isTrue();
- } finally {
- dropShellPermission();
- }
- }
-
- @Test
- public void testOpstrInstallPermissionsOnTestApp() throws Exception {
- int testAppUid = getContext().getPackageManager().getPackageUid(
- TEST_APP_WITH_STORAGE_PERMS.getPackageName(), 0);
- String[] packageName = {TEST_APP_WITH_STORAGE_PERMS.getPackageName()};
- adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES);
-
- try {
- assertThat(
- checkAppOpRequestInstallPackagesForSharedUid(getContext(), testAppUid,
- packageName, null)).isFalse();
-
- modifyAppOp(testAppUid, OPSTR_REQUEST_INSTALL_PACKAGES, AppOpsManager.MODE_ALLOWED);
- assertThat(
- checkAppOpRequestInstallPackagesForSharedUid(getContext(), testAppUid,
- packageName, null)).isTrue();
-
- modifyAppOp(testAppUid, OPSTR_REQUEST_INSTALL_PACKAGES, AppOpsManager.MODE_ERRORED);
- assertThat(
- checkAppOpRequestInstallPackagesForSharedUid(getContext(), testAppUid,
- packageName, null)).isFalse();
- } finally {
- dropShellPermission();
- }
- }
-
- static private void modifyAppOp(int uid, String op, int mode) {
- getContext().getSystemService(AppOpsManager.class).setUidMode(op, uid, mode);
- }
-
- static private void checkPermissionsForGallery(int uid, int pid, String packageName,
- boolean expected) {
- assertEquals(expected,
- checkWriteImagesOrVideoAppOps(getContext(), uid, packageName, null));
- assertEquals(expected,
- checkPermissionWriteImages(getContext(), pid, uid, packageName, null));
- assertEquals(expected,
- checkPermissionWriteVideo(getContext(), pid, uid, packageName, null));
- assertThat(
- checkPermissionWriteAudio(getContext(), pid, uid, packageName, null))
- .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));
+ assertFalse(checkNoIsolatedStorageGranted(context, uid, packageName, null));
}
}
diff --git a/tests/src/com/android/providers/media/util/TestUtils.java b/tests/src/com/android/providers/media/util/TestUtils.java
deleted file mode 100644
index f2d3696..0000000
--- a/tests/src/com/android/providers/media/util/TestUtils.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.util;
-
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
-
-import android.support.test.uiautomator.UiDevice;
-
-import androidx.annotation.NonNull;
-
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-public class TestUtils {
- public static final String QUERY_TYPE = "com.android.providers.media.util.QUERY_TYPE";
- public static final String RUN_INFINITE_ACTIVITY =
- "com.android.providers.media.util.RUN_INFINITE_ACTIVITY";
-
- private static final long POLLING_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(20);
- private static final long POLLING_SLEEP_MILLIS = 100;
-
- public static void adoptShellPermission(@NonNull String... perms) {
- getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(perms);
- }
-
- public static void dropShellPermission() {
- getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
- }
-
- public static int getPid(String packageName)
- throws IOException, InterruptedException, TimeoutException {
- UiDevice uiDevice = UiDevice.getInstance(getInstrumentation());
- for (int i = 0; i < POLLING_TIMEOUT_MILLIS / POLLING_SLEEP_MILLIS; i++) {
- String pid = uiDevice.executeShellCommand("pidof " + packageName).trim();
- if (pid.length() > 0) {
- return new Integer(pid);
- }
- Thread.sleep(POLLING_SLEEP_MILLIS);
- }
-
- throw new TimeoutException("Timed out waiting for pid");
- }
-}
diff --git a/tests/test_app/LegacyTestApp.xml b/tests/test_app/LegacyTestApp.xml
deleted file mode 100644
index 4fe9761..0000000
--- a/tests/test_app/LegacyTestApp.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?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.legacy"
- android:versionCode="1"
- android:versionName="1.0">
-
- <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="28" />
-
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
-
- <application android:label="LegacyTestApp"
- android:requestLegacyExternalStorage="true">
- <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/TestAppForPermissionActivity.xml b/tests/test_app/TestAppForPermissionActivity.xml
deleted file mode 100644
index cd1cc82..0000000
--- a/tests/test_app/TestAppForPermissionActivity.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?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.permission"
- android:versionCode="1"
- android:versionName="1.0">
-
- <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
-
- <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_EXTERNAL_STORAGE"/>
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
-
- <application android:label="TestAppPerms">
- <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/TestAppWithStoragePerms.xml b/tests/test_app/TestAppWithStoragePerms.xml
deleted file mode 100644
index 5910280..0000000
--- a/tests/test_app/TestAppWithStoragePerms.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?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.withstorageperms"
- android:versionCode="1"
- android:versionName="1.0">
-
- <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
-
- <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_EXTERNAL_STORAGE"/>
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
-
- <application android:label="TestAppPerms">
- <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/TestAppWithoutPerms.xml b/tests/test_app/TestAppWithoutPerms.xml
deleted file mode 100644
index 9708129..0000000
--- a/tests/test_app/TestAppWithoutPerms.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?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.withoutperms"
- android:versionCode="1"
- android:versionName="1.0">
-
- <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
-
- <application android:label="TestAppWithoutPerms">
- <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/src/com/android/providers/media/util/TestAppActivity.java b/tests/test_app/src/com/android/providers/media/util/TestAppActivity.java
deleted file mode 100644
index 8c95cce..0000000
--- a/tests/test_app/src/com/android/providers/media/util/TestAppActivity.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.util;
-
-import static com.android.providers.media.util.TestUtils.QUERY_TYPE;
-import static com.android.providers.media.util.TestUtils.RUN_INFINITE_ACTIVITY;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-
-public class TestAppActivity extends Activity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- String queryType = getIntent().getStringExtra(QUERY_TYPE);
- queryType = queryType == null ? "null" : queryType;
-
- switch (queryType) {
- case RUN_INFINITE_ACTIVITY:
- while (true) {
- }
- default:
- throw new IllegalStateException(
- "Unknown query received from launcher app: " + queryType);
- }
- }
-}
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/tools/dialogs/Android.bp b/tools/dialogs/Android.bp
index d0ccff2..8d2c000 100644
--- a/tools/dialogs/Android.bp
+++ b/tools/dialogs/Android.bp
@@ -1,12 +1,3 @@
-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_app {
name: "MediaProviderDialogsTool",
manifest: "AndroidManifest.xml",
diff --git a/tools/dialogs/AndroidManifest.xml b/tools/dialogs/AndroidManifest.xml
index 960cb13..947f1bd 100644
--- a/tools/dialogs/AndroidManifest.xml
+++ b/tools/dialogs/AndroidManifest.xml
@@ -1,18 +1,14 @@
-<?xml version="1.0" encoding="utf-8"?>
-
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.providers.media.tools.dialogs">
+ package="com.android.providers.media.tools.dialogs">
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
- <uses-permission android:name="android.permission.MANAGE_MEDIA"/>
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application android:label="DialogsTool">
- <activity android:name=".DialogsActivity"
- android:exported="true">
+ <activity android:name=".DialogsActivity">
<intent-filter android:label="DialogsTool">
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
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..4ff1731 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
@@ -99,14 +99,6 @@
addAction("Request delete", () -> {
return MediaStore.createDeleteRequest(getContentResolver(), mRequestItems.get());
});
- addAction("Request favorite", () -> {
- return MediaStore.createFavoriteRequest(getContentResolver(), mRequestItems.get(),
- true);
- });
- addAction("Request unfavorite", () -> {
- return MediaStore.createFavoriteRequest(getContentResolver(), mRequestItems.get(),
- false);
- });
new BackgroundTask().execute();
}
diff --git a/transcode.sh b/transcode.sh
deleted file mode 100644
index c223eb5..0000000
--- a/transcode.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-# For extracting a transcode_compat_manifest from a csv file in the format
-# package_name,hevc_support,slow_motion_support,hdr_10_support,hdr_10_plus_support,hdr_hlg_support,hdr_dolby_vision_support,hevc_support_shift,slow_motion_support_shift,hdr_10_support_shift,hd_10_plus_support_shift,hdr_hlg_support_shift,hdr_dolby_vision_support_shift,media_capability
-# com.foo,1,0,0,0,0,0,1,0,0,0,0,0,1
-# ....
-function transcode_compat_manifest() {
- # Cat file
- # Remove CLRF (DOS format)
- # Remove first line (header)
- # Extract first and last columns in each line
- # For device_config convert new lines (\n) to comma(,)
- # For device_config remove trailing comma(,)
- case "$1" in
- -r) cat $2 | tr -d '\r' | sed 1d | awk -F "," '{new_var=$1","$NF; print new_var}';;
- -d) cat $2 | tr -d '\r' | sed 1d | awk -F "," '{new_var=$1","$NF; print new_var}' | tr '\n' ',' | sed 's/,$//g';;
- *) "Enter '-d' for device_config, '-r' for resource";;
- esac
- echo
-}