Merge "Merge Android 12"
diff --git a/Android.bp b/Android.bp
index 9826476..5547a61 100644
--- a/Android.bp
+++ b/Android.bp
@@ -25,6 +25,7 @@
         "androidx.appcompat_appcompat",
         "androidx.core_core",
         "guava",
+        "modules-utils-build",
     ],
 
     libs: [
@@ -32,6 +33,7 @@
         "app-compat-annotations",
         "framework-annotations-lib",
         "framework-mediaprovider.impl",
+        "framework-media.stubs.module_lib",
         "framework-statsd",
     ],
 
@@ -56,8 +58,9 @@
         "error_prone_mediaprovider",
     ],
 
-    min_sdk_version: "30",
     sdk_version: "module_current",
+    min_sdk_version: "30",
+    target_sdk_version: "30",
 
     certificate: "media",
     privileged: true,
@@ -75,6 +78,8 @@
             "-Xep:MediaProviderMimeType:ERROR",
         ],
     },
+
+    required: ["preinstalled-packages-com.android.providers.media.module.xml"],
 }
 
 // Used by MediaProvider and MediaProviderTests
@@ -99,11 +104,14 @@
         "src/com/android/providers/media/util/ForegroundThread.java",
         "src/com/android/providers/media/util/Logging.java",
         "src/com/android/providers/media/util/MimeUtils.java",
+        "src/com/android/providers/media/playlist/*.java",
     ],
     sdk_version: "module_current",
     min_sdk_version: "30",
     static_libs: [
         "modules-utils-backgroundthread",
+        "modules-utils-build",
+        "guava",
     ],
     libs: [
         "androidx.annotation_annotation",
@@ -125,3 +133,10 @@
          " --minApiLevel 30",
     out: ["com/android/providers/media/MediaProviderStatsLog.java"],
 }
+
+prebuilt_etc {
+      name: "preinstalled-packages-com.android.providers.media.module.xml",
+      src: "preinstalled-packages-com.android.providers.media.module.xml",
+      sub_dir: "sysconfig",
+}
+
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 305027b..336e8eb 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -8,6 +8,8 @@
 
     <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" />
@@ -25,6 +27,17 @@
     <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" />
+
     <application
             android:name="com.android.providers.media.MediaApplication"
             android:label="@string/app_label"
@@ -52,7 +65,8 @@
         </provider>
 
         <!-- Handles database upgrades after OTAs, then disables itself -->
-        <receiver android:name="com.android.providers.media.MediaUpgradeReceiver">
+        <receiver android:name="com.android.providers.media.MediaUpgradeReceiver"
+            android:exported="true">
             <!-- This broadcast is sent after the core system has finished
                  booting, before the home app is launched or BOOT_COMPLETED
                  is sent. -->
@@ -61,7 +75,8 @@
             </intent-filter>
         </receiver>
 
-        <receiver android:name="com.android.providers.media.MediaReceiver">
+        <receiver android:name="com.android.providers.media.MediaReceiver"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.BOOT_COMPLETED" />
             </intent-filter>
@@ -94,6 +109,7 @@
             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" />
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 9d973e9..87582bb 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -4,7 +4,15 @@
             "name": "MediaProviderTests"
         },
         {
-            "name": "MediaProviderClientTests"
+            "name": "MediaProviderClientTests",
+            "options": [
+                {
+                  "exclude-annotation": "androidx.test.filters.LargeTest"
+                },
+                {
+                  "exclude-annotation": "androidx.test.filters.FlakyTest"
+                }
+            ]
         },
         {
             "name": "CtsProviderTestCases",
@@ -20,10 +28,32 @@
             "name": "AdoptableHostTest"
         },
         {
+            "name": "CtsScopedStorageCoreHostTest"
+        },
+        {
             "name": "CtsScopedStorageHostTest"
         },
         {
+            "name": "CtsScopedStorageDeviceOnlyTest"
+        },
+        {
             "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 7fa256c..d53560a 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -17,16 +17,16 @@
 
 apex_defaults {
     name: "com.android.mediaprovider-defaults",
-    updatable: true,
-    min_sdk_version: "30",
     bootclasspath_fragments: ["com.android.mediaprovider-bootclasspath-fragment"],
     prebuilts: ["current_sdkinfo"],
     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,
 }
 
 apex_key {
diff --git a/apex/apex_manifest.json b/apex/apex_manifest.json
index ffef8fb..59c4963 100644
--- a/apex/apex_manifest.json
+++ b/apex/apex_manifest.json
@@ -1,4 +1,4 @@
 {
   "name": "com.android.mediaprovider",
-  "version": 300000000
+  "version": 319999910
 }
diff --git a/apex/framework/Android.bp b/apex/framework/Android.bp
index 1d14eed..716f818 100644
--- a/apex/framework/Android.bp
+++ b/apex/framework/Android.bp
@@ -34,9 +34,12 @@
     ],
 
     installable: true,
-    min_sdk_version: "30",
 
-    libs: ["unsupportedappusage"],
+    libs: [
+        "androidx.annotation_annotation",
+        "framework-media.stubs.module_lib",
+        "unsupportedappusage",
+    ],
 
     hostdex: true, // for hiddenapi check
     impl_library_visibility: [
@@ -46,6 +49,7 @@
         "com.android.mediaprovider",
         "test_com.android.mediaprovider",
     ],
+    min_sdk_version: "30",
 }
 
 filegroup {
diff --git a/apex/framework/api/current.txt b/apex/framework/api/current.txt
index 52439fb..2e6312c 100644
--- a/apex/framework/api/current.txt
+++ b/apex/framework/api/current.txt
@@ -3,6 +3,7 @@
 
   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);
@@ -12,11 +13,15 @@
     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";
@@ -26,15 +31,18 @@
     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 public static final String EXTRA_MEDIA_PLAYLIST = "android.intent.extra.playlist";
+    field @Deprecated 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";
@@ -58,6 +66,7 @@
     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";
@@ -114,7 +123,7 @@
     field public static final android.net.Uri INTERNAL_CONTENT_URI;
   }
 
-  public static final class MediaStore.Audio.Artists.Albums implements android.provider.MediaStore.Audio.AlbumColumns {
+  public static final class MediaStore.Audio.Artists.Albums implements android.provider.BaseColumns android.provider.MediaStore.Audio.AlbumColumns {
     ctor public MediaStore.Audio.Artists.Albums();
     method public static android.net.Uri getContentUri(String, long);
   }
@@ -133,6 +142,7 @@
     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";
@@ -178,33 +188,33 @@
     field public static final String RECORD_SOUND_ACTION = "android.provider.MediaStore.RECORD_SOUND";
   }
 
-  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 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.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 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 interface MediaStore.Audio.PlaylistsColumns extends android.provider.MediaStore.MediaColumns {
+  @Deprecated public static interface MediaStore.Audio.PlaylistsColumns extends android.provider.MediaStore.MediaColumns {
     field @Deprecated public static final String DATA = "_data";
-    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";
+    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";
   }
 
   public static final class MediaStore.Audio.Radio {
@@ -236,7 +246,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 public static final int MEDIA_TYPE_PLAYLIST = 4; // 0x4
+    field @Deprecated 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";
diff --git a/apex/framework/api/system-current.txt b/apex/framework/api/system-current.txt
index 5ce4218..d29a6ed 100644
--- a/apex/framework/api/system-current.txt
+++ b/apex/framework/api/system-current.txt
@@ -8,6 +8,7 @@
     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/MediaStore.java b/apex/framework/java/android/provider/MediaStore.java
index c27e09f..6d2f520 100644
--- a/apex/framework/java/android/provider/MediaStore.java
+++ b/apex/framework/java/android/provider/MediaStore.java
@@ -29,6 +29,7 @@
 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;
@@ -39,20 +40,25 @@
 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.storage.StorageManager;
 import android.os.storage.StorageVolume;
@@ -62,6 +68,8 @@
 import android.util.Log;
 import android.util.Size;
 
+import androidx.annotation.RequiresApi;
+
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -77,6 +85,7 @@
 import java.util.Locale;
 import java.util.Objects;
 import java.util.Set;
+import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 /**
@@ -202,6 +211,13 @@
     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";
@@ -213,6 +229,16 @@
     /** {@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";
+
     /**
      * This is for internal use by the media scanner only.
      * Name of the (optional) Uri parameter that determines whether to skip deleting
@@ -331,7 +357,13 @@
     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.
@@ -446,13 +478,17 @@
      * 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
+     * <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
      * 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";
@@ -477,9 +513,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.
      *
      * @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 =
@@ -492,14 +532,20 @@
      * 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. 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)}.
+     * 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>
      *
      * <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
@@ -510,6 +556,7 @@
      * @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";
@@ -587,6 +634,64 @@
     public final static String EXTRA_OUTPUT = "output";
 
     /**
+     * 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.
@@ -620,6 +725,42 @@
     public static final String QUERY_ARG_ALLOW_MOVEMENT = "android:query-arg-allow-movement";
 
     /**
+     * Flag that indicates that a media scan that was triggered as part of
+     * {@link ContentResolver#update} should be asynchronous. This flag should
+     * 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.
+     *
+     * @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";
+
+    /**
      * Specify how {@link MediaColumns#IS_PENDING} items should be filtered when
      * performing a {@link MediaStore} operation.
      * <p>
@@ -763,6 +904,34 @@
     }
 
     /**
+     * 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}.
      *
@@ -992,26 +1161,23 @@
         /**
          * Absolute filesystem path to the media item on disk.
          * <p>
-         * 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.
+         * 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.
          * <p>
-         * 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
+         * 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
          * {@link #RELATIVE_PATH} columns.
          * <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.
+         * Though direct file operations are supported,
+         * {@link ContentResolver#openFileDescriptor(Uri, String)} API is recommended for better
+         * performance.
          *
-         * @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 Apps that target {@link android.os.Build.VERSION_CODES#R R} and higher
+         *             may not update the value of this column. However they may read the file path
+         *             value from this column and use in file operations.
          */
         @Deprecated
         @Column(Cursor.FIELD_TYPE_STRING)
@@ -1672,7 +1838,13 @@
             /**
              * 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;
 
             /**
@@ -1685,6 +1857,112 @@
              * Constant for the {@link #MEDIA_TYPE} column indicating that file is a document file.
              */
             public static final int MEDIA_TYPE_DOCUMENT = 6;
+
+            /**
+             * Constant indicating the count of {@link #MEDIA_TYPE} columns.
+             * @hide
+             */
+            public static final int MEDIA_TYPE_COUNT = 7;
+
+            /**
+             * Modifier of the database row
+             *
+             * Specifies the last modifying operation of the database row. This
+             * does not give any information on the package that modified the
+             * database row.
+             * Initially, this column will be populated by
+             * {@link ContentResolver}#insert and media scan operations. And,
+             * the column will be used to identify if the file was previously
+             * scanned.
+             * @hide
+             */
+            // @Column(value = Cursor.FIELD_TYPE_INTEGER)
+            public static final String _MODIFIER = "_modifier";
+
+            /**
+             * Constant for the {@link #_MODIFIER} column indicating
+             * that the last modifier of the database row is FUSE operation.
+             * @hide
+             */
+            public static final int _MODIFIER_FUSE = 1;
+
+            /**
+             * Constant for the {@link #_MODIFIER} column indicating
+             * that the last modifier of the database row is explicit
+             * {@link ContentResolver} operation from app.
+             * @hide
+             */
+            public static final int _MODIFIER_CR = 2;
+
+            /**
+             * Constant for the {@link #_MODIFIER} column indicating
+             * that the last modifier of the database row is a media scan
+             * operation.
+             * @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";
         }
     }
 
@@ -1794,6 +2072,13 @@
     }
 
     /**
+     * Regex that matches paths under well-known storage paths.
+     * Copied from FileUtils.java
+     */
+    private static final Pattern PATTERN_VOLUME_NAME = Pattern.compile(
+            "(?i)^/storage/([^/]+)");
+
+    /**
      * @deprecated since this method doesn't have a {@link Context}, we can't
      *             find the actual {@link StorageVolume} for the given path, so
      *             only a vague guess is returned. Callers should use
@@ -1804,9 +2089,15 @@
     public static @NonNull String getVolumeName(@NonNull File path) {
         // Ideally we'd find the relevant StorageVolume, but we don't have a
         // Context to obtain it from, so the best we can do is assume
-        if (path.getAbsolutePath()
-                .startsWith(Environment.getStorageDirectory().getAbsolutePath())) {
-            return MediaStore.VOLUME_EXTERNAL;
+        // Borrowed the logic from FileUtils.extractVolumeName
+        final Matcher matcher = PATTERN_VOLUME_NAME.matcher(path.getAbsolutePath());
+        if (matcher.find()) {
+            final String volumeName = matcher.group(1);
+            if (volumeName.equals("emulated")) {
+                return MediaStore.VOLUME_EXTERNAL_PRIMARY;
+            } else {
+                return volumeName.toLowerCase(Locale.ROOT);
+            }
         } else {
             return MediaStore.VOLUME_INTERNAL;
         }
@@ -1892,7 +2183,7 @@
             public static final String PICASA_ID = "picasa_id";
 
             /**
-             * Whether the video should be published as public or private
+             * Whether the image should be published as public or private
              */
             @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String IS_PRIVATE = "isprivate";
@@ -2321,21 +2612,13 @@
 
             /**
              * 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 Apps that target {@link android.os.Build.VERSION_CODES#R R} and higher
+             *             may not update the value of this column. However they may read the file
+             *             path value from this column and use in file operations.
              */
             @Deprecated
             @Column(Cursor.FIELD_TYPE_STRING)
@@ -2512,41 +2795,83 @@
 
             /**
              * 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)
@@ -2831,7 +3156,13 @@
 
         /**
          * 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
@@ -2841,18 +3172,10 @@
 
             /**
              * 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 Apps that target {@link android.os.Build.VERSION_CODES#R R} and higher
+             *             may not update the value of this column. However they may read the file
+             *             path value from this column and use in file operations.
              */
             @Deprecated
             @Column(Cursor.FIELD_TYPE_STRING)
@@ -2875,7 +3198,13 @@
 
         /**
          * 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 {
             /**
@@ -3074,7 +3403,7 @@
              * Sub-directory of each artist containing all albums on which
              * a song by the artist appears.
              */
-            public static final class Albums implements AlbumColumns {
+            public static final class Albums implements BaseColumns, AlbumColumns {
                 public static final Uri getContentUri(String volumeName,long artistId) {
                     return ContentUris
                             .withAppendedId(Audio.Artists.getContentUri(volumeName), artistId)
@@ -3261,18 +3590,10 @@
         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 Apps that target {@link android.os.Build.VERSION_CODES#R R} and higher
+             *             may not update the value of this column. However they may read the file
+             *             path value from this column and use in file operations.
              */
             @Deprecated
             @Column(Cursor.FIELD_TYPE_STRING)
@@ -3352,7 +3673,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 ExifInterface#getLatLong(float[])}.
+             *             {@link MediaMetadataRetriever#METADATA_KEY_LOCATION}.
              */
             @Deprecated
             @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
@@ -3364,7 +3685,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 ExifInterface#getLatLong(float[])}.
+             *             {@link MediaMetadataRetriever#METADATA_KEY_LOCATION}.
              */
             @Deprecated
             @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
@@ -3607,11 +3928,9 @@
             /**
              * 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 Apps that target {@link android.os.Build.VERSION_CODES#R R} and higher
+             *             may not update the value of this column. However they may read the file
+             *             path value from this column and use in file operations.
              */
             @Deprecated
             @Column(Cursor.FIELD_TYPE_STRING)
@@ -3893,13 +4212,16 @@
 
     /**
      * Return a {@link MediaStore} Uri that is an equivalent to the given
-     * {@link DocumentsProvider} Uri.
+     * {@link DocumentsProvider} Uri. This only supports {@code ExternalStorageProvider}
+     * and {@code MediaDocumentsProvider} Uris.
      * <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; callers must already hold permissions 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, 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.
      *
      * @param documentUri The {@link DocumentsProvider} Uri to convert.
      * @return An equivalent {@link MediaStore} Uri. Returns {@code null} if no
@@ -3921,6 +4243,94 @@
         }
     }
 
+    /**
+     * 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);
+    }
+
+    /**
+     * 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) {
+        try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY)) {
+            final Bundle in = new Bundle();
+            in.putParcelable(EXTRA_URI, uri);
+            final Bundle out = client.call(GET_REDACTED_MEDIA_URI_CALL, null, in);
+            return out.getParcelable(EXTRA_URI);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * 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}
+     * @see #getRedactedUri(ContentResolver, Uri)
+     */
+    @NonNull
+    public static List<Uri> getRedactedUri(@NonNull ContentResolver resolver,
+            @NonNull List<Uri> uris) {
+        try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY)) {
+            final Bundle in = new Bundle();
+            in.putParcelableArrayList(EXTRA_URI_LIST, (ArrayList<? extends Parcelable>) uris);
+            final Bundle out = client.call(GET_REDACTED_MEDIA_URI_LIST_CALL, null, in);
+            return out.getParcelableArrayList(EXTRA_URI_LIST);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
     /** {@hide} */
     public static void resolvePlaylistMembers(@NonNull ContentResolver resolver,
             @NonNull Uri playlistUri) {
@@ -3970,4 +4380,46 @@
     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/deploy.sh b/deploy.sh
index e6edcce..cc1be8e 100755
--- a/deploy.sh
+++ b/deploy.sh
@@ -8,6 +8,8 @@
 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/gen_strings.py b/gen_strings.py
index 99bba8a..a3d98be 100755
--- a/gen_strings.py
+++ b/gen_strings.py
@@ -31,12 +31,6 @@
     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())
@@ -49,6 +43,13 @@
     <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&#8230;</item>
+    <item quantity="other">Moving <xliff:g id="count" example="42">^1</xliff:g> ${datalabel}s to trash&#8230;</item>
+</plurals>
+''').substitute(vars()).strip("\n")
 
         elif verb == "untrash":
             print Template('''
@@ -58,6 +59,13 @@
     <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&#8230;</item>
+    <item quantity="other">Moving <xliff:g id="count" example="42">^1</xliff:g> ${datalabel}s out of trash&#8230;</item>
+</plurals>
+''').substitute(vars()).strip("\n")
 
         else:
             print Template('''
@@ -67,6 +75,17 @@
     <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&#8230;</item>
+    <item quantity="other">$actionLabel <xliff:g id="count" example="42">^1</xliff:g> ${datalabel}s&#8230;</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 9a7b604..758bee1 100644
--- a/jni/Android.bp
+++ b/jni/Android.bp
@@ -69,9 +69,9 @@
         "-google-runtime-int",
     ],
 
-    min_sdk_version: "30",
     sdk_version: "current",
     stl: "c++_static",
+    min_sdk_version: "30",
 }
 
 cc_test {
diff --git a/jni/FuseDaemon.cpp b/jni/FuseDaemon.cpp
index d1bfa93..f711405 100755
--- a/jni/FuseDaemon.cpp
+++ b/jni/FuseDaemon.cpp
@@ -17,6 +17,7 @@
 #define LIBFUSE_LOG_TAG "libfuse"
 
 #include "FuseDaemon.h"
+#include "android-base/strings.h"
 
 #include <android-base/logging.h>
 #include <android-base/properties.h>
@@ -78,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__)
@@ -105,10 +106,20 @@
 // Stolen from: UserHandle#getUserId
 constexpr int PER_USER_RANGE = 100000;
 
+// 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|sandbox)/([^/]+)(/?.*)?",
-    std::regex_constants::icase);
+        "^/storage/[^/]+/(?:[0-9]+/)?Android/(?:data|obb)/([^/]+)(/?.*)?",
+        std::regex_constants::icase);
+
+static constexpr char TRANSFORM_SYNTHETIC_DIR[] = "synthetic";
+static constexpr char TRANSFORM_TRANSCODE_DIR[] = "transcode";
 
 /*
  * In order to avoid double caching with fuse, call fadvise on the file handles
@@ -236,22 +247,26 @@
 
 /* Single FUSE mount */
 struct fuse {
-    explicit fuse(const std::string& _path)
+    explicit fuse(const std::string& _path, ino_t _ino)
         : path(_path),
           tracker(mediaprovider::fuse::NodeTracker(&lock)),
-          root(node::CreateRoot(_path, &lock, &tracker)),
+          root(node::CreateRoot(_path, &lock, _ino, &tracker)),
           mp(0),
-          zero_addr(0) {}
+          zero_addr(0),
+          disable_dentry_cache(false),
+          passthrough(false) {}
 
     inline bool IsRoot(const node* node) const { return node == root; }
 
     inline string GetEffectiveRootPath() {
-        if (path.find("/storage/emulated", 0) == 0) {
-            return path + "/" + std::to_string(getuid() / PER_USER_RANGE);
+        if (android::base::StartsWith(path, "/storage/emulated")) {
+            return path + "/" + MY_USER_ID_STRING;
         }
         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
@@ -295,8 +310,14 @@
     FAdviser fadviser;
 
     std::atomic_bool* active;
+    std::atomic_bool disable_dentry_cache;
+    std::atomic_bool passthrough;
+    // FUSE device id.
+    std::atomic_uint dev;
 };
 
+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() + " " : "";
@@ -371,7 +392,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, std::to_string(getuid() / PER_USER_RANGE))) {
+    if (mediaprovider::fuse::containsMount(path, MY_USER_ID_STRING)) {
         LOG(WARNING) << "Ignoring attempt to invalidate dentry for FUSE mounts";
         return;
     }
@@ -383,60 +404,168 @@
     }
 }
 
-static double get_timeout(struct fuse* fuse, const string& path, bool should_inval) {
+static double get_entry_timeout(const string& path, node* node, struct fuse* fuse) {
     string media_path = fuse->GetEffectiveRootPath() + "/Android/media";
-    if (should_inval || path.find(media_path, 0) == 0 || is_package_owned_path(path, fuse->path)) {
+    if (fuse->disable_dentry_cache || node->ShouldInvalidate() ||
+        is_package_owned_path(path, fuse->path) || android::base::StartsWith(path, media_path)) {
         // We set dentry timeout to 0 for the following reasons:
-        // 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.
+        // 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.
         // 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_transcode_supported_path(const string& path, struct fuse* fuse) {
+    // Keep in sync with MediaProvider#supportsTranscode
+    return android::base::EndsWithIgnoreCase(path, ".mp4") &&
+           android::base::StartsWithIgnoreCase(path,
+                                               fuse->GetEffectiveRootPath() + "/dcim/camera/");
+}
+
+static inline bool is_transforms_dir_path(const string& path, struct fuse* fuse) {
+    return android::base::StartsWithIgnoreCase(path, fuse->GetTransformsDir());
+}
+
+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 && !is_transcode_supported_path(path, fuse)) {
+        // 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) {
+                             struct fuse_entry_param* e, int* error_code, const FuseOp op) {
     struct fuse* fuse = get_fuse(req);
     const struct fuse_ctx* ctx = fuse_req_ctx(req);
     node* node;
 
     memset(e, 0, sizeof(*e));
-    if (lstat(path.c_str(), &e->attr) < 0) {
-        *error_code = errno;
-        return NULL;
+
+    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;
     }
 
-    bool should_inval = false;
-    node = parent->LookupChildByName(name, true /* acquire */);
+    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);
     if (!node) {
-        node = ::node::Create(parent, name, &fuse->lock, &fuse->tracker);
+        ino_t ino = e->attr.st_ino;
+        node = ::node::Create(parent, name, io_path, should_invalidate, transforms_complete,
+                              transforms, transforms_reason, &fuse->lock, ino, &fuse->tracker);
     } else if (!mediaprovider::fuse::containsMount(path, std::to_string(getuid() / PER_USER_RANGE))) {
-        should_inval = true;
         // 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)
-        // |should_inval| invalidates lookup_name by using 0 timeout below and we explicitly
+        // SetShouldInvalidate invalidates lookup_name by using 0 timeout below and we explicitly
         // invalidate node_name if different case
         // Note that we invalidate async otherwise we will deadlock the kernel
         if (name != node->GetName()) {
+            // Record that we have made a case insensitive lookup, this allows us invalidate nodes
+            // correctly on subsequent lookups for the case of |node|
+            node->SetShouldInvalidate();
+
             // Make copies of the node name and path so we're not attempting to acquire
             // any node locks from the invalidation thread. Depending on timing, we may end
             // up invalidating the wrong inode but that shouldn't result in correctness issues.
             const fuse_ino_t parent_ino = fuse->ToInode(parent);
             const fuse_ino_t child_ino = fuse->ToInode(node);
             const std::string& node_name = node->GetName();
-
             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);
 
@@ -446,11 +575,8 @@
     // reuse inode numbers.
     e->generation = 0;
     e->ino = fuse->ToInode(node);
-    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();
-
+    e->entry_timeout = get_entry_timeout(path, node, fuse);
+    e->attr_timeout = std::numeric_limits<double>::max();
     return node;
 }
 
@@ -469,15 +595,49 @@
  */
 
 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);
 }
 
@@ -490,7 +650,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) {
+    if (uid < AID_APP_START || uid == MY_UID) {
         return true;
     }
 
@@ -508,8 +668,15 @@
         if (pkg == ".nomedia") {
             return true;
         }
-        if (!mp->IsUidForPackage(pkg, uid)) {
-            PLOG(WARNING) << "Invalid other package file access from " << pkg << "(: " << path;
+        if (android::base::StartsWith(path, "/storage/emulated")) {
+            // Emulated storage bind-mounts app-private data directories, and so these
+            // should not be accessible through FUSE anyway.
+            LOG(WARNING) << "Rejected access to app-private dir on FUSE: " << path
+                         << " from uid: " << uid;
+            return false;
+        }
+        if (!mp->isUidAllowedAccessToDataOrObbPath(uid, path)) {
+            PLOG(WARNING) << "Invalid other package file access from " << uid << "(: " << path;
             return false;
         }
     }
@@ -518,7 +685,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) {
+                       struct fuse_entry_param* e, int* error_code, const FuseOp op) {
     struct fuse* fuse = get_fuse(req);
     node* parent_node = fuse->FromInode(parent);
     if (!parent_node) {
@@ -533,18 +700,25 @@
         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);
-    if (match.size() == 2 && std::to_string(getuid() / PER_USER_RANGE) != match[1].str()) {
-        // Ensure the FuseDaemon user id matches the user id in requested path
-        *error_code = EPERM;
-        return nullptr;
+
+    // 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 user id mismatch, check cross-user lookups
+        long userId = strtol(match[1].str().c_str(), nullptr, 10);
+        if (userId < 0 || userId > MAX_USER_ID ||
+            !fuse->mp->ShouldAllowLookup(req->ctx.uid, userId)) {
+            *error_code = EACCES;
+            return nullptr;
+        }
     }
-    return make_node_entry(req, parent_node, name, child_path, e, error_code);
+
+    return make_node_entry(req, parent_node, name, child_path, e, error_code, op);
 }
 
 static void pf_lookup(fuse_req_t req, fuse_ino_t parent, const char* name) {
@@ -552,7 +726,7 @@
     struct fuse_entry_param e;
 
     int error_code = 0;
-    if (do_lookup(req, parent, name, &e, &error_code)) {
+    if (do_lookup(req, parent, name, &e, &error_code, FuseOp::lookup)) {
         fuse_reply_entry(req, &e);
     } else {
         CHECK(error_code != 0);
@@ -593,6 +767,16 @@
     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) {
@@ -603,7 +787,7 @@
         fuse_reply_err(req, ENOENT);
         return;
     }
-    string path = node->BuildPath();
+    const string& path = get_path(node);
     if (!is_app_accessible_path(fuse->mp, path, req->ctx.uid)) {
         fuse_reply_err(req, ENOENT);
         return;
@@ -615,8 +799,7 @@
     if (lstat(path.c_str(), &s) < 0) {
         fuse_reply_err(req, errno);
     } else {
-        fuse_reply_attr(req, &s, is_package_owned_path(path, fuse->path) ?
-                0 : std::numeric_limits<double>::max());
+        fuse_reply_attr(req, &s, std::numeric_limits<double>::max());
     }
 }
 
@@ -632,7 +815,7 @@
         fuse_reply_err(req, ENOENT);
         return;
     }
-    string path = node->BuildPath();
+    const string& path = get_path(node);
     if (!is_app_accessible_path(fuse->mp, path, req->ctx.uid)) {
         fuse_reply_err(req, ENOENT);
         return;
@@ -645,8 +828,16 @@
         fd = h->fd;
     } else {
         const struct fuse_ctx* ctx = fuse_req_ctx(req);
-        int status = fuse->mp->IsOpenAllowed(path, ctx->uid, true);
-        if (status) {
+        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) {
             fuse_reply_err(req, EACCES);
             return;
         }
@@ -711,15 +902,14 @@
     }
 
     lstat(path.c_str(), attr);
-    fuse_reply_attr(req, attr, is_package_owned_path(path, fuse->path) ?
-            0 : std::numeric_limits<double>::max());
+    fuse_reply_attr(req, attr, 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);
-    string path = node ? node->BuildPath() : "";
+    const string& path = node ? get_path(node) : "";
 
     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
@@ -759,7 +949,7 @@
 
     int error_code = 0;
     struct fuse_entry_param e;
-    if (make_node_entry(req, parent_node, name, child_path, &e, &error_code)) {
+    if (make_node_entry(req, parent_node, name, child_path, &e, &error_code, FuseOp::mknod)) {
         fuse_reply_entry(req, &e);
     } else {
         CHECK(error_code != 0);
@@ -803,7 +993,7 @@
 
     int error_code = 0;
     struct fuse_entry_param e;
-    if (make_node_entry(req, parent_node, name, child_path, &e, &error_code)) {
+    if (make_node_entry(req, parent_node, name, child_path, &e, &error_code, FuseOp::mkdir)) {
         fuse_reply_entry(req, &e);
     } else {
         CHECK(error_code != 0);
@@ -836,12 +1026,8 @@
         return;
     }
 
-    node* child_node = parent_node->LookupChildByName(name, false /* acquire */);
-    TRACE_NODE(child_node, req);
-    if (child_node) {
-        child_node->SetDeleted();
-    }
-
+    // TODO(b/169306422): Log each deleted node
+    parent_node->SetDeletedForChild(name);
     fuse_reply_err(req, 0);
 }
 
@@ -858,6 +1044,14 @@
         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;
@@ -905,6 +1099,12 @@
         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();
@@ -922,22 +1122,22 @@
     TRACE_NODE(old_parent_node, req);
     TRACE_NODE(new_parent_node, req);
 
-    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 old_child_path = old_parent_path + "/" + name;
     const string new_child_path = new_parent_path + "/" + new_name;
 
+    if (android::base::EqualsIgnoreCase(fuse->GetEffectiveRootPath() + "/android", old_child_path)) {
+        // Prevent renaming Android/ dir since it contains bind-mounts on the primary volume
+        return EACCES;
+    }
+
     // TODO(b/147408834): Check ENOTEMPTY & EEXIST error conditions before JNI call.
     const int res = fuse->mp->Rename(old_child_path, new_child_path, req->ctx.uid);
     // 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) {
-        child_node->Rename(new_name, new_parent_node);
+        // TODO(b/169306422): Log each renamed node
+        old_parent_node->RenameChild(name, new_name, new_parent_node);
     }
-    TRACE_NODE(child_node, req) << "new_child";
-
-    child_node->Release(1);
     return res;
 }
 
@@ -955,27 +1155,76 @@
 }
 */
 
-static handle* create_handle_for_node(struct fuse* fuse, const string& path, int fd, node* node,
-                                      const RedactionInfo* ri) {
+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) {
     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 direct_io = ri->isRedactionNeeded() || is_file_locked(fd, path);
 
-    handle* h = new handle(fd, ri, !direct_io);
-    node->AddHandle(h);
-    return h;
+    bool redaction_needed = ri->isRedactionNeeded();
+    handle* handle = nullptr;
+    int transforms = node->GetTransforms();
+    bool transforms_complete = node->IsTransformsComplete();
+    if (transforms_uid > 0) {
+        CHECK(transforms);
+    }
+
+    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);
+    } 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);
+    }
+
+    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;
 }
 
 static void pf_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) {
@@ -987,22 +1236,39 @@
         return;
     }
     const struct fuse_ctx* ctx = fuse_req_ctx(req);
-    const string path = node->BuildPath();
-    if (!is_app_accessible_path(fuse->mp, path, ctx->uid)) {
+    const string& io_path = get_path(node);
+    const string& build_path = node->BuildPath();
+    if (!is_app_accessible_path(fuse->mp, io_path, ctx->uid)) {
         fuse_reply_err(req, ENOENT);
         return;
     }
 
-    TRACE_NODE(node, req) << (is_requesting_write(fi->flags) ? "write" : "read");
+    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");
+    }
 
     if (fi->flags & O_DIRECT) {
         fi->flags &= ~O_DIRECT;
         fi->direct_io = true;
     }
 
-    int status = fuse->mp->IsOpenAllowed(path, ctx->uid, is_requesting_write(fi->flags));
-    if (status) {
-        fuse_reply_err(req, status);
+    // 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);
         return;
     }
 
@@ -1018,30 +1284,31 @@
         open_flags &= ~O_APPEND;
     }
 
-    const int fd = open(path.c_str(), open_flags);
+    const int fd = open(io_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;
-    }
-
-    handle* h = create_handle_for_node(fuse, path, fd, node, ri.release());
+    int keep_cache = 1;
+    handle* h = create_handle_for_node(fuse, io_path, fd, result->uid, result->transforms_uid, node,
+                                       result->redaction_info.release(), &keep_cache);
     fi->fh = ptr_to_id(h);
-    fi->keep_cache = 1;
+    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);
 }
 
@@ -1057,10 +1324,6 @@
     fuse_reply_data(req, &buf, (enum fuse_buf_copy_flags) 0);
 }
 
-static bool range_contains(const RedactionRange& rr, off_t off) {
-    return rr.first <= off && off <= rr.second;
-}
-
 /**
  * Sets the parameters for a fuse_buf that reads from memory, including flags.
  * Makes buf->mem point to an already mapped region of zeroized memory.
@@ -1087,24 +1350,17 @@
 
 static void do_read_with_redaction(fuse_req_t req, size_t size, off_t off, fuse_file_info* fi) {
     handle* h = reinterpret_cast<handle*>(fi->fh);
-    auto overlapping_rr = h->ri->getOverlappingRedactionRanges(size, off);
 
-    if (overlapping_rr->size() <= 0) {
-        // no relevant redaction ranges for this request
+    std::vector<ReadRange> ranges;
+    h->ri->getReadRanges(off, size, &ranges);
+
+    // As an optimization, return early if there are no ranges to redact.
+    if (ranges.size() == 0) {
         do_read(req, size, off, fi);
         return;
     }
-    // the number of buffers we need, if the read doesn't start or end with
-    //  a redaction range.
-    int num_bufs = overlapping_rr->size() * 2 + 1;
-    if (overlapping_rr->front().first <= off) {
-        // the beginning of the read request is redacted
-        num_bufs--;
-    }
-    if (overlapping_rr->back().second >= off + size) {
-        // the end of the read request is redacted
-        num_bufs--;
-    }
+
+    const size_t num_bufs = ranges.size();
     auto bufvec_ptr = std::unique_ptr<fuse_bufvec, decltype(free)*>{
             reinterpret_cast<fuse_bufvec*>(
                     malloc(sizeof(fuse_bufvec) + (num_bufs - 1) * sizeof(fuse_buf))),
@@ -1116,31 +1372,13 @@
     bufvec.idx = 0;
     bufvec.off = 0;
 
-    int rr_idx = 0;
-    off_t start = off;
-    // Add a dummy redaction range to make sure we don't go out of vector
-    // limits when computing the end of the last non-redacted range.
-    // This ranges is invalid because its starting point is larger than it's ending point.
-    overlapping_rr->push_back(RedactionRange(LLONG_MAX, LLONG_MAX - 1));
-
     for (int i = 0; i < num_bufs; ++i) {
-        off_t end;
-        if (range_contains(overlapping_rr->at(rr_idx), start)) {
-            // Handle a redacted range
-            // end should be the end of the redacted range, but can't be out of
-            // the read request bounds
-            end = std::min(static_cast<off_t>(off + size - 1), overlapping_rr->at(rr_idx).second);
-            create_mem_fuse_buf(/*size*/ end - start + 1, &(bufvec.buf[i]), get_fuse(req));
-            ++rr_idx;
+        const ReadRange& range = ranges[i];
+        if (range.is_redaction) {
+            create_mem_fuse_buf(range.size, &(bufvec.buf[i]), get_fuse(req));
         } else {
-            // Handle a non-redacted range
-            // end should be right before the next redaction range starts or
-            // the end of the read request
-            end = std::min(static_cast<off_t>(off + size - 1),
-                    overlapping_rr->at(rr_idx).first - 1);
-            create_file_fuse_buf(/*size*/ end - start + 1, start, h->fd, &(bufvec.buf[i]));
+            create_file_fuse_buf(range.size, range.start, h->fd, &(bufvec.buf[i]));
         }
-        start = end + 1;
     }
 
     fuse_reply_data(req, &bufvec, static_cast<fuse_buf_copy_flags>(0));
@@ -1152,6 +1390,18 @@
     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()) {
@@ -1189,8 +1439,10 @@
     if (size < 0)
         fuse_reply_err(req, -size);
     else {
-        fuse_reply_write(req, size);
+        // Execute Record *before* fuse_reply_write to avoid the following ordering:
+        // fuse_reply_write -> pf_release (destroy handle) -> Record (use handle after free)
         fuse->fadviser.Record(h->fd, size);
+        fuse_reply_write(req, size);
     }
 }
 // Haven't tested this one. Not sure what calls it.
@@ -1223,14 +1475,6 @@
     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,
@@ -1375,7 +1619,7 @@
         h->next_off++;
         if (plus) {
             int error_code = 0;
-            if (do_lookup(req, ino, de->d_name.c_str(), &e, &error_code)) {
+            if (do_lookup(req, ino, de->d_name.c_str(), &e, &error_code, FuseOp::readdir)) {
                 entry_size = fuse_add_direntry_plus(req, buf + used, len - used, de->d_name.c_str(),
                                                     &e, h->next_off);
             } else {
@@ -1527,7 +1771,14 @@
             return;
         }
 
-        status = fuse->mp->IsOpenAllowed(path, req->ctx.uid, for_write);
+        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;
+        }
     }
 
     fuse_reply_err(req, status);
@@ -1586,7 +1837,8 @@
 
     int error_code = 0;
     struct fuse_entry_param e;
-    node* node = make_node_entry(req, parent_node, name, child_path, &e, &error_code);
+    node* node =
+            make_node_entry(req, parent_node, name, child_path, &e, &error_code, FuseOp::create);
     TRACE_NODE(node, req);
     if (!node) {
         CHECK(error_code != 0);
@@ -1601,10 +1853,24 @@
     // This prevents crashing during reads but can be a security hole if a malicious app opens an fd
     // 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.
-    handle* h = create_handle_for_node(fuse, child_path, fd, node, new RedactionInfo());
+    int keep_cache = 1;
+    handle* h = create_handle_for_node(fuse, child_path, fd, req->ctx.uid, 0 /* transforms_uid */,
+                                       node, new RedactionInfo(), &keep_cache);
     fi->fh = ptr_to_id(h);
-    fi->keep_cache = 1;
+    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);
 }
 /*
@@ -1668,7 +1934,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,
@@ -1684,8 +1950,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,*/
 };
@@ -1711,6 +1977,13 @@
 }
 
 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)) {
@@ -1787,7 +2060,7 @@
         return;
     }
 
-    struct fuse fuse_default(path);
+    struct fuse fuse_default(path, stat.st_ino);
     fuse_default.mp = &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
@@ -1807,6 +2080,16 @@
         fuse_set_log_func(fuse_logger);
     }
 
+    if (MY_USER_ID != 0 && mp.IsAppCloneUser(MY_USER_ID)) {
+        // 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) {
@@ -1835,5 +2118,42 @@
     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 3c4b947..f7c5614 100644
--- a/jni/FuseDaemon.h
+++ b/jni/FuseDaemon.h
@@ -54,6 +54,16 @@
      */
     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/MediaProviderWrapper.cpp b/jni/MediaProviderWrapper.cpp
index 04c2503..9f8a759 100644
--- a/jni/MediaProviderWrapper.cpp
+++ b/jni/MediaProviderWrapper.cpp
@@ -56,29 +56,17 @@
     return false;
 }
 
-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()));
-    ScopedLongArrayRO redaction_ranges(
-            env, static_cast<jlongArray>(env->CallObjectMethod(
-                         media_provider_object, mid_get_redaction_ranges, j_path.get(), uid, tid)));
-
-    if (CheckForJniException(env)) {
-        return nullptr;
+/**
+ * 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> 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;
+    return fid;
 }
 
 int insertFileInternal(JNIEnv* env, jobject media_provider_object, jmethodID mid_insert_file,
@@ -103,25 +91,6 @@
     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;
-}
-
-void scanFileInternal(JNIEnv* env, jobject media_provider_object, jmethodID mid_scan_file,
-                      const string& path) {
-    ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
-    env->CallVoidMethod(media_provider_object, mid_scan_file, j_path.get());
-    CheckForJniException(env);
-}
-
 int isMkdirOrRmdirAllowedInternal(JNIEnv* env, jobject media_provider_object,
                                   jmethodID mid_is_mkdir_or_rmdir_allowed, const string& path,
                                   uid_t uid, bool forCreate) {
@@ -148,11 +117,12 @@
     return res;
 }
 
-bool isUidForPackageInternal(JNIEnv* env, jobject media_provider_object,
-                             jmethodID mid_is_uid_for_package, const string& pkg, uid_t uid) {
-    ScopedLocalRef<jstring> j_pkg(env, env->NewStringUTF(pkg.c_str()));
-    bool res = env->CallBooleanMethod(media_provider_object, mid_is_uid_for_package, j_pkg.get(),
-            uid);
+bool isUidAllowedAccessToDataOrObbPathInternal(JNIEnv* env, jobject media_provider_object,
+                                               jmethodID mid_is_uid_allowed_path_access_, uid_t uid,
+                                               const string& path) {
+    ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
+    bool res = env->CallBooleanMethod(media_provider_object, mid_is_uid_allowed_path_access_, uid,
+                                      j_path.get());
 
     if (CheckForJniException(env)) {
         return false;
@@ -258,15 +228,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_is_open_allowed_ = CacheMethod(env, "isOpenAllowed", "(Ljava/lang/String;IZ)I",
-                                       /*is_static*/ false);
-    mid_scan_file_ = CacheMethod(env, "scanFile", "(Ljava/lang/String;)V",
-                                 /*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_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",
@@ -276,10 +244,51 @@
                         /*is_static*/ false);
     mid_rename_ = CacheMethod(env, "rename", "(Ljava/lang/String;Ljava/lang/String;I)I",
                               /*is_static*/ false);
-    mid_is_uid_for_package_ = CacheMethod(env, "isUidForPackage", "(Ljava/lang/String;I)Z",
-                              /*is_static*/ false);
+    mid_is_uid_allowed_access_to_data_or_obb_path_ =
+            CacheMethod(env, "isUidAllowedAccessToDataOrObbPath", "(ILjava/lang/String;)Z",
+                        /*is_static*/ false);
     mid_on_file_created_ = CacheMethod(env, "onFileCreated", "(Ljava/lang/String;)V",
                                        /*is_static*/ false);
+    mid_should_allow_lookup_ = CacheMethod(env, "shouldAllowLookup", "(II)Z",
+                                           /*is_static*/ false);
+    mid_is_app_clone_user_ = CacheMethod(env, "isAppCloneUser", "(I)Z",
+                                         /*is_static*/ false);
+    mid_transform_ = CacheMethod(env, "transform", "(Ljava/lang/String;Ljava/lang/String;IIIII)Z",
+                                 /*is_static*/ false);
+    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() {
@@ -288,23 +297,6 @@
     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;
@@ -324,19 +316,53 @@
     return deleteFileInternal(env, media_provider_object_, mid_delete_file_, path, uid);
 }
 
-int MediaProviderWrapper::IsOpenAllowed(const string& path, uid_t uid, bool for_write) {
+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();
     if (shouldBypassMediaProvider(uid)) {
-        return 0;
+        return std::make_unique<FileOpenResult>(0, uid, 0 /* transforms_uid */, new RedactionInfo());
     }
 
-    JNIEnv* env = MaybeAttachCurrentThread();
-    return isOpenAllowedInternal(env, media_provider_object_, mid_is_open_allowed_, path, uid,
-                                 for_write);
-}
+    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));
 
-void MediaProviderWrapper::ScanFile(const string& path) {
-    JNIEnv* env = MaybeAttachCurrentThread();
-    scanFileInternal(env, media_provider_object_, mid_scan_file_, path);
+    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());
+    }
 }
 
 int MediaProviderWrapper::IsCreatingDirAllowed(const string& path, uid_t uid) {
@@ -395,13 +421,14 @@
                                     forWrite);
 }
 
-bool MediaProviderWrapper::IsUidForPackage(const string& pkg, uid_t uid) {
+bool MediaProviderWrapper::isUidAllowedAccessToDataOrObbPath(uid_t uid, const string& path) {
     if (shouldBypassMediaProvider(uid)) {
         return true;
     }
 
     JNIEnv* env = MaybeAttachCurrentThread();
-    return isUidForPackageInternal(env, media_provider_object_, mid_is_uid_for_package_, pkg, uid);
+    return isUidAllowedAccessToDataOrObbPathInternal(
+            env, media_provider_object_, mid_is_uid_allowed_access_to_data_or_obb_path_, uid, path);
 }
 
 int MediaProviderWrapper::Rename(const string& old_path, const string& new_path, uid_t uid) {
@@ -423,6 +450,80 @@
     return onFileCreatedInternal(env, media_provider_object_, mid_on_file_created_, path);
 }
 
+bool MediaProviderWrapper::ShouldAllowLookup(uid_t uid, int path_user_id) {
+    JNIEnv* env = MaybeAttachCurrentThread();
+
+    bool res = env->CallBooleanMethod(media_provider_object_, mid_should_allow_lookup_, uid,
+                                      path_user_id);
+
+    if (CheckForJniException(env)) {
+        return false;
+    }
+    return res;
+}
+
+bool MediaProviderWrapper::IsAppCloneUser(uid_t userId) {
+    JNIEnv* env = MaybeAttachCurrentThread();
+
+    bool res = env->CallBooleanMethod(media_provider_object_, mid_is_app_clone_user_, userId);
+
+    if (CheckForJniException(env)) {
+        return false;
+    }
+    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 *******************************/
 /*****************************************************************************************/
@@ -441,7 +542,6 @@
         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 1f74acc..2ad1769 100644
--- a/jni/MediaProviderWrapper.h
+++ b/jni/MediaProviderWrapper.h
@@ -17,6 +17,7 @@
 #ifndef MEDIAPROVIDER_FUSE_MEDIAPROVIDERWRAPPER_H_
 #define MEDIAPROVIDER_FUSE_MEDIAPROVIDERWRAPPER_H_
 
+#include <android-base/logging.h>
 #include <jni.h>
 #include <sys/types.h>
 
@@ -35,6 +36,48 @@
 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.
@@ -48,11 +91,14 @@
      * 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
+     * @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
      * @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, uid_t uid, pid_t tid);
+    std::unique_ptr<RedactionInfo> GetRedactionInfo(const std::string& path,
+                                                    const std::string& io_path, uid_t uid,
+                                                    pid_t tid);
 
     /**
      * Inserts a new entry for the given path and UID.
@@ -93,20 +139,20 @@
     /**
      * Determines if the given UID is allowed to open the file denoted by the given path.
      *
-     * @param path the path of the file to be opened
-     * @param uid UID of the calling app
-     * @param for_write specifies if the file is to be opened for write
-     * @return 0 upon success or errno value upon failure.
-     */
-    int IsOpenAllowed(const std::string& path, uid_t uid, bool for_write);
-
-    /**
-     * Potentially triggers a scan of the file before closing it and reconciles it with the
-     * MediaProvider database.
+     * Also computes and returns the RedactionInfo for a given file and |uid|
      *
-     * @param path the path of the file to be scanned
+     * @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 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
      */
-    void ScanFile(const std::string& path);
+    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);
 
     /**
      * Determines if the given UID is allowed to create a directory with the given path.
@@ -137,13 +183,19 @@
     int IsOpendirAllowed(const std::string& path, uid_t uid, bool forWrite);
 
     /**
-     * Determines if the given package name matches its uid.
+     * Determines if one of the follows is true:
+     * 1. The package name of the given private path matches the given uid,
+          then this uid has access to private-app directories for this package.
+     * 2. The calling uid has special access to private-app directories:
+     *    * DownloadProvider and ExternalStorageProvider has access to private
+     *      app directories.
+     *    * Installer apps have access to Android/obb directories
      *
-     * @param pkg the package name of the app
      * @param uid UID of the app
+     * @param path the private path that the UID wants to access
      * @return true if it matches, otherwise return false.
      */
-    bool IsUidForPackage(const std::string& pkg, uid_t uid);
+    bool isUidAllowedAccessToDataOrObbPath(uid_t uid, const std::string& path);
 
     /**
      * Renames a file or directory to new path.
@@ -165,6 +217,30 @@
     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.
+     *
+     * @param uid linux uid to check
+     */
+    bool ShouldAllowLookup(uid_t uid, int path_user_id);
+
+    /**
+     * Determines if the passed in user ID is an app clone user (paired with user 0)
+     *
+     * @param userId the user ID to check
+     */
+    bool IsAppCloneUser(uid_t userId);
+
+    /**
      * Initializes per-process static variables associated with the lifetime of
      * a managed runtime.
      */
@@ -174,20 +250,37 @@
     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_is_open_allowed_;
+    jmethodID mid_on_file_open_;
     jmethodID mid_scan_file_;
     jmethodID mid_is_mkdir_or_rmdir_allowed_;
     jmethodID mid_is_opendir_allowed_;
     jmethodID mid_get_files_in_dir_;
     jmethodID mid_rename_;
-    jmethodID mid_is_uid_for_package_;
+    jmethodID mid_is_uid_allowed_access_to_data_or_obb_path_;
     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/RedactionInfo.cpp b/jni/RedactionInfo.cpp
index 17de22e..384e59f 100644
--- a/jni/RedactionInfo.cpp
+++ b/jni/RedactionInfo.cpp
@@ -16,6 +16,8 @@
 
 #include "include/libfuse_jni/RedactionInfo.h"
 
+#include <android-base/logging.h>
+
 using std::unique_ptr;
 using std::vector;
 
@@ -28,6 +30,7 @@
  * Given ranges should be sorted, and they remain sorted.
  */
 static void mergeOverlappingRedactionRanges(vector<RedactionRange>& ranges) {
+    if (ranges.size() == 0) return;
     int newRangesSize = ranges.size();
     for (int i = 0; i < ranges.size() - 1; ++i) {
         if (ranges[i].second >= ranges[i + 1].first) {
@@ -47,14 +50,37 @@
 }
 
 /**
+ * Removes any range with zero size.
+ *
+ * If ranges are modified, it will be guaranteed to be sorted.
+ */
+static void removeZeroSizeRedactionRanges(vector<RedactionRange>& ranges) {
+    int newRangesSize = ranges.size();
+    for (int i = 0; i < ranges.size(); ++i) {
+        if (ranges[i].first == ranges[i].second) {
+            // This redaction range is of length zero, hence we don't have anything
+            // to redact in this range, so remove it from the redaction_ranges_.
+            ranges[i].first = LONG_MAX;
+            ranges[i].second = LONG_MAX;
+            newRangesSize--;
+        }
+    }
+    if (newRangesSize < ranges.size()) {
+        // Move invalid ranges to end of array
+        std::sort(ranges.begin(), ranges.end());
+        ranges.resize(newRangesSize);
+    }
+}
+
+/**
  * Determine whether the read request overlaps with the redaction ranges
  * defined by the given RedactionInfo.
  *
  * This function assumes redaction_ranges_ within RedactionInfo is sorted.
  */
 bool RedactionInfo::hasOverlapWithReadRequest(size_t size, off64_t off) const {
-    if (!isRedactionNeeded() || off > redaction_ranges_.back().second ||
-        off + size < redaction_ranges_.front().first) {
+    if (!isRedactionNeeded() || off >= redaction_ranges_.back().second ||
+        off + size <= redaction_ranges_.front().first) {
         return false;
     }
     return true;
@@ -72,6 +98,7 @@
         redaction_ranges_[i].second = static_cast<off64_t>(redaction_ranges[2 * i + 1]);
     }
     std::sort(redaction_ranges_.begin(), redaction_ranges_.end());
+    removeZeroSizeRedactionRanges(redaction_ranges_);
     mergeOverlappingRedactionRanges(redaction_ranges_);
 }
 
@@ -91,26 +118,90 @@
 unique_ptr<vector<RedactionRange>> RedactionInfo::getOverlappingRedactionRanges(size_t size,
                                                                                 off64_t off) const {
     if (hasOverlapWithReadRequest(size, off)) {
+        const off64_t start = off;
+        const off64_t end = static_cast<off64_t>(off + size);
+
         auto first_redaction = redaction_ranges_.end();
-        auto last_redaction = redaction_ranges_.end();
+        auto last_redaction = redaction_ranges_.begin();
         for (auto iter = redaction_ranges_.begin(); iter != redaction_ranges_.end(); ++iter) {
-            const RedactionRange& rr = *iter;
-            // Look for the first range that overlaps with the read request
-            if (first_redaction == redaction_ranges_.end() && off <= rr.second &&
-                off + size >= rr.first) {
-                first_redaction = iter;
-            } else if (first_redaction != redaction_ranges_.end() && off + size < rr.first) {
-                // Once we're in the read request range, we start checking if
-                // we're out of it so we can return the result to the caller
+            if (iter->second > start && iter->first < end) {
+                if (iter < first_redaction) first_redaction = iter;
+                if (iter > last_redaction) last_redaction = iter;
+            }
+
+            if (iter->first >= end) {
                 break;
             }
-            last_redaction = iter;
         }
+
         if (first_redaction != redaction_ranges_.end()) {
+            CHECK(first_redaction <= last_redaction);
             return std::make_unique<vector<RedactionRange>>(first_redaction, last_redaction + 1);
         }
     }
     return std::make_unique<vector<RedactionRange>>();
 }
+
+void RedactionInfo::getReadRanges(off64_t off, size_t size, std::vector<ReadRange>* out) const {
+    const auto rr = getOverlappingRedactionRanges(size, off);
+    const size_t num_ranges = rr->size();
+    if (num_ranges == 0) {
+        return;
+    }
+
+    const off64_t read_start = off;
+    const off64_t read_end = static_cast<off64_t>(read_start + size);
+
+    // The algorithm for computing redaction ranges is very simple.
+    // Given a set of overlapping redaction ranges [s1, e1) [s2, e2) .. [sN, eN) for a read
+    // [s, e)
+    //
+    // We can construct a series of indices that we know will be the starts of every read range
+    // that we intend to return. Then, it's relatively simple to compute the lengths of the ranges.
+    // Also note that the read ranges we return always alternate in whether they're redacting or
+    // not. i.e, we will never return two consecutive redacting ranges or non redacting ranges.
+    std::vector<off64_t> sorted_indices;
+
+    // Compute the list of indices -- this list will always contain { e1, s2, e2... sN }
+    // In addition, it may contain s or both (s and s1), depending on the start index.
+    // In addition, it may contain e or both (e and eN), depending on the end index.
+    //
+    // For a concrete example, consider ranges [10, 20) and [30, 40)
+    // For a read [0, 60) : sorted_indices will be { 0, 10, 20, 30, 40, 60 } is_first = false
+    // For a read [15, 60) : sorted_indices will be { 15, 20, 30, 40, 60 } is_first = true
+    // For a read [0, 35) : sorted_indices will be { 0, 10, 20, 30, 35 } is_first = false
+    // For a read [15, 35) : sorted_indices will be { 15, 20, 30, 35 } is_first = true
+    for (int i = 0; i < num_ranges; ++i) {
+        sorted_indices.push_back(rr->at(i).first);
+        sorted_indices.push_back(rr->at(i).second);
+    }
+
+    // Find the right position for read_start in sorted_indices
+    // Either insert at the beginning or replace s1 with read_start
+    bool is_first_range_redaction = true;
+    if (read_start < rr->at(0).first) {
+        is_first_range_redaction = false;
+        sorted_indices.insert(sorted_indices.begin(), read_start);
+    } else {
+        sorted_indices.front() = read_start;
+    }
+
+    // Find the right position for read_end in sorted_indices
+    // Either insert at the end or replace eN with read_end
+    if (read_end > rr->at(num_ranges - 1).second) {
+        sorted_indices.push_back(read_end);
+    } else {
+        sorted_indices.back() = read_end;
+    }
+
+    bool is_redaction = is_first_range_redaction;
+    for (int i = 0; i < (sorted_indices.size() - 1); ++i) {
+        const off64_t read_size = sorted_indices[i + 1] - sorted_indices[i];
+        CHECK(read_size > 0);
+        out->push_back(ReadRange(sorted_indices[i], read_size, is_redaction));
+        is_redaction = !is_redaction;
+    }
+}
+
 }  // namespace fuse
 }  // namespace mediaprovider
diff --git a/jni/RedactionInfoTest.cpp b/jni/RedactionInfoTest.cpp
index 9d98058..76eec13 100644
--- a/jni/RedactionInfoTest.cpp
+++ b/jni/RedactionInfoTest.cpp
@@ -19,6 +19,7 @@
 #include <gtest/gtest.h>
 
 #include <memory>
+#include <ostream>
 #include <vector>
 
 #include "libfuse_jni/RedactionInfo.h"
@@ -28,249 +29,355 @@
 using std::unique_ptr;
 using std::vector;
 
-unique_ptr<vector<RedactionRange>> createRedactionRangeVector(int num_rr, off64_t* rr) {
-    auto res = std::make_unique<vector<RedactionRange>>();
-    for (int i = 0; i < num_rr; ++i) {
-        res->push_back(RedactionRange(rr[2 * i], rr[2 * i + 1]));
-    }
-    return res;
+std::ostream& operator<<(std::ostream& os, const ReadRange& rr) {
+    os << "{ " << rr.start << ", " << rr.size << ", " << rr.is_redaction << " }";
+    return os;
 }
 
-/**
- * Test the case where there are no redaction ranges.
- */
 TEST(RedactionInfoTest, testNoRedactionRanges) {
     RedactionInfo info(0, nullptr);
     EXPECT_EQ(0, info.size());
     EXPECT_EQ(false, info.isRedactionNeeded());
 
-    auto overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 1000, /*off*/ 1000);
-    EXPECT_EQ(0, overlapping_rr->size());
+    std::vector<ReadRange> out;
+    info.getReadRanges(0, std::numeric_limits<size_t>::max(), &out);
+    EXPECT_EQ(0, out.size());
 }
 
-/**
- * Test the case where there is 1 redaction range.
- */
+// Test the case where there is 1 redaction range.
 TEST(RedactionInfoTest, testSingleRedactionRange) {
     off64_t ranges[2] = {
             1,
             10,
     };
+
     RedactionInfo info(1, ranges);
     EXPECT_EQ(1, info.size());
     EXPECT_EQ(true, info.isRedactionNeeded());
+
     // Overlapping ranges
-    auto overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 1000, /*off*/ 0);
-    EXPECT_EQ(*(createRedactionRangeVector(1, ranges)), *overlapping_rr);
+    std::vector<ReadRange> out;
+    info.getReadRanges(0, 1000, &out);  // read offsets [0, 1000)
+    EXPECT_EQ(3, out.size());
+    EXPECT_EQ(ReadRange(0, 1, false), out[0]);
+    EXPECT_EQ(ReadRange(1, 9, true), out[1]);
+    EXPECT_EQ(ReadRange(10, 990, false), out[2]);
 
-    overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 5, /*off*/ 0);
-    EXPECT_EQ(*(createRedactionRangeVector(1, ranges)), *overlapping_rr);
+    out.clear();
+    info.getReadRanges(0, 5, &out);  // read offsets [0, 5)
+    EXPECT_EQ(2, out.size());
+    EXPECT_EQ(ReadRange(0, 1, false), out[0]);  // offsets: [0, 1) len = 1
+    EXPECT_EQ(ReadRange(1, 4, true), out[1]);   // offsets: [1, 5) len = 4
 
-    overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 5, /*off*/ 5);
-    EXPECT_EQ(*(createRedactionRangeVector(1, ranges)), *overlapping_rr);
+    out.clear();
+    info.getReadRanges(1, 10, &out);  // read offsets [1, 11)
+    EXPECT_EQ(2, out.size());
+    EXPECT_EQ(ReadRange(1, 9, true), out[0]);    // offsets: [1, 10) len = 9
+    EXPECT_EQ(ReadRange(10, 1, false), out[1]);  // offsets: [10, 11) len = 1
 
-    overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 10, /*off*/ 1);
-    EXPECT_EQ(*(createRedactionRangeVector(1, ranges)), *overlapping_rr);
+    // Read ranges that start or end with the boundary of the redacted area.
+    out.clear();
+    info.getReadRanges(5, 5, &out);  // read offsets [5, 10)
+    EXPECT_EQ(1, out.size());
+    EXPECT_EQ(ReadRange(5, 5, true), out[0]);  // offsets: [5, 10) len = 5
 
-    overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 1, /*off*/ 1);
-    EXPECT_EQ(*(createRedactionRangeVector(1, ranges)), *overlapping_rr);
+    out.clear();
+    info.getReadRanges(1, 5, &out);  // read offsets [1, 6)
+    EXPECT_EQ(1, out.size());
+    EXPECT_EQ(ReadRange(1, 5, true), out[0]);  // offsets: [1, 6) len = 5
 
-    // Non-overlapping range
-    overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 100, /*off*/ 11);
-    EXPECT_EQ(*(createRedactionRangeVector(0, nullptr)), *overlapping_rr);
+    // Read ranges adjoining the redacted area.
+    out.clear();
+    info.getReadRanges(10, 10, &out);  // read offsets [10, 20)
+    EXPECT_EQ(0, out.size());
 
-    overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 1, /*off*/ 11);
-    EXPECT_EQ(*(createRedactionRangeVector(0, nullptr)), *overlapping_rr);
+    out.clear();
+    info.getReadRanges(0, 1, &out);  // read offsets [0, 1)
+    EXPECT_EQ(0, out.size());
+
+    // Read Range outside the redacted area.
+    out.clear();
+    info.getReadRanges(200, 10, &out);  // read offsets [200, 210)
+    EXPECT_EQ(0, out.size());
 }
 
-/**
- * Test the case where the redaction ranges don't require sorting or merging
- */
+// Multiple redaction ranges within a given area.
 TEST(RedactionInfoTest, testSortedAndNonOverlappingRedactionRanges) {
-    off64_t ranges[6] = {
-            1, 10, 15, 21, 32, 40,
-    };
+    // [10, 20), [30, 40), [40, 50)
+    off64_t ranges[4] = {10, 20, 30, 40};
+
+    RedactionInfo info = RedactionInfo(2, ranges);
+    EXPECT_EQ(2, info.size());
+    EXPECT_EQ(true, info.isRedactionNeeded());
+
+    std::vector<ReadRange> out;
+    info.getReadRanges(0, 40, &out);  // read offsets [0, 40)
+    EXPECT_EQ(4, out.size());
+    EXPECT_EQ(ReadRange(0, 10, false), out[0]);   // offsets: [0, 10) len = 10
+    EXPECT_EQ(ReadRange(10, 10, true), out[1]);   // offsets: [10, 20) len = 10
+    EXPECT_EQ(ReadRange(20, 10, false), out[2]);  // offsets: [20, 30) len = 10
+    EXPECT_EQ(ReadRange(30, 10, true), out[3]);   // offsets [30, 40) len = 10
+
+    // Read request straddling two ranges.
+    out.clear();
+    info.getReadRanges(5, 30, &out);  // read offsets [5, 35)
+    EXPECT_EQ(4, out.size());
+    EXPECT_EQ(ReadRange(5, 5, false), out[0]);    // offsets: [5, 10) len = 5
+    EXPECT_EQ(ReadRange(10, 10, true), out[1]);   // offsets: [10, 20) len = 10
+    EXPECT_EQ(ReadRange(20, 10, false), out[2]);  // offsets: [20, 30) len = 10
+    EXPECT_EQ(ReadRange(30, 5, true), out[3]);    // offsets [30, 35) len = 5
+
+    // Read request overlapping first range only.
+    out.clear();
+    info.getReadRanges(5, 10, &out);  // read offsets [5, 15)
+    EXPECT_EQ(2, out.size());
+    EXPECT_EQ(ReadRange(5, 5, false), out[0]);  // offsets: [5, 10) len = 5
+    EXPECT_EQ(ReadRange(10, 5, true), out[1]);  // offsets: [10, 15) len = 5
+
+    // Read request overlapping last range only.
+    out.clear();
+    info.getReadRanges(35, 10, &out);  // read offsets [35, 45)
+    EXPECT_EQ(2, out.size());
+    EXPECT_EQ(ReadRange(35, 5, true), out[0]);   // offsets: [35, 40) len = 5
+    EXPECT_EQ(ReadRange(40, 5, false), out[1]);  // offsets: [40, 45) len = 5
+
+    // Read request overlapping no ranges.
+    out.clear();
+    info.getReadRanges(0, 10, &out);  // read offsets [0, 10)
+    EXPECT_EQ(0, out.size());
+    out.clear();
+    info.getReadRanges(21, 5, &out);  // read offsets [21, 26)
+    EXPECT_EQ(0, out.size());
+    out.clear();
+    info.getReadRanges(40, 10, &out);  // read offsets [40, 50)
+    EXPECT_EQ(0, out.size());
+}
+
+// Multiple redaction ranges overlapping with read range.
+TEST(RedactionInfoTest, testReadRangeOverlappingWithRedactionRanges) {
+    // [10, 20), [30, 40)
+    off64_t ranges[4] = {10, 20, 30, 40};
+
+    RedactionInfo info = RedactionInfo(2, ranges);
+    EXPECT_EQ(2, info.size());
+    EXPECT_EQ(true, info.isRedactionNeeded());
+
+    std::vector<ReadRange> out;
+    // Read request overlaps with end of the ranges.
+    info.getReadRanges(20, 20, &out);  // read offsets [20, 40)
+    EXPECT_EQ(2, out.size());
+    EXPECT_EQ(ReadRange(20, 10, false), out[0]);  // offsets: [20, 30) len = 10
+    EXPECT_EQ(ReadRange(30, 10, true), out[1]);   // offsets: [30, 40) len = 10
+
+    // Read request overlapping with start of the ranges
+    out.clear();
+    info.getReadRanges(10, 20, &out);  // read offsets [10, 30)
+    EXPECT_EQ(2, out.size());
+    EXPECT_EQ(ReadRange(10, 10, true), out[0]);   // offsets: [10, 20) len = 10
+    EXPECT_EQ(ReadRange(20, 10, false), out[1]);  // offsets: [20, 30) len = 10
+
+    // Read request overlaps with start of one and end of other range.
+    out.clear();
+    info.getReadRanges(10, 30, &out);  // read offsets [10, 40)
+    EXPECT_EQ(3, out.size());
+    EXPECT_EQ(ReadRange(10, 10, true), out[0]);   // offsets: [10, 20) len = 10
+    EXPECT_EQ(ReadRange(20, 10, false), out[1]);  // offsets: [20, 30) len = 10
+    EXPECT_EQ(ReadRange(30, 10, true), out[2]);   // offsets: [30, 40) len = 10
+
+    // Read request overlaps with end of one and start of other range.
+    out.clear();
+    info.getReadRanges(20, 10, &out);  // read offsets [20, 30)
+    EXPECT_EQ(0, out.size());
+}
+
+TEST(RedactionInfoTest, testRedactionRangesSorted) {
+    off64_t ranges[6] = {30, 40, 50, 60, 10, 20};
 
     RedactionInfo info = RedactionInfo(3, ranges);
     EXPECT_EQ(3, info.size());
     EXPECT_EQ(true, info.isRedactionNeeded());
 
-    // Read request strictly contains all ranges: [0, 49]
-    auto overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 50, /*off*/ 0);
-    off64_t expected1[] = {
-            1, 10, 15, 21, 32, 40,
-    };
-    EXPECT_EQ(*(createRedactionRangeVector(3, expected1)), *overlapping_rr);
+    std::vector<ReadRange> out;
+    info.getReadRanges(0, 60, &out);  // read offsets [0, 60)
+    EXPECT_EQ(6, out.size());
+    EXPECT_EQ(ReadRange(0, 10, false), out[0]);   // offsets: [0, 10) len = 10
+    EXPECT_EQ(ReadRange(10, 10, true), out[1]);   // offsets: [10, 20) len = 10
+    EXPECT_EQ(ReadRange(20, 10, false), out[2]);  // offsets: [20, 30) len = 10
+    EXPECT_EQ(ReadRange(30, 10, true), out[3]);   // offsets [30, 40) len = 10
+    EXPECT_EQ(ReadRange(40, 10, false), out[4]);  // offsets [40, 50) len = 10
+    EXPECT_EQ(ReadRange(50, 10, true), out[5]);   // offsets [50, 60) len = 10
 
-    // Read request strictly contains a subset of the ranges: [15, 40]
-    overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 26, /*off*/ 15);
-    off64_t expected2[] = {
-            15,
-            21,
-            32,
-            40,
-    };
-    EXPECT_EQ(*(createRedactionRangeVector(2, expected2)), *overlapping_rr);
+    // Read request overlapping first range only.
+    out.clear();
+    info.getReadRanges(5, 10, &out);  // read offsets [5, 15)
+    EXPECT_EQ(2, out.size());
+    EXPECT_EQ(ReadRange(5, 5, false), out[0]);  // offsets: [5, 10) len = 5
+    EXPECT_EQ(ReadRange(10, 5, true), out[1]);  // offsets: [10, 15) len = 5
 
-    // Read request intersects with a subset of the ranges" [16, 32]
-    overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 17, /*off*/ 16);
-    EXPECT_EQ(*(createRedactionRangeVector(2, expected2)), *overlapping_rr);
+    // Read request overlapping last range only.
+    out.clear();
+    info.getReadRanges(55, 10, &out);  // read offsets [55, 65)
+    EXPECT_EQ(2, out.size());
+    EXPECT_EQ(ReadRange(55, 5, true), out[0]);   // offsets: [55, 60) len = 5
+    EXPECT_EQ(ReadRange(60, 5, false), out[1]);  // offsets: [60, 65) len = 5
+
+    // Read request overlapping no ranges.
+    out.clear();
+    info.getReadRanges(0, 10, &out);  // read offsets [0, 10)
+    EXPECT_EQ(0, out.size());
+    out.clear();
+    info.getReadRanges(60, 10, &out);  // read offsets [60, 70)
+    EXPECT_EQ(0, out.size());
 }
 
-/**
- * Test the case where the redaction ranges require sorting
- */
-TEST(RedactionInfoTest, testSortRedactionRanges) {
-    off64_t ranges[6] = {
-            1, 10, 32, 40, 15, 21,
-    };
-
-    RedactionInfo info = RedactionInfo(3, ranges);
-    EXPECT_EQ(3, info.size());
-    EXPECT_EQ(true, info.isRedactionNeeded());
-
-    // Read request strictly contains all ranges: [0, 49]
-    auto overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 50, /*off*/ 0);
-    off64_t expected1[] = {
-            1, 10, 15, 21, 32, 40,
-    };
-    EXPECT_EQ(*(createRedactionRangeVector(3, expected1)), *overlapping_rr);
-
-    // Read request strictly contains a subset of the ranges: [15, 40]
-    overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 26, /*off*/ 15);
-    off64_t expected2[] = {
-            15,
-            21,
-            32,
-            40,
-    };
-    EXPECT_EQ(*(createRedactionRangeVector(2, expected2)), *overlapping_rr);
-
-    // Read request intersects with a subset of the ranges" [16, 32]
-    overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 17, /*off*/ 16);
-    EXPECT_EQ(*(createRedactionRangeVector(2, expected2)), *overlapping_rr);
-}
-
-/**
- * Test the case where the redaction ranges require sorting or merging
- */
+// Test that the ranges are both sorted and merged
 TEST(RedactionInfoTest, testSortAndMergeRedactionRanges) {
-    off64_t ranges[8] = {
-            35, 40, 1, 10, 32, 35, 15, 21,
-    };
+    // Ranges are: [10, 20), [25, 40), [50, 60)
+    off64_t ranges[8] = {30, 40, 10, 20, 25, 30, 50, 60};
 
     RedactionInfo info = RedactionInfo(4, ranges);
     EXPECT_EQ(3, info.size());
     EXPECT_EQ(true, info.isRedactionNeeded());
 
-    // Read request strictly contains all ranges: [0, 49]
-    auto overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 50, /*off*/ 0);
-    off64_t expected1[] = {
-            1, 10, 15, 21, 32, 40,
-    };
-    EXPECT_EQ(*(createRedactionRangeVector(3, expected1)), *overlapping_rr);
-
-    // Read request strictly contains a subset of the ranges: [15, 40]
-    overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 26, /*off*/ 15);
-    off64_t expected2[] = {
-            15,
-            21,
-            32,
-            40,
-    };
-    EXPECT_EQ(*(createRedactionRangeVector(2, expected2)), *overlapping_rr);
-
-    // Read request intersects with a subset of the ranges" [16, 32]
-    overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 17, /*off*/ 16);
-    EXPECT_EQ(*(createRedactionRangeVector(2, expected2)), *overlapping_rr);
+    std::vector<ReadRange> out;
+    info.getReadRanges(0, 60, &out);  // read offsets [0, 60)
+    EXPECT_EQ(6, out.size());
+    EXPECT_EQ(ReadRange(0, 10, false), out[0]);   // offsets: [0, 10) len = 10
+    EXPECT_EQ(ReadRange(10, 10, true), out[1]);   // offsets: [10, 20) len = 10
+    EXPECT_EQ(ReadRange(20, 5, false), out[2]);   // offsets: [20, 25) len = 5
+    EXPECT_EQ(ReadRange(25, 15, true), out[3]);   // offsets [25, 40) len = 15
+    EXPECT_EQ(ReadRange(40, 10, false), out[4]);  // offsets [40, 50) len = 10
+    EXPECT_EQ(ReadRange(50, 10, true), out[5]);   // offsets [50, 60) len = 10
 }
 
-/**
- * Test the case where the redaction ranges all merge into the first range
- */
-TEST(RedactionInfoTest, testMergeAllRangesIntoTheFirstRange) {
-    off64_t ranges[10] = {
-            1, 100, 2, 99, 3, 98, 4, 97, 3, 15,
-    };
+// Test that the ranges are both sorted and merged when there's an overlap.
+//
+// TODO: Can this ever happen ? Will we ever be in a state where we need to
+// redact exif attributes that have overlapping ranges ?
+TEST(RedactionInfoTest, testSortAndMergeRedactionRanges_overlap) {
+    // Ranges are: [10, 20), [25, 40), [50, 60)
+    off64_t ranges[8] = {30, 40, 10, 20, 25, 34, 50, 60};
 
-    RedactionInfo info = RedactionInfo(5, ranges);
-    EXPECT_EQ(1, info.size());
+    RedactionInfo info = RedactionInfo(4, ranges);
+    EXPECT_EQ(3, info.size());
     EXPECT_EQ(true, info.isRedactionNeeded());
 
-    // Read request equals the range: [1, 100]
-    auto overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 100, /*off*/ 1);
-    off64_t expected[] = {1, 100};
-    EXPECT_EQ(*(createRedactionRangeVector(1, expected)), *overlapping_rr);
-
-    // Read request is contained in the range: [15, 40]
-    overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 26, /*off*/ 15);
-    EXPECT_EQ(*(createRedactionRangeVector(1, expected)), *overlapping_rr);
-
-    // Read request that strictly contains all of the redaction ranges
-    overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 1000, /*off*/ 0);
-    EXPECT_EQ(*(createRedactionRangeVector(1, expected)), *overlapping_rr);
+    std::vector<ReadRange> out;
+    info.getReadRanges(0, 60, &out);  // read offsets [0, 60)
+    EXPECT_EQ(6, out.size());
+    EXPECT_EQ(ReadRange(0, 10, false), out[0]);   // offsets: [0, 10) len = 10
+    EXPECT_EQ(ReadRange(10, 10, true), out[1]);   // offsets: [10, 20) len = 10
+    EXPECT_EQ(ReadRange(20, 5, false), out[2]);   // offsets: [20, 25) len = 5
+    EXPECT_EQ(ReadRange(25, 15, true), out[3]);   // offsets [25, 40) len = 15
+    EXPECT_EQ(ReadRange(40, 10, false), out[4]);  // offsets [40, 50) len = 10
+    EXPECT_EQ(ReadRange(50, 10, true), out[5]);   // offsets [50, 60) len = 10
 }
 
-/**
- * Test the case where the redaction ranges all merge into the last range
- */
-TEST(RedactionInfoTest, testMergeAllRangesIntoTheLastRange) {
-    off64_t ranges[10] = {
-            4, 96, 3, 97, 2, 98, 1, 99, 0, 100,
-    };
+// WARNING: The tests below assume that merging of ranges happen during
+// object construction (which is asserted by the check on |info.size()|.
+// Therefore, we don't write redundant tests for boundary conditions that
+// we've covered above. If this ever changes, these tests need to be expanded.
+TEST(RedactionInfoTest, testMergeAllRangesIntoSingleRange) {
+    // Ranges are: [8, 24)
+    off64_t ranges[8] = {10, 20, 8, 14, 14, 24, 12, 16};
 
-    RedactionInfo info = RedactionInfo(5, ranges);
+    RedactionInfo info = RedactionInfo(4, ranges);
     EXPECT_EQ(1, info.size());
     EXPECT_EQ(true, info.isRedactionNeeded());
 
-    // Read request equals the range: [0, 100]
-    auto overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 100, /*off*/ 0);
-    off64_t expected[] = {0, 100};
-    EXPECT_EQ(*(createRedactionRangeVector(1, expected)), *overlapping_rr);
+    std::vector<ReadRange> out;
+    info.getReadRanges(0, 30, &out);  // read offsets [0, 30)
+    EXPECT_EQ(3, out.size());
+    EXPECT_EQ(ReadRange(0, 8, false), out[0]);   // offsets: [0, 8) len = 8
+    EXPECT_EQ(ReadRange(8, 16, true), out[1]);   // offsets: [8, 24) len = 16
+    EXPECT_EQ(ReadRange(24, 6, false), out[2]);  // offsets: [24, 30) len = 6
 
-    // Read request is contained in the range: [15, 40]
-    overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 26, /*off*/ 15);
-    EXPECT_EQ(*(createRedactionRangeVector(1, expected)), *overlapping_rr);
+    // Ranges are: [85, 100)
+    off64_t ranges2[10] = {90, 95, 95, 100, 85, 91, 92, 94, 99, 100};
+    info = RedactionInfo(5, ranges2);
+    EXPECT_EQ(1, info.size());
+    EXPECT_EQ(true, info.isRedactionNeeded());
 
-    // Read request that strictly contains all of the redaction ranges
-    overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 1000, /*off*/ 0);
-    EXPECT_EQ(*(createRedactionRangeVector(1, expected)), *overlapping_rr);
+    out.clear();
+    info.getReadRanges(80, 30, &out);  // read offsets [80, 110)
+    EXPECT_EQ(3, out.size());
+    EXPECT_EQ(ReadRange(80, 5, false), out[0]);    // offsets: [80, 85) len = 5
+    EXPECT_EQ(ReadRange(85, 15, true), out[1]);    // offsets: [85, 100) len = 15
+    EXPECT_EQ(ReadRange(100, 10, false), out[2]);  // offsets: [100, 110) len = 10
 }
 
-/**
- * Test the case where the redaction ranges progressively merge
- */
-TEST(RedactionInfoTest, testMergeAllRangesProgressively) {
-    off64_t ranges[10] = {
-            1, 11, 2, 12, 3, 13, 4, 14, 5, 15,
-    };
+TEST(RedactionInfoTest, testMergeMultipleRanges) {
+    // Ranges are: [10, 30), [60, 80)
+    off64_t ranges[8] = {20, 30, 10, 20, 70, 80, 60, 70};
 
-    RedactionInfo info = RedactionInfo(5, ranges);
-    EXPECT_EQ(1, info.size());
+    RedactionInfo info = RedactionInfo(4, ranges);
+    EXPECT_EQ(2, info.size());
     EXPECT_EQ(true, info.isRedactionNeeded());
 
-    // Read request equals the range: [1, 15]
-    auto overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 15, /*off*/ 1);
-    off64_t expected[] = {1, 15};
-    EXPECT_EQ(*(createRedactionRangeVector(1, expected)), *overlapping_rr);
+    std::vector<ReadRange> out;
+    info.getReadRanges(0, 100, &out);  // read offsets [0, 100)
+    EXPECT_EQ(5, out.size());
+    EXPECT_EQ(ReadRange(0, 10, false), out[0]);   // offsets: [0, 10) len = 10
+    EXPECT_EQ(ReadRange(10, 20, true), out[1]);   // offsets: [10, 30) len = 20
+    EXPECT_EQ(ReadRange(30, 30, false), out[2]);  // offsets: [30, 60) len = 30
+    EXPECT_EQ(ReadRange(60, 20, true), out[3]);   // offsets [60, 80) len = 20
+    EXPECT_EQ(ReadRange(80, 20, false), out[4]);  // offsets [80, 100) len = 20
+}
 
-    // Read request is contained in the range: [2, 12]
-    overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 10, /*off*/ 2);
-    EXPECT_EQ(*(createRedactionRangeVector(1, expected)), *overlapping_rr);
+// Redaction ranges of size zero.
+TEST(RedactionInfoTest, testRedactionRangesZeroSize) {
+    // [10, 20), [30, 40)
+    off64_t ranges[6] = {10, 20, 30, 40, 25, 25};
 
-    // Read request that strictly contains all of the redaction ranges
-    overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 100, /*off*/ 0);
-    EXPECT_EQ(*(createRedactionRangeVector(1, expected)), *overlapping_rr);
-
-    off64_t reverse_rr[10] = {
-            5, 15, 4, 14, 3, 13, 2, 12, 1, 11,
-    };
-
-    RedactionInfo reverse_info = RedactionInfo(5, reverse_rr);
-    EXPECT_EQ(1, info.size());
+    RedactionInfo info = RedactionInfo(3, ranges);
+    EXPECT_EQ(2, info.size());
     EXPECT_EQ(true, info.isRedactionNeeded());
 
-    // Read request equals the range: [1, 15]
-    overlapping_rr = info.getOverlappingRedactionRanges(/*size*/ 15, /*off*/ 1);
-    EXPECT_EQ(*(createRedactionRangeVector(1, expected)), *overlapping_rr);
+    // Normal read request, should skip range with zero size
+    std::vector<ReadRange> out;
+    info.getReadRanges(0, 40, &out);  // read offsets [0, 40)
+    EXPECT_EQ(4, out.size());
+    EXPECT_EQ(ReadRange(0, 10, false), out[0]);   // offsets: [0, 10) len = 10
+    EXPECT_EQ(ReadRange(10, 10, true), out[1]);   // offsets: [10, 20) len = 10
+    EXPECT_EQ(ReadRange(20, 10, false), out[2]);  // offsets: [20, 30) len = 10
+    EXPECT_EQ(ReadRange(30, 10, true), out[3]);   // offsets [30, 40) len = 10
+
+    // Read request starting at offset overlapping with zero size range.
+    out.clear();
+    info.getReadRanges(25, 10, &out);  // read offsets [25, 35)
+    EXPECT_EQ(2, out.size());
+    EXPECT_EQ(ReadRange(25, 5, false), out[0]);  // offsets: [25, 30) len = 5
+    EXPECT_EQ(ReadRange(30, 5, true), out[1]);   // offsets [30, 35) len = 5
+
+    // 1 byte read request starting at offset overlapping with zero size range.
+    out.clear();
+    info.getReadRanges(25, 1, &out);  // read offsets [25, 26)
+    EXPECT_EQ(0, out.size());
+
+    // Read request ending at offset overlapping with zero size range.
+    out.clear();
+    info.getReadRanges(0, 25, &out);  // read offsets [0, 25)
+    EXPECT_EQ(3, out.size());
+    EXPECT_EQ(ReadRange(0, 10, false), out[0]);  // offsets: [0, 10) len = 10
+    EXPECT_EQ(ReadRange(10, 10, true), out[1]);  // offsets: [10, 20) len = 10
+    EXPECT_EQ(ReadRange(20, 5, false), out[2]);  // offsets: [20, 25) len = 10
+
+    // Read request that includes only zero size range
+    out.clear();
+    info.getReadRanges(20, 10, &out);  // read offsets [20, 27)
+    EXPECT_EQ(0, out.size());
+}
+
+// Single redaction range with zero size
+TEST(RedactionInfoTest, testSingleRedactionRangesZeroSize) {
+    off64_t ranges[2] = {10, 10};
+
+    RedactionInfo info = RedactionInfo(1, ranges);
+    EXPECT_EQ(0, info.size());
+    EXPECT_EQ(false, info.isRedactionNeeded());
+
+    // Normal read request, should skip range with zero size
+    std::vector<ReadRange> out;
+    info.getReadRanges(0, 40, &out);  // read offsets [0, 40)
+    EXPECT_EQ(0, out.size());
 }
diff --git a/jni/com_android_providers_media_FuseDaemon.cpp b/jni/com_android_providers_media_FuseDaemon.cpp
index 3a65696..d67123d 100644
--- a/jni/com_android_providers_media_FuseDaemon.cpp
+++ b/jni/com_android_providers_media_FuseDaemon.cpp
@@ -101,6 +101,25 @@
     // 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;
 }
@@ -120,7 +139,12 @@
          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)}};
+                 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)}};
 }  // namespace
 
 void register_android_providers_media_FuseDaemon(JavaVM* vm, JNIEnv* env) {
diff --git a/jni/include/libfuse_jni/RedactionInfo.h b/jni/include/libfuse_jni/RedactionInfo.h
index 5218e28..fe475cb 100644
--- a/jni/include/libfuse_jni/RedactionInfo.h
+++ b/jni/include/libfuse_jni/RedactionInfo.h
@@ -17,19 +17,31 @@
 #ifndef MEDIA_PROVIDER_FUSE_REDACTIONINFO_H_
 #define MEDIA_PROVIDER_FUSE_REDACTIONINFO_H_
 
+#include <ostream>
 #include <vector>
 
 namespace mediaprovider {
 namespace fuse {
 
 /**
- * Type that represents a single redaction range within a file.
- * first is the offset of the first byte in the redaction range within the file
- * second is the offset of the last byte in the redaction range within the file
- * Ranges are inclusive.
+ * Type that represents a single redaction range within a file. Contains
+ * a pair of offsets in the file, [start, end).
  */
 typedef std::pair<off64_t, off64_t> RedactionRange;
 
+class ReadRange {
+  public:
+    ReadRange(off64_t s, size_t l, bool r) : start(s), size(l), is_redaction(r) {}
+
+    const off64_t start;
+    const size_t size;
+    const bool is_redaction;
+
+    bool operator==(const ReadRange& rhs) const {
+        return start == rhs.start && size == rhs.size && is_redaction == rhs.is_redaction;
+    }
+};
+
 class RedactionInfo {
   public:
     /**
@@ -55,6 +67,23 @@
      * Calls d'tor for redactionRanges (vector).
      */
     ~RedactionInfo() = default;
+
+    /**
+     * Returns a set of ranges to fulfill a read request starting at |off| of size
+     * |size|.
+     */
+    void getReadRanges(off64_t off, size_t size, std::vector<ReadRange>* out) const;
+
+    /**
+     * Returns whether any ranges need to be redacted.
+     */
+    bool isRedactionNeeded() const;
+    /**
+     * Returns number of redaction ranges.
+     */
+    int size() const;
+
+  private:
     /**
      * Calculates the redaction ranges that overlap with a given read request.
      * The read request is defined by its size and the offset of its first byte.
@@ -70,16 +99,6 @@
      */
     std::unique_ptr<std::vector<RedactionRange>> getOverlappingRedactionRanges(size_t size,
                                                                                off64_t off) const;
-    /**
-     * Returns whether any ranges need to be redacted.
-     */
-    bool isRedactionNeeded() const;
-    /**
-     * Returns number of redaction ranges.
-     */
-    int size() const;
-
-  private:
     std::vector<RedactionRange> redaction_ranges_;
     void processRedactionRanges(int redaction_ranges_num, const off64_t* redaction_ranges);
     bool hasOverlapWithReadRequest(size_t size, off64_t off) const;
diff --git a/jni/node-inl.h b/jni/node-inl.h
index 364a327..e531a0a 100644
--- a/jni/node-inl.h
+++ b/jni/node-inl.h
@@ -19,6 +19,8 @@
 
 #include <android-base/logging.h>
 
+#include <sys/types.h>
+#include <atomic>
 #include <cstdint>
 #include <limits>
 #include <list>
@@ -40,13 +42,23 @@
 namespace fuse {
 
 struct handle {
-    explicit handle(int fd, const RedactionInfo* ri, bool cached) : fd(fd), ri(ri), cached(cached) {
+    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) {
         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); }
 };
@@ -114,21 +126,26 @@
 class node {
   public:
     // Creates a new node with the specified parent, name and lock.
-    static node* Create(node* parent, const std::string& name, std::recursive_mutex* 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,
                         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, lock, tracker);
+        return new node(parent, name, io_path, should_invalidate, transforms_complete, transforms,
+                        transforms_reason, lock, ino, 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,
+    static node* CreateRoot(const std::string& path, std::recursive_mutex* lock, ino_t ino,
                             NodeTracker* tracker) {
         std::lock_guard<std::recursive_mutex> guard(*lock);
-        node* root = new node(nullptr, path, lock, tracker);
+        node* root = new node(nullptr, path, path, false /* should_invalidate */,
+                              true /* transforms_complete */, 0 /* transforms */,
+                              0 /* transforms_reason */, lock, ino, tracker);
 
         // The root always has one extra reference to avoid it being
         // accidentally collected.
@@ -174,38 +191,44 @@
     // associated with its descendants.
     std::string BuildSafePath() const;
 
-    // Looks up a direct descendant of this node by name. If |acquire| is true,
+    // Looks up a direct descendant of this node by case-insensitive |name|. If |acquire| is true,
     // also Acquire the node before returning a reference to it.
-    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_) {
+    // |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) {
                 if (acquire) {
                     child->Acquire();
                 }
-                return child;
+                return true;
             }
-        }
-        return nullptr;
+            return false;
+        });
     }
 
-    // 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
+    // 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
     // 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;
+        });
+    }
+
     void Rename(const std::string& name, node* new_parent) {
         std::lock_guard<std::recursive_mutex> guard(*lock_);
 
@@ -252,6 +275,20 @@
         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_;
@@ -282,6 +319,26 @@
         return false;
     }
 
+    bool ShouldInvalidate() const {
+        std::lock_guard<std::recursive_mutex> guard(*lock_);
+        return should_invalidate_;
+    }
+
+    void SetShouldInvalidate() {
+        std::lock_guard<std::recursive_mutex> guard(*lock_);
+        should_invalidate_ = true;
+    }
+
+    bool HasRedactedCache() const {
+        std::lock_guard<std::recursive_mutex> guard(*lock_);
+        return has_redacted_cache_;
+    }
+
+    void SetRedactedCache(bool state) {
+        std::lock_guard<std::recursive_mutex> guard(*lock_);
+        has_redacted_cache_ = state;
+    }
+
     inline void AddDirHandle(dirhandle* d) {
         std::lock_guard<std::recursive_mutex> guard(*lock_);
 
@@ -304,13 +361,25 @@
     // 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, std::recursive_mutex* lock, NodeTracker* tracker)
+    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)
         : 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),
           deleted_(false),
           lock_(lock),
+          ino_(ino),
           tracker_(tracker) {
         tracker_->NodeCreated(this);
         Acquire();
@@ -319,6 +388,10 @@
         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
@@ -359,6 +432,32 @@
         }
     }
 
+    // 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.
     //
@@ -405,6 +504,20 @@
 
     // 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
@@ -416,8 +529,12 @@
     std::vector<std::unique_ptr<handle>> handles_;
     // List of directory handles associated with this node. Guarded by |lock_|.
     std::vector<std::unique_ptr<dirhandle>> dirhandles_;
+    bool has_redacted_cache_;
+    bool should_invalidate_;
     bool deleted_;
     std::recursive_mutex* lock_;
+    // Inode number of the file represented by this node.
+    const ino_t ino_;
 
     NodeTracker* const tracker_;
 
diff --git a/jni/node.cpp b/jni/node.cpp
index e17a9e8..31e4970 100644
--- a/jni/node.cpp
+++ b/jni/node.cpp
@@ -93,6 +93,24 @@
     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 357cea8..e6870f8 100644
--- a/jni/node_test.cpp
+++ b/jni/node_test.cpp
@@ -31,8 +31,15 @@
 
     typedef std::unique_ptr<node, decltype(&NodeTest::destroy)> unique_node_ptr;
 
-    unique_node_ptr CreateNode(node* parent, const std::string& path) {
-        return unique_node_ptr(node::Create(parent, path, &lock_, &tracker_), &NodeTest::destroy);
+    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);
     }
 
     // Expose NodeCompare for testing.
@@ -61,7 +68,7 @@
 }
 
 TEST_F(NodeTest, TestRelease) {
-    node* node = node::Create(nullptr, "/path", &lock_, &tracker_);
+    node* node = node::Create(nullptr, "/path", "", false, true, 0, 0, &lock_, 0, &tracker_);
     acquire(node);
     acquire(node);
     ASSERT_EQ(3, GetRefCount(node));
@@ -77,7 +84,7 @@
     ASSERT_TRUE(node->Release(2));
 }
 
-TEST_F(NodeTest, TestRenameWithName) {
+TEST_F(NodeTest, TestRenameName) {
     unique_node_ptr parent = CreateNode(nullptr, "/path");
 
     unique_node_ptr child = CreateNode(parent.get(), "subdir");
@@ -94,7 +101,7 @@
     ASSERT_EQ(1, GetRefCount(child.get()));
 }
 
-TEST_F(NodeTest, TestRenameWithParent) {
+TEST_F(NodeTest, TestRenameParent) {
     unique_node_ptr parent1 = CreateNode(nullptr, "/path1");
     unique_node_ptr parent2 = CreateNode(nullptr, "/path2");
 
@@ -113,7 +120,7 @@
     ASSERT_EQ(1, GetRefCount(child.get()));
 }
 
-TEST_F(NodeTest, TestRenameWithNameAndParent) {
+TEST_F(NodeTest, TestRenameNameAndParent) {
     unique_node_ptr parent1 = CreateNode(nullptr, "/path1");
     unique_node_ptr parent2 = CreateNode(nullptr, "/path2");
 
@@ -133,6 +140,101 @@
     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());
@@ -156,14 +258,30 @@
     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", &lock_, &tracker_);
-    node::Create(child, "s1", &lock_, &tracker_);
-    node* subchild2 = node::Create(child, "s2", &lock_, &tracker_);
-    node::Create(subchild2, "sc2", &lock_, &tracker_);
+    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_);
 
     ASSERT_EQ(child, parent->LookupChildByName("subdir", false /* acquire */));
     node::DeleteTree(child);
@@ -178,6 +296,20 @@
     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");
@@ -217,7 +349,8 @@
 TEST_F(NodeTest, AddDestroyHandle) {
     unique_node_ptr node = CreateNode(nullptr, "/path");
 
-    handle* h = new handle(-1, new mediaprovider::fuse::RedactionInfo, true /* cached */);
+    handle* h = new handle(-1, new mediaprovider::fuse::RedactionInfo, true /* cached */,
+                           false /* passthrough */, 0 /* uid */, 0 /* transforms_uid */);
     node->AddHandle(h);
     ASSERT_TRUE(node->HasCachedHandle());
 
@@ -228,8 +361,9 @@
     // 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 */));
+    std::unique_ptr<handle> h2(new handle(-1, new mediaprovider::fuse::RedactionInfo,
+                                          true /* cached */, false /* passthrough */, 0 /* uid */,
+                                          0 /* transforms_uid */));
     EXPECT_DEATH(node->DestroyHandle(h2.get()), "");
 }
 
@@ -318,3 +452,44 @@
     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 18d6af2..3b03bc8 100644
--- a/legacy/Android.bp
+++ b/legacy/Android.bp
@@ -16,7 +16,6 @@
         "mediaprovider-database",
         "androidx.appcompat_appcompat",
         "androidx.core_core",
-        "guava",
     ],
 
     libs: [
@@ -29,7 +28,7 @@
         "src/**/*.java",
     ],
 
+    platform_apis: true,
     certificate: "media",
     privileged: true,
-    sdk_version: "module_current",
 }
diff --git a/lint-baseline.xml b/lint-baseline.xml
new file mode 100644
index 0000000..5f39c57
--- /dev/null
+++ b/lint-baseline.xml
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.os.storage.StorageVolume#getStorageUuid`"
+        errorLine1="                mTranscodeVolumeUuid = vol.getStorageUuid();"
+        errorLine2="                                           ~~~~~~~~~~~~~~">
+        <location
+            file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
+            line="311"
+            column="44"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.content.pm.PackageManager#getProperty`"
+        errorLine1="            Property mediaCapProperty = mPackageManager.getProperty("
+        errorLine2="                                                        ~~~~~~~~~~~">
+        <location
+            file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
+            line="827"
+            column="57"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.content.pm.PackageManager.Property#getResourceId`"
+        errorLine1="                    .getXml(mediaCapProperty.getResourceId());"
+        errorLine2="                                             ~~~~~~~~~~~~~">
+        <location
+            file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
+            line="830"
+            column="46"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.media.ApplicationMediaCapabilities#createFromXml`"
+        errorLine1="            ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml("
+        errorLine2="                                                                                   ~~~~~~~~~~~~~">
+        <location
+            file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
+            line="831"
+            column="84"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.media.ApplicationMediaCapabilities#isFormatSpecified`"
+        errorLine1="        if (capability.isFormatSpecified(MediaFormat.MIMETYPE_VIDEO_HEVC)) {"
+        errorLine2="                       ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
+            line="852"
+            column="24"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.media.ApplicationMediaCapabilities#isVideoMimeTypeSupported`"
+        errorLine1="            if (capability.isVideoMimeTypeSupported(MediaFormat.MIMETYPE_VIDEO_HEVC)) {"
+        errorLine2="                           ~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
+            line="853"
+            column="28"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.media.ApplicationMediaCapabilities#isFormatSpecified`"
+        errorLine1="        if (capability.isFormatSpecified(MediaFeature.HdrType.HDR10)) {"
+        errorLine2="                       ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
+            line="861"
+            column="24"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.media.ApplicationMediaCapabilities#isHdrTypeSupported`"
+        errorLine1="            if (capability.isHdrTypeSupported(MediaFeature.HdrType.HDR10)) {"
+        errorLine2="                           ~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
+            line="862"
+            column="28"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.media.ApplicationMediaCapabilities#isFormatSpecified`"
+        errorLine1="        if (capability.isFormatSpecified(MediaFeature.HdrType.HDR10_PLUS)) {"
+        errorLine2="                       ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
+            line="869"
+            column="24"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.media.ApplicationMediaCapabilities#isHdrTypeSupported`"
+        errorLine1="            if (capability.isHdrTypeSupported(MediaFeature.HdrType.HDR10_PLUS)) {"
+        errorLine2="                           ~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
+            line="870"
+            column="28"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.media.ApplicationMediaCapabilities#isFormatSpecified`"
+        errorLine1="        if (capability.isFormatSpecified(MediaFeature.HdrType.HLG)) {"
+        errorLine2="                       ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
+            line="877"
+            column="24"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.media.ApplicationMediaCapabilities#isHdrTypeSupported`"
+        errorLine1="            if (capability.isHdrTypeSupported(MediaFeature.HdrType.HLG)) {"
+        errorLine2="                           ~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
+            line="878"
+            column="28"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.media.ApplicationMediaCapabilities#isFormatSpecified`"
+        errorLine1="        if (capability.isFormatSpecified(MediaFeature.HdrType.DOLBY_VISION)) {"
+        errorLine2="                       ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
+            line="885"
+            column="24"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.media.ApplicationMediaCapabilities#isHdrTypeSupported`"
+        errorLine1="            if (capability.isHdrTypeSupported(MediaFeature.HdrType.DOLBY_VISION)) {"
+        errorLine2="                           ~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
+            line="886"
+            column="28"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.media.ApplicationMediaCapabilities.Builder#build`"
+        errorLine1="                        new ApplicationMediaCapabilities.Builder().build();"
+        errorLine2="                                                                   ~~~~~">
+        <location
+            file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
+            line="1079"
+            column="68"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `new android.media.ApplicationMediaCapabilities.Builder`"
+        errorLine1="                        new ApplicationMediaCapabilities.Builder().build();"
+        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/providers/MediaProvider/src/com/android/providers/media/TranscodeHelper.java"
+            line="1079"
+            column="25"/>
+    </issue>
+
+</issues>
diff --git a/logging.sh b/logging.sh
index fd2a3a5..c1143ce 100755
--- a/logging.sh
+++ b/logging.sh
@@ -6,6 +6,7 @@
 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
@@ -20,6 +21,7 @@
     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
new file mode 100644
index 0000000..fd31162
--- /dev/null
+++ b/preinstalled-packages-com.android.providers.media.module.xml
@@ -0,0 +1,23 @@
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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/layout/permission_body.xml b/res/layout/permission_body.xml
index 0fb4ece..7c74065 100644
--- a/res/layout/permission_body.xml
+++ b/res/layout/permission_body.xml
@@ -97,11 +97,5 @@
             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/photo_picker.xml b/res/layout/photo_picker.xml
new file mode 100644
index 0000000..073a8bd
--- /dev/null
+++ b/res/layout/photo_picker.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:app="http://schemas.android.com/apk/res-auto"
+              xmlns:tools="http://schemas.android.com/tools"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              tools:context=".MainActivity">
+
+    <Button
+        android:id="@+id/button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Give up"
+        />
+
+    <ListView
+        android:id="@+id/names_list"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        />
+
+</LinearLayout>
diff --git a/res/raw/transcode_compat_manifest b/res/raw/transcode_compat_manifest
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/res/raw/transcode_compat_manifest
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 09a1296..46d36bf 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -47,64 +47,136 @@
       <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 1a62bf5..fb69fe1 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g> <xliff:g id="COUNT">^2</xliff:g> ኦዲዮ ፋይሎችን እንዲቀይር ይፈቀድለት?</item>
       <item 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 c5d7cea..9fef177 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -59,6 +59,14 @@
       <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g 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>
@@ -67,6 +75,14 @@
       <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g 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>
@@ -75,6 +91,14 @@
       <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g 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>
@@ -83,6 +107,14 @@
       <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بتعديل <xliff:g 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>
@@ -91,6 +123,14 @@
       <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g 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>
@@ -99,6 +139,14 @@
       <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g 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>
@@ -107,6 +155,14 @@
       <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g 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>
@@ -115,6 +171,14 @@
       <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g 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>
@@ -123,6 +187,14 @@
       <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g 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>
@@ -131,6 +203,14 @@
       <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g 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>
@@ -139,6 +219,14 @@
       <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g 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>
@@ -147,6 +235,14 @@
       <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بنقل <xliff:g 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>
@@ -155,6 +251,14 @@
       <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g 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>
@@ -163,6 +267,14 @@
       <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g 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>
@@ -171,6 +283,14 @@
       <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g 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>
@@ -179,4 +299,20 @@
       <item quantity="other">هل تريد السماح لتطبيق <xliff:g id="APP_NAME_1">^1</xliff:g> بحذف <xliff:g 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 35449a8..fbb3a35 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ক <xliff:g id="COUNT">^2</xliff:g> টা অডিঅ’ ফাইল সংশোধন কৰিবলৈ অনুমতি দিবনে?</item>
       <item 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 a8d2ab3..785ada1 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -47,64 +47,136 @@
       <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 9756151..a4a20c8 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -50,79 +50,167 @@
       <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 58bffee..18c1dcc 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -53,94 +53,198 @@
       <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 b576228..878ad13 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="other">Да се разреши ли на <xliff:g id="APP_NAME_1">^1</xliff:g> да промени <xliff:g 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 4b02f68..06b23e7 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>-কে <xliff:g id="COUNT">^2</xliff:g>টি অডিও ফাইল পরিবর্তন করার অনুমতি দিতে চান?</item>
       <item 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 9f89481..791b3a4 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -50,79 +50,167 @@
       <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 184a9b9..9341998 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -47,64 +47,136 @@
       <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 3fa3e40..eab7293 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -53,94 +53,198 @@
       <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 96ee522..e5bba25 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -47,64 +47,136 @@
       <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 8b19ff5..6a0001a 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -47,64 +47,136 @@
       <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 7045ca0..e50e8f8 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="other">Να επιτραπεί στην εφαρμογή <xliff:g id="APP_NAME_1">^1</xliff:g> η τροποποίηση <xliff:g 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 1205a7e..2f1bda0 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -47,64 +47,136 @@
       <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 1205a7e..2f1bda0 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -47,64 +47,136 @@
       <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 1205a7e..2f1bda0 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -47,64 +47,136 @@
       <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 1205a7e..2f1bda0 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -47,64 +47,136 @@
       <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 18ba860..707a0f5 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -47,64 +47,136 @@
       <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 e2ffe99..016c843 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -47,64 +47,136 @@
       <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 7d0ee18..89b404f 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -47,64 +47,136 @@
       <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 1dbfc5c..27a526a 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -47,64 +47,136 @@
       <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 a4b655a..1c14882 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -27,7 +27,7 @@
     <string name="root_documents" msgid="3829103301363849237">"Dokumentuak"</string>
     <string name="permission_required" msgid="1460820436132943754">"Baimena behar da elementu hau aldatu edo ezabatzeko."</string>
     <string name="permission_required_action" msgid="706370952366113539">"Egin aurrera"</string>
-    <string name="grant_dialog_button_allow" msgid="1644287024501033471">"Eman baimena"</string>
+    <string name="grant_dialog_button_allow" msgid="1644287024501033471">"Baimendu"</string>
     <string name="grant_dialog_button_deny" msgid="6190589471415815741">"Ukatu"</string>
     <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
       <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
@@ -41,70 +41,142 @@
     <string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aplikazioak aldi baterako fitxategi batzuk ezabatu nahi ditu. Ondorioz, baliteke bateria edo datu-konexioko datu gehiago erabiltzea."</string>
     <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Aplikazioaren aldi baterako fitxategiak garbitzen…"</string>
     <string name="clear" msgid="5524638938415865915">"Garbitu"</string>
-    <string name="allow" msgid="8885707816848569619">"Eman baimena"</string>
+    <string name="allow" msgid="8885707816848569619">"Baimendu"</string>
     <string name="deny" msgid="6040983710442068936">"Ukatu"</string>
     <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
       <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> audio-fitxategiri aldaketak egiteko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
       <item quantity="one">Audio-fitxategi honi aldaketak egiteko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
     </plurals>
+    <plurals name="permission_progress_write_audio" formatted="false" msgid="3757901555809850632">
+      <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> audio-fitxategi aldatzen…</item>
+      <item quantity="one">Audio-fitxategia aldatzen…</item>
+    </plurals>
     <plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
       <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> bideori aldaketak egiteko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
       <item quantity="one">Bideo honi aldaketak egiteko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
     </plurals>
+    <plurals name="permission_progress_write_video" formatted="false" msgid="2244685155683762411">
+      <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> bideo aldatzen…</item>
+      <item quantity="one">Bideoa aldatzen…</item>
+    </plurals>
     <plurals name="permission_write_image" formatted="false" msgid="748745548893845892">
       <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> argazkiri aldaketak egiteko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
       <item quantity="one">Argazki honi aldaketak egiteko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
     </plurals>
+    <plurals name="permission_progress_write_image" formatted="false" msgid="9126939088839855157">
+      <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> argazki aldatzen…</item>
+      <item quantity="one">Argazkia aldatzen…</item>
+    </plurals>
     <plurals name="permission_write_generic" formatted="false" msgid="3270172714743671779">
       <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> elementuri aldaketak egiteko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
       <item quantity="one">Elementu honi aldaketak egiteko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
     </plurals>
+    <plurals name="permission_progress_write_generic" formatted="false" msgid="1928961922186705621">
+      <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> elementu aldatzen…</item>
+      <item quantity="one">Elementua aldatzen…</item>
+    </plurals>
     <plurals name="permission_trash_audio" formatted="false" msgid="8907813869381755423">
       <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> audio-fitxategi zaborrontzira eramateko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
       <item quantity="one">Audio-fitxategi hau zaborrontzira eramateko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
     </plurals>
+    <plurals name="permission_progress_trash_audio" formatted="false" msgid="8142631134676951388">
+      <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> audio-fitxategi zaborrontzira eramaten…</item>
+      <item quantity="one">Audio-fitxategia zaborrontzira eramaten…</item>
+    </plurals>
     <plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
       <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> bideo zaborrontzira eramateko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
       <item quantity="one">Bideo hau zaborrontzira eramateko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
     </plurals>
+    <plurals name="permission_progress_trash_video" formatted="false" msgid="2566683722600149120">
+      <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> bideo zaborrontzira eramaten…</item>
+      <item quantity="one">Bideoa zaborrontzira eramaten…</item>
+    </plurals>
     <plurals name="permission_trash_image" formatted="false" msgid="6400475304599873227">
       <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> argazki zaborrontzira eramateko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
       <item quantity="one">Argazki hau zaborrontzira eramateko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
     </plurals>
+    <plurals name="permission_progress_trash_image" formatted="false" msgid="4529586663770971476">
+      <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> argazki zaborrontzira eramaten…</item>
+      <item quantity="one">Argazkia zaborrontzira eramaten…</item>
+    </plurals>
     <plurals name="permission_trash_generic" formatted="false" msgid="3814167365075039711">
       <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> elementu zaborrontzira eramateko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
       <item quantity="one">Elementu hau zaborrontzira eramateko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
     </plurals>
+    <plurals name="permission_progress_trash_generic" formatted="false" msgid="6995141190896908381">
+      <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> elementu zaborrontzira eramaten…</item>
+      <item quantity="one">Elementua zaborrontzira eramaten…</item>
+    </plurals>
     <plurals name="permission_untrash_audio" formatted="false" msgid="7795265980168966321">
       <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> audio-fitxategi zaborrontzitik ateratzeko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
       <item quantity="one">Audio-fitxategi hau zaborrontzitik ateratzeko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
     </plurals>
+    <plurals name="permission_progress_untrash_audio" formatted="false" msgid="4047200387122043006">
+      <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> audio-fitxategi zaborrontzitik ateratzen…</item>
+      <item quantity="one">Audio-fitxategia zaborrontzitik ateratzen…</item>
+    </plurals>
     <plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
       <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> bideo zaborrontzitik ateratzeko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
       <item quantity="one">Bideo hau zaborrontzitik ateratzeko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
     </plurals>
+    <plurals name="permission_progress_untrash_video" formatted="false" msgid="7996233128375495458">
+      <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> bideo zaborrontzitik ateratzen…</item>
+      <item quantity="one">Bideoa zaborrontzitik ateratzen…</item>
+    </plurals>
     <plurals name="permission_untrash_image" formatted="false" msgid="7024071378733595056">
       <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> argazki zaborrontzitik ateratzeko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
       <item quantity="one">Argazki hau zaborrontzitik ateratzeko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
     </plurals>
+    <plurals name="permission_progress_untrash_image" formatted="false" msgid="3473769131910926122">
+      <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> argazki zaborrontzitik ateratzen…</item>
+      <item quantity="one">Argazkia zaborrontzitik ateratzen…</item>
+    </plurals>
     <plurals name="permission_untrash_generic" formatted="false" msgid="6872817093731198374">
       <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> elementu zaborrontzitik ateratzeko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
       <item quantity="one">Elementu hau zaborrontzitik ateratzeko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
     </plurals>
+    <plurals name="permission_progress_untrash_generic" formatted="false" msgid="263867753672461510">
+      <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> elementu zaborrontzitik ateratzen…</item>
+      <item quantity="one">Elementua zaborrontzitik ateratzen…</item>
+    </plurals>
     <plurals name="permission_delete_audio" formatted="false" msgid="6848547621165184719">
       <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> audio-fitxategi ezabatzeko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
       <item quantity="one">Audio-fitxategi hau ezabatzeko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
     </plurals>
+    <plurals name="permission_progress_delete_audio" formatted="false" msgid="8579231060666743501">
+      <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> audio-fitxategi ezabatzen…</item>
+      <item quantity="one">Audio-fitxategia ezabatzen…</item>
+    </plurals>
     <plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
       <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> bideo ezabatzeko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
       <item quantity="one">Bideo hau ezabatzeko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
     </plurals>
+    <plurals name="permission_progress_delete_video" formatted="false" msgid="4349991290732459111">
+      <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> bideo ezabatzen…</item>
+      <item quantity="one">Bideoa ezabatzen…</item>
+    </plurals>
     <plurals name="permission_delete_image" formatted="false" msgid="2303409455224710111">
       <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> argazki ezabatzeko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
       <item quantity="one">Argazki hau ezabatzeko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
     </plurals>
+    <plurals name="permission_progress_delete_image" formatted="false" msgid="118648854886957046">
+      <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> argazki ezabatzen…</item>
+      <item quantity="one">Argazkia ezabatzen…</item>
+    </plurals>
     <plurals name="permission_delete_generic" formatted="false" msgid="1412218850351841181">
       <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> elementu ezabatzeko baimena eman nahi diozu <xliff:g id="APP_NAME_1">^1</xliff:g> aplikazioari?</item>
       <item quantity="one">Elementu hau ezabatzeko baimena eman nahi diozu <xliff:g id="APP_NAME_0">^1</xliff:g> aplikazioari?</item>
     </plurals>
+    <plurals name="permission_progress_delete_generic" formatted="false" msgid="1006212243422543162">
+      <item quantity="other"><xliff:g id="COUNT">^1</xliff:g> elementu ezabatzen…</item>
+      <item quantity="one">Elementua ezabatzen…</item>
+    </plurals>
+    <string name="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 63ef764..deb3250 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="one">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه می‌دهید <xliff:g id="COUNT">^2</xliff:g> فایل صوتی را تغییر دهد؟</item>
       <item 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 39517ba..9e65ae7 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -47,64 +47,136 @@
       <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 021ba70..de7245c 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -47,64 +47,136 @@
       <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 7b0d694..c995739 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -47,64 +47,136 @@
       <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 93e281d..aa53246 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -47,64 +47,136 @@
       <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 d7cc37d..3cffaf6 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ને <xliff:g id="COUNT">^2</xliff:g> ઑડિયો ફાઇલમાં ફેરફાર કરવાની મંજૂરી આપીએ?</item>
       <item 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 3937bd2..5881984 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="one">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइल में बदलाव करने की अनुमति देना चाहते हैं?</item>
       <item 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 6b4abd9..776b290 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -50,79 +50,167 @@
       <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 1e39499..e3ea512 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -47,64 +47,136 @@
       <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 862e27a..095e238 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="one">Թույլատրե՞լ <xliff:g id="APP_NAME_1">^1</xliff:g> հավելվածին վերականգնել <xliff:g id="COUNT">^2</xliff:g> աուդիո ֆայլ աղբարկղից</item>
       <item 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 42406d7..3987c55 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -47,64 +47,136 @@
       <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 fb7daa3..cf0d7c8 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -47,64 +47,136 @@
       <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 9b5d598..7906977 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -47,64 +47,136 @@
       <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 e7011df..4552372 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -53,94 +53,198 @@
       <item quantity="other">לאפשר לאפליקציה <xliff:g id="APP_NAME_1">^1</xliff:g> לשנות <xliff:g 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 481c730..58cc9b0 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -38,7 +38,7 @@
       <item quantity="one">他 <xliff:g id="COUNT_0">^1</xliff:g> 件の項目</item>
     </plurals>
     <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"アプリの一時ファイルの削除"</string>
-    <string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>が一部の一時ファイルを削除する権限を求めています。この処理により、バッテリーやモバイルデータの使用量が増えることがあります。"</string>
+    <string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>が一部の一時ファイルを削除する権限を求めています。この処理により、電池やモバイルデータの使用量が増えることがあります。"</string>
     <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"アプリの一時ファイルを削除しています…"</string>
     <string name="clear" msgid="5524638938415865915">"削除"</string>
     <string name="allow" msgid="8885707816848569619">"許可"</string>
@@ -47,64 +47,136 @@
       <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 420e846..d7d8808 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="other">აძლევთ უფლებას <xliff:g id="APP_NAME_1">^1</xliff:g>-ს, შეცვალოს <xliff:g 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 1b899b2..f6a9252 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> қолданбасына <xliff:g 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 b4a930f..d7662d6 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="other">អនុញ្ញាតឱ្យ <xliff:g id="APP_NAME_1">^1</xliff:g> កែ​ឯកសារសំឡេង <xliff:g 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 99c1788..ca1da24 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -47,64 +47,136 @@
       <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 97fa4ec..afb14cc 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>에서 오디오 파일 <xliff:g 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 5f065b6..f6b244b 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> колдонмосу <xliff:g 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 568d20a..c21aec4 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="other">ອະນຸຍາດໃຫ້ <xliff:g id="APP_NAME_1">^1</xliff:g> ແກ້ໄຂໄຟລ໌ສຽງ <xliff:g 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 4190c0f..c4f30d9 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -53,94 +53,198 @@
       <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 7bc296f..fbd2cba 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -50,79 +50,167 @@
       <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 2fe7609..ca450f7 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -18,7 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="uid_label" msgid="8421971615411294156">"Аудио-визуелни содржини"</string>
     <string name="storage_description" msgid="4081716890357580107">"Локална меморија"</string>
-    <string name="app_label" msgid="9035307001052716210">"Капацитет за аудиовизуелни содржини"</string>
+    <string name="app_label" msgid="9035307001052716210">"Капацитет за аудио-визуелни содржини"</string>
     <string name="artist_label" msgid="8105600993099120273">"Изведувач"</string>
     <string name="unknown" msgid="2059049215682829375">"Непознат"</string>
     <string name="root_images" msgid="5861633549189045666">"Слики"</string>
@@ -47,64 +47,136 @@
       <item quantity="one">Да се дозволи <xliff:g id="APP_NAME_1">^1</xliff:g> да измени <xliff:g id="COUNT">^2</xliff:g> аудиодатотека?</item>
       <item 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 d505748..a11ddef 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -47,64 +47,136 @@
       <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 8c7a39b..3b791d8 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g>-д <xliff:g 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 b110be3..98bef88 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ला <xliff:g 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 067cc43..dc5d040 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -47,64 +47,136 @@
       <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 ab3766c..ce0b311 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> ကို အသံဖိုင် <xliff:g 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 35fd892..3ebc4c1 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -47,64 +47,136 @@
       <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 36d63ba..e93dd86 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -21,7 +21,7 @@
     <string name="app_label" msgid="9035307001052716210">"मिडिया भण्डारण"</string>
     <string name="artist_label" msgid="8105600993099120273">"कलाकार"</string>
     <string name="unknown" msgid="2059049215682829375">"अज्ञात"</string>
-    <string name="root_images" msgid="5861633549189045666">"फोटो"</string>
+    <string name="root_images" msgid="5861633549189045666">"छविहरू"</string>
     <string name="root_videos" msgid="8792703517064649453">"भिडियोहरू"</string>
     <string name="root_audio" msgid="3505830755201326018">"अडियो"</string>
     <string name="root_documents" msgid="3829103301363849237">"कागजातहरू"</string>
@@ -47,64 +47,137 @@
       <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> लाई यी <xliff:g 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>
+    <!-- no translation found for transcode_denied (6760546817138288976) -->
+    <skip />
+    <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-nl/strings.xml b/res/values-nl/strings.xml
index d685675..dcd8a36 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -47,64 +47,136 @@
       <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 5ea6ea3..1e185dc 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -47,64 +47,136 @@
       <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 cb5e9b4..273c6b6 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="one">ਕੀ <xliff:g id="APP_NAME_1">^1</xliff:g> ਨੂੰ <xliff:g id="COUNT">^2</xliff:g> ਆਡੀਓ ਫ਼ਾਈਲ ਨੂੰ ਸੋਧਣ ਦੇਣਾ ਹੈ?</item>
       <item 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 ea4a8ce..d308c74 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -53,94 +53,198 @@
       <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 d0ff6d6..82f8b43 100644
--- a/res/values-pt-rBR/strings.xml
+++ b/res/values-pt-rBR/strings.xml
@@ -47,64 +47,136 @@
       <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 27c1cb0..058b69b 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -47,64 +47,136 @@
       <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 d0ff6d6..82f8b43 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -47,64 +47,136 @@
       <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 8df20b8..4bd0b43 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -50,79 +50,167 @@
       <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 d7a1aaa..054d9fc 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -53,94 +53,198 @@
       <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 ad22297..4b0b9bb 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="one"><xliff:g id="APP_NAME_1">^1</xliff:g>ට ඕඩියෝ ගොනු <xliff:g id="COUNT">^2</xliff:g>ක් වෙනස් කිරීමට ඉඩ දෙන්නද?</item>
       <item 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 359ba24..fc0ea6b 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -53,94 +53,198 @@
       <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 spracúvať 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 ff63498..d4ac3a1 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -53,94 +53,198 @@
       <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 1e90f35..d69a10c 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -47,64 +47,136 @@
       <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 079f9aa..cfc2aa8 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -50,79 +50,167 @@
       <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 b0b990c..a576b61 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -47,64 +47,136 @@
       <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 4917e30..1357d44 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -47,64 +47,136 @@
       <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 f13588a..e1404f6 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -47,64 +47,136 @@
       <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 30c0faa..2b14c3e 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -47,64 +47,136 @@
       <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 db76bee..da8604c 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="other">อนุญาตให้ <xliff:g id="APP_NAME_1">^1</xliff:g> แก้ไขไฟล์เสียง <xliff:g 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 79452b3..804e99e 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -47,64 +47,136 @@
       <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 669eac9..e3efff8 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -47,64 +47,136 @@
       <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 281c8b2..d03fb4c 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -53,94 +53,198 @@
       <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 ad6cdf1..63488cf 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> کو <xliff:g 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 0dcad99..168bbf8 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -47,64 +47,136 @@
       <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 d0cbec7..87f6201 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -47,64 +47,136 @@
       <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 6cd41f7..b1f7f7f 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="other">要允许<xliff:g id="APP_NAME_1">^1</xliff:g>修改这 <xliff:g 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 3243f9b..51d8c9e 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="other">允許 <xliff:g id="APP_NAME_1">^1</xliff:g> 修改 <xliff:g 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 1a37210..a67a29e 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -47,64 +47,136 @@
       <item quantity="other">要允許「<xliff:g id="APP_NAME_1">^1</xliff:g>」修改這 <xliff:g 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 e2f528d..36d01de 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -47,64 +47,136 @@
       <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/strings.xml b/res/values/strings.xml
index d6171ca..98c885b 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -88,21 +88,41 @@
         <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&#8230;</item>
+        <item quantity="other">Modifying <xliff:g id="count" example="42">^1</xliff:g> audio files&#8230;</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&#8230;</item>
+        <item quantity="other">Modifying <xliff:g id="count" example="42">^1</xliff:g> videos&#8230;</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&#8230;</item>
+        <item quantity="other">Modifying <xliff:g id="count" example="42">^1</xliff:g> photos&#8230;</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&#8230;</item>
+        <item quantity="other">Modifying <xliff:g id="count" example="42">^1</xliff:g> items&#8230;</item>
+    </plurals>
 
     <!-- ========================= TRASH STRINGS ========================= -->
 
@@ -111,21 +131,41 @@
         <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&#8230;</item>
+        <item quantity="other">Moving <xliff:g id="count" example="42">^1</xliff:g> audio files to trash&#8230;</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&#8230;</item>
+        <item quantity="other">Moving <xliff:g id="count" example="42">^1</xliff:g> videos to trash&#8230;</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&#8230;</item>
+        <item quantity="other">Moving <xliff:g id="count" example="42">^1</xliff:g> photos to trash&#8230;</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&#8230;</item>
+        <item quantity="other">Moving <xliff:g id="count" example="42">^1</xliff:g> items to trash&#8230;</item>
+    </plurals>
 
     <!-- ========================= UNTRASH STRINGS ========================= -->
 
@@ -134,21 +174,41 @@
         <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&#8230;</item>
+        <item quantity="other">Moving <xliff:g id="count" example="42">^1</xliff:g> audio files out of trash&#8230;</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&#8230;</item>
+        <item quantity="other">Moving <xliff:g id="count" example="42">^1</xliff:g> videos out of trash&#8230;</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&#8230;</item>
+        <item quantity="other">Moving <xliff:g id="count" example="42">^1</xliff:g> photos out of trash&#8230;</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&#8230;</item>
+        <item quantity="other">Moving <xliff:g id="count" example="42">^1</xliff:g> items out of trash&#8230;</item>
+    </plurals>
 
     <!-- ========================= DELETE STRINGS ========================= -->
 
@@ -157,22 +217,67 @@
         <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&#8230;</item>
+        <item quantity="other">Deleting <xliff:g id="count" example="42">^1</xliff:g> audio files&#8230;</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&#8230;</item>
+        <item quantity="other">Deleting <xliff:g id="count" example="42">^1</xliff:g> videos&#8230;</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&#8230;</item>
+        <item quantity="other">Deleting <xliff:g id="count" example="42">^1</xliff:g> photos&#8230;</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&#8230;</item>
+        <item quantity="other">Deleting <xliff:g id="count" example="42">^1</xliff:g> items&#8230;</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&#8230;</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/sqlite3.sh b/sqlite3.sh
index 7f7780b..cf9b4a5 100644
--- a/sqlite3.sh
+++ b/sqlite3.sh
@@ -59,6 +59,26 @@
     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 eb3b923..00bbc7a 100644
--- a/src/com/android/providers/media/DatabaseHelper.java
+++ b/src/com/android/providers/media/DatabaseHelper.java
@@ -22,6 +22,7 @@
 
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
+import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
@@ -35,8 +36,10 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Environment;
+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;
@@ -59,19 +62,24 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.modules.utils.BackgroundThread;
+import com.android.providers.media.playlist.Playlist;
 import com.android.providers.media.util.DatabaseUtils;
 import com.android.providers.media.util.FileUtils;
 import com.android.providers.media.util.ForegroundThread;
 import com.android.providers.media.util.Logging;
 import com.android.providers.media.util.MimeUtils;
 
+import com.google.common.collect.Iterables;
+
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.FilenameFilter;
 import java.io.IOException;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 import java.util.UUID;
@@ -96,6 +104,8 @@
      */
     public static final String CURRENT_GENERATION_CLAUSE = "SELECT generation FROM local_metadata";
 
+    private static final int NOTIFY_BATCH_SIZE = 256;
+
     final Context mContext;
     final String mName;
     final int mVersion;
@@ -109,17 +119,11 @@
     final @Nullable OnLegacyMigrationListener mMigrationListener;
     final @Nullable UnaryOperator<String> mIdGenerator;
     final Set<String> mFilterVolumeNames = new ArraySet<>();
+    private final String mMigrationFileName;
     long mScanStartTime;
     long mScanStopTime;
 
     /**
-     * Flag indicating that this database should invoke
-     * {@link #migrateFromLegacy} to migrate from a legacy database, typically
-     * only set when this database is starting from scratch.
-     */
-    boolean mMigrateFromLegacy;
-
-    /**
      * Lock used to guard against deadlocks in SQLite; the write lock is used to
      * guard any schema changes, and the read lock is used for all other
      * database operations.
@@ -132,9 +136,12 @@
      */
     private final ReentrantReadWriteLock mSchemaLock = new ReentrantReadWriteLock();
 
+    private static Object sMigrationLockInternal = new Object();
+    private static Object sMigrationLockExternal = new Object();
+
     public interface OnSchemaChangeListener {
         public void onSchemaChange(@NonNull String volumeName, int versionFrom, int versionTo,
-                long itemCount, long durationMillis);
+                long itemCount, long durationMillis, String databaseUuid);
     }
 
     public interface OnFilesChangeListener {
@@ -186,6 +193,7 @@
         mFilesListener = filesListener;
         mMigrationListener = migrationListener;
         mIdGenerator = idGenerator;
+        mMigrationFileName = "." + mVolumeName;
 
         // Configure default filters until we hear differently
         if (mInternal) {
@@ -364,9 +372,19 @@
     @Override
     public void onOpen(final SQLiteDatabase db) {
         Log.v(TAG, "onOpen() for " + mName);
-        if (mMigrateFromLegacy) {
-            // Clear flag, since we should only attempt once
-            mMigrateFromLegacy = false;
+
+        tryMigrateFromLegacy(db, mInternal ? sMigrationLockInternal : sMigrationLockExternal);
+    }
+
+    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.
+        synchronized (migrationLock) {
+            if (!migration.exists()) {
+                Log.v(TAG, "onOpen() finished for " + mName);
+                return;
+            }
 
             mSchemaLock.writeLock().lock();
             try {
@@ -376,9 +394,11 @@
                 createLatestIndexes(db, mInternal);
             } finally {
                 mSchemaLock.writeLock().unlock();
+                // Clear flag, since we should only attempt once
+                migration.delete();
+                Log.v(TAG, "onOpen() finished for " + mName);
             }
         }
-        Log.v(TAG, "onOpen() finished for " + mName);
     }
 
     @GuardedBy("mProjectionMapCache")
@@ -409,7 +429,7 @@
                     } catch (ReflectiveOperationException e) {
                         throw new RuntimeException(e);
                     }
-                   mProjectionMapCache.put(clazz, map);
+                    mProjectionMapCache.put(clazz, map);
                 }
                 result.putAll(map);
             }
@@ -520,7 +540,6 @@
             for (int i = 0; i < state.blockingTasks.size(); i++) {
                 state.blockingTasks.get(i).run();
             }
-
             // We carefully "phase" our two sets of work here to ensure that we
             // completely finish dispatching all change notifications before we
             // process background tasks, to ensure that the background work
@@ -635,7 +654,9 @@
     private void notifyChangeInternal(@NonNull Collection<Uri> uris, int flags) {
         Trace.beginSection("notifyChange");
         try {
-            mContext.getContentResolver().notifyChange(uris, null, flags);
+            for (List<Uri> partition : Iterables.partition(uris, NOTIFY_BATCH_SIZE)) {
+                mContext.getContentResolver().notifyChange(partition, null, flags);
+            }
         } finally {
             Trace.endSection();
         }
@@ -694,6 +715,11 @@
 
     @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);
@@ -791,7 +817,11 @@
                 + "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)");
+                + "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 (!mInternal) {
@@ -807,7 +837,11 @@
         // 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
         if (!mLegacyProvider) {
-            mMigrateFromLegacy = true;
+            try {
+                new File(mContext.getFilesDir(), mMigrationFileName).createNewFile();
+            } catch (IOException e) {
+                Log.e(TAG, "Failed to create a migration file: ." + mVolumeName, e);
+            }
         }
     }
 
@@ -838,7 +872,9 @@
 
             db.beginTransaction();
             Log.d(TAG, "Starting migration from legacy provider");
-            mMigrationListener.onStarted(client, mVolumeName);
+            if (mMigrationListener != null) {
+                mMigrationListener.onStarted(client, mVolumeName);
+            }
             try (Cursor c = client.query(queryUri, sMigrateColumns.toArray(new String[0]),
                     extras, null)) {
                 final ContentValues values = new ContentValues();
@@ -850,24 +886,65 @@
                     final String data = c.getString(c.getColumnIndex(MediaColumns.DATA));
                     values.put(MediaColumns.DATA, data);
                     FileUtils.computeValuesFromData(values, /*isForFuse*/ false);
+                    final String volumeNameFromPath = values.getAsString(MediaColumns.VOLUME_NAME);
                     for (String column : sMigrateColumns) {
                         DatabaseUtils.copyFromCursorToContentValues(column, c, values);
                     }
+                    final String volumeNameMigrated = values.getAsString(MediaColumns.VOLUME_NAME);
+                    // While upgrading from P OS or below, VOLUME_NAME can be NULL in legacy
+                    // database. When VOLUME_NAME is NULL, extract VOLUME_NAME from
+                    // MediaColumns.DATA
+                    if (volumeNameMigrated == null || volumeNameMigrated.isEmpty()) {
+                        values.put(MediaColumns.VOLUME_NAME, volumeNameFromPath);
+                    }
+
+                    final String volumePath = FileUtils.extractVolumePath(data);
+
+                    // Handle playlist files which may need special handling if
+                    // there are no "real" playlist files.
+                    final int mediaType = c.getInt(c.getColumnIndex(FileColumns.MEDIA_TYPE));
+                    if (!mInternal && volumePath != null &&
+                            mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) {
+                        File playlistFile = new File(data);
+
+                        if (!playlistFile.exists()) {
+                            if (LOGV) Log.v(TAG, "Migrating playlist file " + playlistFile);
+
+                            // Migrate virtual playlists to a "real" playlist file.
+                            // Also change playlist file name and path to adapt to new
+                            // default primary directory.
+                            String playlistFilePath = data;
+                            try {
+                                playlistFilePath = migratePlaylistFiles(client,
+                                        c.getLong(c.getColumnIndex(FileColumns._ID)));
+                                // Either migration didn't happen or is not necessary because
+                                // playlist file already exists
+                                if (playlistFilePath == null) playlistFilePath = data;
+                            } catch (Exception e) {
+                                // We only have one shot to migrate data, so log and
+                                // keep marching forward.
+                                Log.w(TAG, "Couldn't migrate playlist file " + data);
+                            }
+
+                            values.put(FileColumns.DATA, playlistFilePath);
+                            FileUtils.computeValuesFromData(values, /*isForFuse*/ false);
+                        }
+                    }
 
                     // When migrating pending or trashed files, we might need to
                     // rename them on disk to match new schema
-                    final String volumePath = FileUtils.extractVolumePath(data);
                     if (volumePath != null) {
+                        final String oldData = values.getAsString(MediaColumns.DATA);
                         FileUtils.computeDataFromValues(values, new File(volumePath),
                                 /*isForFuse*/ false);
                         final String recomputedData = values.getAsString(MediaColumns.DATA);
-                        if (!Objects.equals(data, recomputedData)) {
+                        if (!Objects.equals(oldData, recomputedData)) {
                             try {
-                                renameWithRetry(data, recomputedData);
+                                renameWithRetry(oldData, recomputedData);
                             } catch (IOException e) {
                                 // We only have one shot to migrate data, so log and
                                 // keep marching forward
-                                Log.wtf(TAG, "Failed to rename " + values + "; continuing", e);
+                                Log.w(TAG, "Failed to rename " + values + "; continuing", e);
                                 FileUtils.computeValuesFromData(values, /*isForFuse*/ false);
                             }
                         }
@@ -890,7 +967,9 @@
                         final int progress = c.getPosition();
                         final int total = c.getCount();
                         Log.v(TAG, "Migrated " + progress + " of " + total + "...");
-                        mMigrationListener.onProgress(client, mVolumeName, progress, total);
+                        if (mMigrationListener != null) {
+                            mMigrationListener.onProgress(client, mVolumeName, progress, total);
+                        }
                     }
                 }
 
@@ -898,15 +977,146 @@
             } catch (Exception e) {
                 // We have to guard ourselves against any weird behavior of the
                 // legacy provider by trying to catch everything
-                Log.wtf(TAG, "Failed migration from legacy provider", e);
+                Log.w(TAG, "Failed migration from legacy provider", e);
             }
 
             // We tried our best above to migrate everything we could, and we
             // only have one possible shot, so mark everything successful
             db.setTransactionSuccessful();
             db.endTransaction();
-            mMigrationListener.onFinished(client, mVolumeName);
+            if (mMigrationListener != null) {
+                mMigrationListener.onFinished(client, mVolumeName);
+            }
         }
+
+    }
+
+    @Nullable
+    private String migratePlaylistFiles(ContentProviderClient client, long playlistId)
+            throws IllegalStateException {
+        final String selection = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_PLAYLIST
+                + " AND " + FileColumns._ID + "=" + playlistId;
+        final String[] projection = new String[]{
+                FileColumns._ID,
+                FileColumns.DATA,
+                MediaColumns.MIME_TYPE,
+                MediaStore.Audio.PlaylistsColumns.NAME,
+        };
+        final Uri queryUri = MediaStore
+                .rewriteToLegacy(MediaStore.Files.getContentUri(mVolumeName));
+
+        try (Cursor cursor = client.query(queryUri, projection, selection, null, null)) {
+            if (!cursor.moveToFirst()) {
+                throw new IllegalStateException("Couldn't find database row for playlist file"
+                        + playlistId);
+            }
+
+            final String data = cursor.getString(cursor.getColumnIndex(MediaColumns.DATA));
+            File playlistFile = new File(data);
+            if (playlistFile.exists()) {
+                throw new IllegalStateException("Playlist file exists " + data);
+            }
+
+            String mimeType = cursor.getString(cursor.getColumnIndex(MediaColumns.MIME_TYPE));
+            // Sometimes, playlists in Q may have mimeType as
+            // "application/octet-stream". Ensure that playlist rows have the
+            // right playlist mimeType. These rows will be committed to a file
+            // and hence they should have correct playlist mimeType for
+            // Playlist#write to identify the right child playlist class.
+            if (!MimeUtils.isPlaylistMimeType(mimeType)) {
+                // Playlist files should always have right mimeType, default to
+                // audio/mpegurl when mimeType doesn't match playlist media_type.
+                mimeType = "audio/mpegurl";
+            }
+
+            // If the directory is Playlists/ change the directory to Music/
+            // since defaultPrimary for playlists is Music/. This helps
+            // resolve any future app-compat issues around renaming playlist
+            // files.
+            File parentFile = playlistFile.getParentFile();
+            if (parentFile.getName().equalsIgnoreCase("Playlists")) {
+                parentFile = new File(parentFile.getParentFile(), Environment.DIRECTORY_MUSIC);
+            }
+            final String playlistName = cursor.getString(
+                    cursor.getColumnIndex(MediaStore.Audio.PlaylistsColumns.NAME));
+
+            try {
+                // Build playlist file path with a file extension that matches
+                // playlist mimeType.
+                playlistFile = FileUtils.buildUniqueFile(parentFile, mimeType, playlistName);
+            } catch(FileNotFoundException e) {
+                Log.e(TAG, "Couldn't create unique file for " + playlistFile +
+                        ", using actual playlist file name", e);
+            }
+
+            final long rowId = cursor.getLong(cursor.getColumnIndex(FileColumns._ID));
+            final Uri playlistMemberUri = MediaStore.rewriteToLegacy(
+                    MediaStore.Audio.Playlists.Members.getContentUri(mVolumeName, rowId));
+            createPlaylistFile(client, playlistMemberUri, playlistFile);
+            return playlistFile.getAbsolutePath();
+        } catch (RemoteException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    /**
+     * Creates "real" playlist files on disk from the playlist data from the database.
+     */
+    private void createPlaylistFile(ContentProviderClient client, @NonNull Uri playlistMemberUri,
+            @NonNull File playlistFile) throws IllegalStateException {
+        final String[] projection = new String[] {
+                MediaStore.Audio.Playlists.Members.AUDIO_ID,
+                MediaStore.Audio.Playlists.Members.PLAY_ORDER,
+        };
+
+        final Playlist playlist = new Playlist();
+        // Migrating music->playlist association.
+        try (Cursor c = client.query(playlistMemberUri, projection, null, null,
+                Audio.Playlists.Members.DEFAULT_SORT_ORDER)) {
+            while (c.moveToNext()) {
+                // Write these values to the playlist file
+                final long audioId = c.getLong(0);
+                final int playOrder = c.getInt(1);
+
+                final Uri audioFileUri = MediaStore.rewriteToLegacy(ContentUris.withAppendedId(
+                        MediaStore.Files.getContentUri(mVolumeName), audioId));
+                final String audioFilePath = queryForData(client, audioFileUri);
+                if (audioFilePath == null)  {
+                    // This shouldn't happen, we should always find audio file
+                    // unless audio file is removed, and database has stale db
+                    // row. However this shouldn't block creating playlist
+                    // files;
+                    Log.e(TAG, "Couldn't find audio file for " + audioId + ", continuing..");
+                    continue;
+                }
+                playlist.add(playOrder, playlistFile.toPath().getParent().
+                        relativize(new File(audioFilePath).toPath()));
+            }
+
+            try {
+                writeToPlaylistFileWithRetry(playlistFile, playlist);
+            } catch (IOException e) {
+                // We only have one shot to migrate data, so log and
+                // keep marching forward.
+                Log.w(TAG, "Couldn't migrate playlist file " + playlistFile);
+            }
+        } catch (RemoteException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    /**
+     * Return the {@link MediaColumns#DATA} field for the given {@code uri}.
+     */
+    private String queryForData(ContentProviderClient client, @NonNull Uri uri) {
+        try (Cursor c = client.query(uri, new String[] {FileColumns.DATA}, Bundle.EMPTY, null)) {
+            if (c.moveToFirst()) {
+                return c.getString(0);
+            }
+        } catch (Exception e) {
+            Log.w(TAG, "Exception occurred while querying for data file for " + uri, e);
+        }
+        return null;
     }
 
     /**
@@ -931,12 +1141,19 @@
         sMigrateColumns.add(MediaStore.MediaColumns.IS_FAVORITE);
         sMigrateColumns.add(MediaStore.MediaColumns.OWNER_PACKAGE_NAME);
 
+        sMigrateColumns.add(MediaStore.MediaColumns.ORIENTATION);
+        sMigrateColumns.add(MediaStore.Files.FileColumns.PARENT);
+
         sMigrateColumns.add(MediaStore.Audio.AudioColumns.BOOKMARK);
 
         sMigrateColumns.add(MediaStore.Video.VideoColumns.TAGS);
         sMigrateColumns.add(MediaStore.Video.VideoColumns.CATEGORY);
         sMigrateColumns.add(MediaStore.Video.VideoColumns.BOOKMARK);
 
+        // This also migrates MediaStore.Images.ImageColumns.IS_PRIVATE
+        // as they both have the same value "isprivate".
+        sMigrateColumns.add(MediaStore.Video.VideoColumns.IS_PRIVATE);
+
         sMigrateColumns.add(MediaStore.DownloadColumns.DOWNLOAD_URI);
         sMigrateColumns.add(MediaStore.DownloadColumns.REFERER_URI);
     }
@@ -966,7 +1183,7 @@
 
         if (!internal) {
             db.execSQL("CREATE VIEW audio_playlists AS SELECT "
-                    + String.join(",", getProjectionMap(Audio.Playlists.class).keySet())
+                    + getColumnsForCollection(Audio.Playlists.class)
                     + " FROM files WHERE media_type=4");
         }
 
@@ -990,16 +1207,16 @@
                 + "3 AS grouporder FROM searchhelpertitle WHERE (title != '')");
 
         db.execSQL("CREATE VIEW audio AS SELECT "
-                + String.join(",", getProjectionMap(Audio.Media.class).keySet())
+                + getColumnsForCollection(Audio.Media.class)
                 + " FROM files WHERE media_type=2");
         db.execSQL("CREATE VIEW video AS SELECT "
-                + String.join(",", getProjectionMap(Video.Media.class).keySet())
+                + getColumnsForCollection(Video.Media.class)
                 + " FROM files WHERE media_type=3");
         db.execSQL("CREATE VIEW images AS SELECT "
-                + String.join(",", getProjectionMap(Images.Media.class).keySet())
+                + getColumnsForCollection(Images.Media.class)
                 + " FROM files WHERE media_type=1");
         db.execSQL("CREATE VIEW downloads AS SELECT "
-                + String.join(",", getProjectionMap(Downloads.class).keySet())
+                + getColumnsForCollection(Downloads.class)
                 + " FROM files WHERE is_download=1");
 
         db.execSQL("CREATE VIEW audio_artists AS SELECT "
@@ -1009,9 +1226,29 @@
                 + ", 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 volume_name IN " + filterVolumeNames
+                + " WHERE is_music=1 AND is_pending=0 AND is_trashed=0"
+                + " 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
@@ -1026,17 +1263,22 @@
                 + ", MAX(year) AS " + Audio.Albums.LAST_YEAR
                 + ", NULL AS " + Audio.Albums.ALBUM_ART
                 + " FROM audio"
-                + " WHERE is_music=1 AND volume_name IN " + filterVolumeNames
+                + " WHERE is_music=1 AND is_pending=0 AND is_trashed=0"
+                + " AND volume_name IN " + filterVolumeNames
                 + " 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 volume_name IN " + filterVolumeNames
+                + " WHERE is_pending=0 AND is_trashed=0 AND volume_name IN " + filterVolumeNames
                 + " 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'",
@@ -1167,6 +1409,16 @@
         db.execSQL("ALTER TABLE files ADD COLUMN is_audiobook INTEGER DEFAULT 0;");
     }
 
+    private static void updateAddRecording(SQLiteDatabase db, boolean internal) {
+        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, boolean internal) {
         db.execSQL("UPDATE files SET latitude=NULL, longitude=NULL;");
     }
@@ -1216,6 +1468,15 @@
                 + " AND " + MediaColumns.RELATIVE_PATH + " NOT LIKE '%/';");
     }
 
+    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, boolean internal) {
+        db.execSQL("ALTER TABLE files ADD COLUMN _video_codec_type TEXT DEFAULT NULL;");
+    }
+
     private static void updateClearDirectories(SQLiteDatabase db, boolean internal) {
         db.execSQL("UPDATE files SET primary_directory=NULL, secondary_directory=NULL;");
     }
@@ -1295,6 +1556,17 @@
         db.execSQL("UPDATE files SET date_modified=0 WHERE media_type=2;");
     }
 
+    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 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, boolean internal) {
         try (Cursor c = db.query("files", new String[] { FileColumns._ID, FileColumns.DATA },
                 null, null, null, null, null, null)) {
@@ -1320,26 +1592,30 @@
         final String selection = FileColumns.MEDIA_TYPE + "=?";
         final String[] selectionArgs = new String[]{String.valueOf(FileColumns.MEDIA_TYPE_NONE)};
 
+        ArrayMap<Long, Integer> newMediaTypes = new ArrayMap<>();
         try (Cursor c = db.query("files", new String[] { FileColumns._ID, FileColumns.MIME_TYPE },
                 selection, selectionArgs, null, null, null, null)) {
             Log.d(TAG, "Recomputing " + c.getCount() + " MediaType values");
 
-            final ContentValues values = new ContentValues();
+            // Accumulate all the new MEDIA_TYPE updates.
             while (c.moveToNext()) {
-                values.clear();
                 final long id = c.getLong(0);
                 final String mimeType = c.getString(1);
                 // Only update Document and Subtitle media type
-                if (MimeUtils.isDocumentMimeType(mimeType)) {
-                    values.put(FileColumns.MEDIA_TYPE, FileColumns.MEDIA_TYPE_DOCUMENT);
-                } else if (MimeUtils.isSubtitleMimeType(mimeType)) {
-                    values.put(FileColumns.MEDIA_TYPE, FileColumns.MEDIA_TYPE_SUBTITLE);
-                }
-                if (!values.isEmpty()) {
-                    db.update("files", values, "_id=" + id, null);
+                if (MimeUtils.isSubtitleMimeType(mimeType)) {
+                    newMediaTypes.put(id, FileColumns.MEDIA_TYPE_SUBTITLE);
+                } else if (MimeUtils.isDocumentMimeType(mimeType)) {
+                    newMediaTypes.put(id, FileColumns.MEDIA_TYPE_DOCUMENT);
                 }
             }
         }
+        // Now, update all the new MEDIA_TYPE values.
+        final ContentValues values = new ContentValues();
+        for (long id: newMediaTypes.keySet()) {
+            values.clear();
+            values.put(FileColumns.MEDIA_TYPE, newMediaTypes.get(id));
+            db.update("files", values, "_id=" + id, null);
+        }
     }
 
     static final int VERSION_J = 509;
@@ -1351,7 +1627,10 @@
     static final int VERSION_P = 900;
     static final int VERSION_Q = 1023;
     static final int VERSION_R = 1115;
-    static final int VERSION_LATEST = VERSION_R;
+    // Leave some gaps in database version tagging to allow R schema changes
+    // to go independent of S schema changes.
+    static final int VERSION_S = 1209;
+    static final int VERSION_LATEST = VERSION_S;
 
     /**
      * This method takes care of updating all the tables in the database to the
@@ -1496,6 +1775,36 @@
             if (fromVersion < 1115) {
                 updateAudioAlbumId(db, internal);
             }
+            if (fromVersion < 1200) {
+                updateAddTranscodeSatus(db, internal);
+            }
+            if (fromVersion < 1201) {
+                updateAddVideoCodecType(db, internal);
+            }
+            if (fromVersion < 1202) {
+                updateAddModifier(db, internal);
+            }
+            if (fromVersion < 1203) {
+                // Empty version bump to ensure views are recreated
+            }
+            if (fromVersion < 1204) {
+                // Empty version bump to ensure views are recreated
+            }
+            if (fromVersion < 1205) {
+                updateAddRecording(db, internal);
+            }
+            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 this is the legacy database, it's not worth recomputing data
             // values locally, since they'll be recomputed after the migration
@@ -1518,7 +1827,7 @@
         final long elapsedMillis = (SystemClock.elapsedRealtime() - startTime);
         if (mSchemaListener != null) {
             mSchemaListener.onSchemaChange(mVolumeName, fromVersion, toVersion,
-                    getItemCount(db), elapsedMillis);
+                    getItemCount(db), elapsedMillis, getOrCreateUuid(db));
         }
     }
 
@@ -1531,7 +1840,7 @@
         final long elapsedMillis = (SystemClock.elapsedRealtime() - startTime);
         if (mSchemaListener != null) {
             mSchemaListener.onSchemaChange(mVolumeName, fromVersion, toVersion,
-                    getItemCount(db), elapsedMillis);
+                    getItemCount(db), elapsedMillis, getOrCreateUuid(db));
         }
     }
 
@@ -1547,20 +1856,51 @@
         } catch (ErrnoException e) {
             if (e.errno == OsConstants.ENODATA) {
                 // Doesn't exist yet, so generate and persist a UUID
-                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;
+                return resetAndGetUuid(db);
             } else {
                 throw new RuntimeException(e);
             }
         }
     }
 
-    private static final long RENAME_TIMEOUT = 10 * DateUtils.SECOND_IN_MILLIS;
+    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;
+
+    /**
+     * When writing to playlist files during migration, the underlying
+     * pass-through view of storage may not be mounted yet, so we're willing
+     * to retry several times before giving up.
+     * The retry logic is mainly added to avoid test flakiness.
+     */
+    private static void writeToPlaylistFileWithRetry(@NonNull File playlistFile,
+            @NonNull Playlist playlist) throws IOException {
+        final long start = SystemClock.elapsedRealtime();
+        while (true) {
+            if (SystemClock.elapsedRealtime() - start > PASSTHROUGH_WAIT_TIMEOUT) {
+                throw new IOException("Passthrough failed to mount");
+            }
+
+            try {
+                playlistFile.getParentFile().mkdirs();
+                playlistFile.createNewFile();
+                playlist.write(playlistFile);
+                return;
+            } catch (IOException e) {
+                Log.i(TAG, "Failed to migrate playlist file, retrying " + e);
+            }
+            Log.i(TAG, "Waiting for passthrough to be mounted...");
+            SystemClock.sleep(100);
+        }
+    }
 
     /**
      * When renaming files during migration, the underlying pass-through view of
@@ -1571,7 +1911,7 @@
             throws IOException {
         final long start = SystemClock.elapsedRealtime();
         while (true) {
-            if (SystemClock.elapsedRealtime() - start > RENAME_TIMEOUT) {
+            if (SystemClock.elapsedRealtime() - start > PASSTHROUGH_WAIT_TIMEOUT) {
                 throw new IOException("Passthrough failed to mount");
             }
 
diff --git a/src/com/android/providers/media/FileLookupResult.java b/src/com/android/providers/media/FileLookupResult.java
new file mode 100644
index 0000000..f0e6bfb
--- /dev/null
+++ b/src/com/android/providers/media/FileLookupResult.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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
new file mode 100644
index 0000000..1052f98
--- /dev/null
+++ b/src/com/android/providers/media/FileOpenResult.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/IdleService.java b/src/com/android/providers/media/IdleService.java
index 11fe7a0..213d2c1 100644
--- a/src/com/android/providers/media/IdleService.java
+++ b/src/com/android/providers/media/IdleService.java
@@ -51,6 +51,11 @@
     @Override
     public boolean onStopJob(JobParameters params) {
         mSignal.cancel();
+        try (ContentProviderClient cpc = getContentResolver()
+                .acquireContentProviderClient(MediaStore.AUTHORITY)) {
+            ((MediaProvider) cpc.getLocalContentProvider()).onIdleMaintenanceStopped();
+        } catch (OperationCanceledException ignored) {
+        }
         return false;
     }
 
diff --git a/src/com/android/providers/media/LocalCallingIdentity.java b/src/com/android/providers/media/LocalCallingIdentity.java
index 4d3700b..6191457 100644
--- a/src/com/android/providers/media/LocalCallingIdentity.java
+++ b/src/com/android/providers/media/LocalCallingIdentity.java
@@ -17,12 +17,16 @@
 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;
 
+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.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.checkPermissionManager;
 import static com.android.providers.media.util.PermissionUtils.checkPermissionReadAudio;
 import static com.android.providers.media.util.PermissionUtils.checkPermissionReadImages;
@@ -39,6 +43,9 @@
 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;
@@ -51,23 +58,31 @@
 import android.os.UserManager;
 import android.util.ArrayMap;
 
+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 Context context;
     public final int pid;
     public final int uid;
-    public final String packageNameUnchecked;
+    private final UserHandle user;
+    private final Context context;
+    private final String packageNameUnchecked;
     // Info used for logging permission checks
-    public @Nullable String attributionTag;
+    private final @Nullable String attributionTag;
+    private final Object lock = new Object();
 
-    private LocalCallingIdentity(Context context, int pid, int uid, String packageNameUnchecked,
-            @Nullable String attributionTag) {
+    private LocalCallingIdentity(Context context, int pid, int uid, UserHandle user,
+            String packageNameUnchecked, @Nullable String attributionTag) {
         this.context = context;
         this.pid = pid;
         this.uid = uid;
+        this.user = user;
         this.packageNameUnchecked = packageNameUnchecked;
         this.attributionTag = attributionTag;
     }
@@ -84,25 +99,54 @@
 
     private static final long UNKNOWN_ROW_ID = -1;
 
-    public static LocalCallingIdentity fromBinder(Context context, ContentProvider provider) {
+    public static LocalCallingIdentity fromBinder(Context context, ContentProvider provider,
+            UserCache userCache) {
         String callingPackage = provider.getCallingPackageUnchecked();
+        int binderUid = Binder.getCallingUid();
         if (callingPackage == null) {
+            if (binderUid == Process.SYSTEM_UID) {
+                // If UID is system assume we are running as ourself and not handling IPC
+                // Otherwise, we'd crash when we attempt AppOpsManager#checkPackage
+                // in LocalCallingIdentity#getPackageName
+                return fromSelf(context);
+            }
             callingPackage = context.getOpPackageName();
         }
         String callingAttributionTag = provider.getCallingAttributionTag();
         if (callingAttributionTag == null) {
             callingAttributionTag = context.getAttributionTag();
         }
-        return new LocalCallingIdentity(context, Binder.getCallingPid(), Binder.getCallingUid(),
-                callingPackage, callingAttributionTag);
+        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);
     }
 
-    public static LocalCallingIdentity fromExternal(Context context, int uid) {
+    public static LocalCallingIdentity fromExternal(Context context, @Nullable UserCache userCache,
+            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, uid, sharedPackageNames[0], null);
+        LocalCallingIdentity ident = fromExternal(context, userCache, uid, sharedPackageNames[0],
+                null);
         ident.sharedPackageNames = sharedPackageNames;
         ident.sharedPackageNamesResolved = true;
         if (uid == Process.SHELL_UID) {
@@ -112,19 +156,34 @@
                 ident.hasPermissionResolved = PERMISSION_IS_REDACTION_NEEDED;
             }
         }
+
         return ident;
     }
 
-    public static LocalCallingIdentity fromExternal(Context context, int uid, String packageName,
-            @Nullable String attributionTag) {
-        return new LocalCallingIdentity(context, -1, uid, packageName, attributionTag);
+    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 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());
 
@@ -133,6 +192,8 @@
         // 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);
@@ -140,8 +201,8 @@
         return ident;
     }
 
-    private String packageName;
-    private boolean packageNameResolved;
+    private volatile String packageName;
+    private volatile boolean packageNameResolved;
 
     public String getPackageName() {
         if (!packageNameResolved) {
@@ -158,8 +219,8 @@
         return packageNameUnchecked;
     }
 
-    private String[] sharedPackageNames;
-    private boolean sharedPackageNamesResolved;
+    private volatile String[] sharedPackageNames;
+    private volatile boolean sharedPackageNamesResolved;
 
     public String[] getSharedPackageNames() {
         if (!sharedPackageNamesResolved) {
@@ -174,8 +235,8 @@
         return (packageNames != null) ? packageNames : new String[0];
     }
 
-    private int targetSdkVersion;
-    private boolean targetSdkVersionResolved;
+    private volatile int targetSdkVersion;
+    private volatile boolean targetSdkVersionResolved;
 
     public int getTargetSdkVersion() {
         if (!targetSdkVersionResolved) {
@@ -197,6 +258,10 @@
         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;
@@ -214,10 +279,21 @@
     public static final int PERMISSION_WRITE_VIDEO = 1 << 20;
     public static final int PERMISSION_WRITE_IMAGES = 1 << 21;
 
-    public static final int PERMISSION_IS_SYSTEM_GALLERY = 1 <<22;
+    public static final int PERMISSION_IS_SYSTEM_GALLERY = 1 << 22;
+    /**
+     * Explicitly checks **only** for INSTALL_PACKAGES runtime permission.
+     */
+    public static final int PERMISSION_INSTALL_PACKAGES = 1 << 23;
+    public static final int PERMISSION_WRITE_EXTERNAL_STORAGE = 1 << 24;
 
-    private int hasPermission;
-    private int hasPermissionResolved;
+    /**
+     * Checks if REQUEST_INSTALL_PACKAGES app-op is allowed for any package sharing this UID.
+     */
+    public static final int APPOP_REQUEST_INSTALL_PACKAGES_FOR_SHARED_UID = 1 << 25;
+    public static final int PERMISSION_ACCESS_MTP = 1 << 26;
+
+    private volatile int hasPermission;
+    private volatile int hasPermissionResolved;
 
     public boolean hasPermission(int permission) {
         if ((hasPermissionResolved & permission) == 0) {
@@ -256,6 +332,10 @@
             case PERMISSION_IS_LEGACY_WRITE:
                 return isLegacyWriteInternal();
 
+            case PERMISSION_WRITE_EXTERNAL_STORAGE:
+                return checkPermissionWriteStorage(
+                        context, pid, uid, getPackageName(), attributionTag);
+
             case PERMISSION_READ_AUDIO:
                 return checkPermissionReadAudio(
                         context, pid, uid, getPackageName(), attributionTag);
@@ -277,6 +357,15 @@
             case PERMISSION_IS_SYSTEM_GALLERY:
                 return checkWriteImagesOrVideoAppOps(
                         context, uid, getPackageName(), attributionTag);
+            case PERMISSION_INSTALL_PACKAGES:
+                return checkPermissionInstallPackages(
+                        context, pid, uid, getPackageName(), attributionTag);
+            case APPOP_REQUEST_INSTALL_PACKAGES_FOR_SHARED_UID:
+                return checkAppOpRequestInstallPackagesForSharedUid(
+                        context, uid, getSharedPackageNames(), attributionTag);
+            case PERMISSION_ACCESS_MTP:
+                return checkPermissionAccessMtp(
+                        context, pid, uid, getPackageName(), attributionTag);
             default:
                 return false;
         }
@@ -297,7 +386,84 @@
             return true;
         }
 
-        return checkIsLegacyStorageGranted(context, uid, getPackageName());
+        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;
     }
 
     private boolean isScopedStorageEnforced(boolean defaultScopedStorage,
@@ -336,42 +502,70 @@
         return false;
     }
 
-    private LongArray ownedIds = new LongArray();
+    @GuardedBy("lock")
+    private final LongArray ownedIds = new LongArray();
 
     public boolean isOwned(long id) {
-        return ownedIds.indexOf(id) != -1;
+        synchronized (lock) {
+            return ownedIds.indexOf(id) != -1;
+        }
     }
 
     public void setOwned(long id, boolean owned) {
-        final int index = ownedIds.indexOf(id);
-        if (owned) {
-            if (index == -1) {
-                ownedIds.add(id);
-            }
-        } else {
-            if (index != -1) {
-                ownedIds.remove(index);
+        synchronized (lock) {
+            final int index = ownedIds.indexOf(id);
+            if (owned) {
+                if (index == -1) {
+                    ownedIds.add(id);
+                }
+            } else {
+                if (index != -1) {
+                    ownedIds.remove(index);
+                }
             }
         }
     }
 
-    private ArrayMap<String, Long> rowIdOfDeletedPaths = new ArrayMap<>();
+    @GuardedBy("lock")
+    private final ArrayMap<String, Long> rowIdOfDeletedPaths = new ArrayMap<>();
 
     public void addDeletedRowId(@NonNull String path, long id) {
-        rowIdOfDeletedPaths.put(path, id);
+        synchronized (lock) {
+            rowIdOfDeletedPaths.put(path.toLowerCase(Locale.ROOT), id);
+        }
     }
 
     public boolean removeDeletedRowId(long id) {
-        int index = rowIdOfDeletedPaths.indexOfValue(id);
-        final boolean isDeleted = index > -1;
-        while (index > -1) {
-            rowIdOfDeletedPaths.removeAt(index);
-            index = rowIdOfDeletedPaths.indexOfValue(id);
+        synchronized (lock) {
+            int index = rowIdOfDeletedPaths.indexOfValue(id);
+            final boolean isDeleted = index > -1;
+            while (index > -1) {
+                rowIdOfDeletedPaths.removeAt(index);
+                index = rowIdOfDeletedPaths.indexOfValue(id);
+            }
+            return isDeleted;
         }
-        return isDeleted;
     }
 
     public long getDeletedRowId(@NonNull String path) {
-        return rowIdOfDeletedPaths.getOrDefault(path, UNKNOWN_ROW_ID);
+        synchronized (lock) {
+            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 5121a45..c958721 100644
--- a/src/com/android/providers/media/MediaDocumentsProvider.java
+++ b/src/com/android/providers/media/MediaDocumentsProvider.java
@@ -17,11 +17,13 @@
 package com.android.providers.media;
 
 import static android.content.ContentResolver.EXTRA_SIZE;
+import static android.content.ContentResolver.QUERY_ARG_SQL_LIMIT;
 import static android.provider.DocumentsContract.QUERY_ARG_DISPLAY_NAME;
 import static android.provider.DocumentsContract.QUERY_ARG_EXCLUDE_MEDIA;
 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;
@@ -70,6 +72,7 @@
 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;
@@ -429,6 +432,28 @@
     }
 
     @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);
@@ -1017,6 +1042,7 @@
             throws FileNotFoundException {
         enforceShellRestrictions();
         final Uri target = getUriForDocumentId(docId);
+        final int callingUid = Binder.getCallingUid();
 
         if (!"r".equals(mode)) {
             throw new IllegalArgumentException("Media is read-only");
@@ -1025,12 +1051,27 @@
         // Delegate to real provider
         final long token = Binder.clearCallingIdentity();
         try {
-            return getContext().getContentResolver().openFileDescriptor(target, mode);
+            return openFileForRead(target, callingUid);
         } 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 {
@@ -1060,8 +1101,9 @@
     private boolean isEmpty(Uri uri) {
         final ContentResolver resolver = getContext().getContentResolver();
         final long token = Binder.clearCallingIdentity();
-        try (Cursor cursor = resolver.query(uri,
-                new String[] { "COUNT(_id)" }, null, null, null)) {
+        Bundle extras = new Bundle();
+        extras.putString(QUERY_ARG_SQL_LIMIT, "1");
+        try (Cursor cursor = resolver.query(uri, new String[]{FileColumns._ID}, extras, null)) {
             if (cursor.moveToFirst()) {
                 return cursor.getInt(0) == 0;
             } else {
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index 9a38d7e..96e5342 100644
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -23,19 +23,28 @@
 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;
+import static com.android.providers.media.LocalCallingIdentity.APPOP_REQUEST_INSTALL_PACKAGES_FOR_SHARED_UID;
+import static com.android.providers.media.LocalCallingIdentity.PERMISSION_ACCESS_MTP;
+import static com.android.providers.media.LocalCallingIdentity.PERMISSION_INSTALL_PACKAGES;
 import static com.android.providers.media.LocalCallingIdentity.PERMISSION_IS_DELEGATOR;
 import static com.android.providers.media.LocalCallingIdentity.PERMISSION_IS_LEGACY_GRANTED;
 import static com.android.providers.media.LocalCallingIdentity.PERMISSION_IS_LEGACY_READ;
@@ -49,6 +58,7 @@
 import static com.android.providers.media.LocalCallingIdentity.PERMISSION_READ_IMAGES;
 import static com.android.providers.media.LocalCallingIdentity.PERMISSION_READ_VIDEO;
 import static com.android.providers.media.LocalCallingIdentity.PERMISSION_WRITE_AUDIO;
+import static com.android.providers.media.LocalCallingIdentity.PERMISSION_WRITE_EXTERNAL_STORAGE;
 import static com.android.providers.media.LocalCallingIdentity.PERMISSION_WRITE_IMAGES;
 import static com.android.providers.media.LocalCallingIdentity.PERMISSION_WRITE_VIDEO;
 import static com.android.providers.media.scan.MediaScanner.REASON_DEMAND;
@@ -57,15 +67,20 @@
 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;
 import static com.android.providers.media.util.FileUtils.extractRelativePathForDirectory;
 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.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.sanitizePath;
 import static com.android.providers.media.util.Logging.LOGV;
 import static com.android.providers.media.util.Logging.TAG;
@@ -77,6 +92,10 @@
 import android.app.PendingIntent;
 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;
@@ -123,17 +142,23 @@
 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;
@@ -164,13 +189,16 @@
 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.modules.utils.BackgroundThread;
 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.playlist.Playlist;
 import com.android.providers.media.scan.MediaScanner;
 import com.android.providers.media.scan.ModernMediaScanner;
@@ -184,8 +212,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;
@@ -198,17 +226,22 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
 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;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
+import java.util.UUID;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
@@ -225,36 +258,53 @@
  */
 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+)");
 
-    /**
-     * Property that indicates whether fuse is enabled.
-     */
-    private static final String PROP_FUSE = "persist.sys.fuse";
+    /** 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;
 
     /**
      * These directory names aren't declared in Environment as final variables, and so we need to
      * have the same values in separate final variables in order to have them considered constant
      * expressions.
+     * These directory names are intentionally in lower case to ease the case insensitive path
+     * comparison.
      */
-    private static final String DIRECTORY_MUSIC = "Music";
-    private static final String DIRECTORY_PODCASTS = "Podcasts";
-    private static final String DIRECTORY_RINGTONES = "Ringtones";
-    private static final String DIRECTORY_ALARMS = "Alarms";
-    private static final String DIRECTORY_NOTIFICATIONS = "Notifications";
-    private static final String DIRECTORY_PICTURES = "Pictures";
-    private static final String DIRECTORY_MOVIES = "Movies";
-    private static final String DIRECTORY_DOWNLOADS = "Download";
-    private static final String DIRECTORY_DCIM = "DCIM";
-    private static final String DIRECTORY_DOCUMENTS = "Documents";
-    private static final String DIRECTORY_AUDIOBOOKS = "Audiobooks";
-    private static final String DIRECTORY_ANDROID = "Android";
+    private static final String DIRECTORY_MUSIC_LOWER_CASE = "music";
+    private static final String DIRECTORY_PODCASTS_LOWER_CASE = "podcasts";
+    private static final String DIRECTORY_RINGTONES_LOWER_CASE = "ringtones";
+    private static final String DIRECTORY_ALARMS_LOWER_CASE = "alarms";
+    private static final String DIRECTORY_NOTIFICATIONS_LOWER_CASE = "notifications";
+    private static final String DIRECTORY_PICTURES_LOWER_CASE = "pictures";
+    private static final String DIRECTORY_MOVIES_LOWER_CASE = "movies";
+    private static final String DIRECTORY_DOWNLOADS_LOWER_CASE = "download";
+    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
@@ -282,6 +332,8 @@
      */
     private static final int MATCH_VISIBLE_FOR_FILEPATH = 32;
 
+    private static final int NON_HIDDEN_CACHE_SIZE = 50;
+
     /**
      * Where clause to match pending files from FUSE. Pending files from FUSE will not have
      * PATTERN_PENDING_FILEPATH_FOR_SQL pattern.
@@ -290,11 +342,35 @@
             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();
+
+    /**
      * Set of {@link Cursor} columns that refer to raw filesystem paths.
      */
     private static final ArrayMap<String, Object> sDataColumns = new ArrayMap<>();
 
-    {
+    static {
         sDataColumns.put(MediaStore.MediaColumns.DATA, null);
         sDataColumns.put(MediaStore.Images.Thumbnails.DATA, null);
         sDataColumns.put(MediaStore.Video.Thumbnails.DATA, null);
@@ -302,57 +378,32 @@
         sDataColumns.put(MediaStore.Audio.AlbumColumns.ALBUM_ART, null);
     }
 
-    private static final Object sCacheLock = new Object();
+    private static final int sUserId = UserHandle.myUserId();
 
-    @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<>();
+    /**
+     * Please use {@link getDownloadsProviderAuthority()} instead of using this directly.
+     */
+    private static final String DOWNLOADS_PROVIDER_AUTHORITY = "downloads";
 
-    @GuardedBy("mShouldRedactThreadIds")
-    private final LongArray mShouldRedactThreadIds = new LongArray();
+    @GuardedBy("mPendingOpenInfo")
+    private final Map<Integer, PendingOpenInfo> mPendingOpenInfo = new ArrayMap<>();
+
+    @GuardedBy("mNonHiddenPaths")
+    private final LRUCache<String, Integer> mNonHiddenPaths = new LRUCache<>(NON_HIDDEN_CACHE_SIZE);
 
     public void updateVolumes() {
-        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);
-                }
-            }
-        }
-
+        mVolumeCache.update();
         // Update filters to reflect mounted volumes so users don't get
         // confused by metadata from ejected volumes
         ForegroundThread.getExecutor().execute(() -> {
-            mExternalDatabase.setFilterVolumeNames(getExternalVolumeNames());
+            mExternalDatabase.setFilterVolumeNames(mVolumeCache.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
@@ -360,60 +411,56 @@
             return Environment.getExternalStorageDirectory();
         }
 
-        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;
-        }
+        return mVolumeCache.getVolumePath(volumeName, mCallingIdentity.get().getUser());
     }
 
     public @NonNull String getVolumeId(@NonNull File file) throws FileNotFoundException {
-        synchronized (sCacheLock) {
-            for (int i = 0; i < sCachedVolumePathToId.size(); i++) {
-                if (FileUtils.contains(sCachedVolumePathToId.keyAt(i), file)) {
-                    return sCachedVolumePathToId.valueAt(i);
-                }
-            }
-
-            // 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 mVolumeCache.getVolumeId(file);
     }
 
-    public @NonNull Set<String> getExternalVolumeNames() {
-        synchronized (sCacheLock) {
-            return new ArraySet<>(sCachedExternalVolumeNames);
-        }
-    }
-
-    public @NonNull Collection<File> getVolumeScanPaths(String volumeName)
+    private @NonNull Collection<File> getAllowedVolumePaths(String volumeName)
             throws FileNotFoundException {
-        synchronized (sCacheLock) {
-            if (sCachedVolumeScanPaths.containsKey(volumeName)) {
-                return new ArrayList<>(sCachedVolumeScanPaths.get(volumeName));
-            }
-
-            // 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;
+        // 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);
         }
+
+        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 void onAnrDelayStarted(@NonNull String packageName, int uid, int tid, int reason) {
+        mTranscodeHelper.onAnrDelayStarted(packageName, uid, tid, reason);
+    }
+
+    private volatile Locale mLastLocale = Locale.getDefault();
+
     private StorageManager mStorageManager;
     private AppOpsManager mAppOpsManager;
     private PackageManager mPackageManager;
+    private DevicePolicyManager mDevicePolicyManager;
+    private UserManager mUserManager;
 
+    private UserCache mUserCache;
+    private VolumeCache mVolumeCache;
+
+    private int mExternalStorageAuthorityAppId;
+    private int mDownloadsAuthorityAppId;
     private Size mThumbSize;
 
     /**
@@ -429,8 +476,8 @@
             if (active) {
                 // TODO moltmann: Set correct featureId
                 mCachedCallingIdentity.put(uid,
-                        LocalCallingIdentity.fromExternal(getContext(), uid, packageName,
-                                null));
+                        LocalCallingIdentity.fromExternal(getContext(), mUserCache, uid,
+                            packageName, null));
             } else {
                 mCachedCallingIdentity.remove(uid);
             }
@@ -449,6 +496,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.
@@ -456,12 +506,19 @@
     private LocalCallingIdentity getCachedCallingIdentityForFuse(int uid) {
         synchronized (mCachedCallingIdentityForFuse) {
             PermissionUtils.setOpDescription("via FUSE");
-            LocalCallingIdentity ident = mCachedCallingIdentityForFuse.get(uid);
-            if (ident == null) {
-               ident = LocalCallingIdentity.fromExternal(getContext(), uid);
-               mCachedCallingIdentityForFuse.put(uid, ident);
+            LocalCallingIdentity identity = mCachedCallingIdentityForFuse.get(uid);
+            if (identity == null) {
+               identity = LocalCallingIdentity.fromExternal(getContext(), mUserCache, uid);
+               if (uid / PER_USER_RANGE == sUserId) {
+                   mCachedCallingIdentityForFuse.put(uid, identity);
+               } else {
+                   // In some app cloning designs, MediaProvider user 0 may
+                   // serve requests for apps running as a "clone" user; in
+                   // those cases, don't keep a cache for the clone user, since
+                   // we don't get any invalidation events for these users.
+               }
             }
-            return ident;
+            return identity;
         }
     }
 
@@ -477,7 +534,7 @@
                     final LocalCallingIdentity cached = mCachedCallingIdentity
                             .get(Binder.getCallingUid());
                     return (cached != null) ? cached
-                            : LocalCallingIdentity.fromBinder(getContext(), this);
+                            : LocalCallingIdentity.fromBinder(getContext(), this, mUserCache);
                 }
             });
 
@@ -514,6 +571,8 @@
 
     private static final String CANONICAL = "canonical";
 
+    private static final String ALL_VOLUMES = "all_volumes";
+
     private BroadcastReceiver mPackageReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -562,8 +621,9 @@
             }
 
             updateQuotaTypeForFileInternal(file, mediaType);
-        } catch (FileNotFoundException e) {
+        } catch (FileNotFoundException | IllegalArgumentException e) {
             // Ignore
+            Log.w(TAG, "Failed to update quota for uri: " + uri, e);
             return;
         } finally {
             Trace.endSection();
@@ -654,6 +714,9 @@
                 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
@@ -666,6 +729,14 @@
                     Trace.endSection();
                 }
 
+                switch (mediaType) {
+                    case FileColumns.MEDIA_TYPE_PLAYLIST:
+                    case FileColumns.MEDIA_TYPE_AUDIO:
+                        if (helper.isExternal()) {
+                            removePlaylistMembers(mediaType, id);
+                        }
+                }
+
                 // Invalidate any thumbnails now that media is gone
                 invalidateThumbnails(MediaStore.Files.getContentUri(volumeName, id));
 
@@ -731,6 +802,11 @@
             case FileColumns.MEDIA_TYPE_IMAGE:
                 consumer.accept(MediaStore.Images.Media.getContentUri(volumeName, id));
                 break;
+
+            case FileColumns.MEDIA_TYPE_PLAYLIST:
+                consumer.accept(ContentUris.withAppendedId(
+                        MediaStore.Audio.Playlists.getContentUri(volumeName), id));
+                break;
         }
 
         // Also notify through any generic views
@@ -757,39 +833,22 @@
      * devices. We only do this once per volume so we don't annoy the user if
      * deleted manually.
      */
-    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;
-            }
+    private void ensureDefaultFolders(@NonNull MediaVolume volume, @NonNull SQLiteDatabase db) {
+        final String key = "created_default_folders_" + volume.getId();
 
-            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());
-                    }
+        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());
                 }
-
-                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);
+
+            SharedPreferences.Editor editor = prefs.edit();
+            editor.putInt(key, 1);
+            editor.commit();
         }
     }
 
@@ -799,10 +858,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 String volumeName, @NonNull SQLiteDatabase db) {
+    private void ensureThumbnailsValid(@NonNull MediaVolume volume, @NonNull SQLiteDatabase db) {
         final String uuidFromDatabase = DatabaseHelper.getOrCreateUuid(db);
         try {
-            for (File dir : getThumbnailDirectories(volumeName)) {
+            for (File dir : getThumbnailDirectories(volume)) {
                 if (!dir.exists()) {
                     dir.mkdirs();
                 }
@@ -830,7 +889,7 @@
                 }
             }
         } catch (IOException e) {
-            Log.w(TAG, "Failed to ensure thumbnails valid for " + volumeName, e);
+            Log.w(TAG, "Failed to ensure thumbnails valid for " + volume.getName(), e);
         }
     }
 
@@ -847,12 +906,17 @@
     public boolean onCreate() {
         final Context context = getContext();
 
+        mUserCache = new UserCache(context);
+
         // Shift call statistics back to the original caller
         Binder.setProxyTransactListener(mTransactListener);
 
         mStorageManager = context.getSystemService(StorageManager.class);
         mAppOpsManager = context.getSystemService(AppOpsManager.class);
         mPackageManager = context.getPackageManager();
+        mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
+        mUserManager = context.getSystemService(UserManager.class);
+        mVolumeCache = new VolumeCache(context, mUserCache);
 
         // Reasonable thumbnail size is half of the smallest screen edge width
         final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
@@ -868,6 +932,15 @@
                 false, false, false, Column.class,
                 Metrics::logSchemaChange, mFilesListener, MIGRATION_LISTENER, mIdGenerator);
 
+        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();
+
         final IntentFilter packageFilter = new IntentFilter();
         packageFilter.setPriority(10);
         packageFilter.addDataScheme("package");
@@ -885,9 +958,9 @@
                 });
 
         updateVolumes();
-        attachVolume(MediaStore.VOLUME_INTERNAL, /* validate */ false);
-        for (String volumeName : getExternalVolumeNames()) {
-            attachVolume(volumeName, /* validate */ false);
+        attachVolume(MediaVolume.fromInternal(), /* validate */ false);
+        for (MediaVolume volume : mVolumeCache.getExternalVolumes()) {
+            attachVolume(volume, /* validate */ false);
         }
 
         // Watch for performance-sensitive activity
@@ -918,11 +991,26 @@
             // 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(PermissionUtils.OPSTR_NO_ISOLATED_STORAGE,
+            mAppOpsManager.startWatchingMode(AppOpsManager.OPSTR_NO_ISOLATED_STORAGE,
                     null /* all packages */, mModeListener);
         } catch (IllegalArgumentException e) {
-            Log.w(TAG, "Failed to start watching " + PermissionUtils.OPSTR_NO_ISOLATED_STORAGE, e);
+            Log.w(TAG, "Failed to start watching " + AppOpsManager.OPSTR_NO_ISOLATED_STORAGE, e);
         }
+
+        ProviderInfo provider = mPackageManager.resolveContentProvider(
+                getDownloadsProviderAuthority(), 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);
+        if (provider != null) {
+            mExternalStorageAuthorityAppId = UserHandle.getAppId(provider.applicationInfo.uid);
+        }
+
+        PulledMetrics.initialize(context);
         return true;
     }
 
@@ -933,7 +1021,11 @@
     }
 
     public LocalCallingIdentity clearLocalCallingIdentity() {
-        return clearLocalCallingIdentity(LocalCallingIdentity.fromSelf(getContext()));
+        // 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));
     }
 
     public LocalCallingIdentity clearLocalCallingIdentity(LocalCallingIdentity replacement) {
@@ -974,19 +1066,19 @@
         Logging.trimPersistent();
 
         // Scan all volumes to resolve any staleness
-        for (String volumeName : getExternalVolumeNames()) {
+        for (MediaVolume volume : mVolumeCache.getExternalVolumes()) {
             // Possibly bail before digging into each volume
             signal.throwIfCanceled();
 
             try {
-                MediaService.onScanVolume(getContext(), volumeName, REASON_IDLE);
+                MediaService.onScanVolume(getContext(), volume, REASON_IDLE);
             } catch (IOException e) {
                 Log.w(TAG, e);
             }
 
             // Ensure that our thumbnails are valid
             mExternalDatabase.runWithTransaction((db) -> {
-                ensureThumbnailsValid(volumeName, db);
+                ensureThumbnailsValid(volume, db);
                 return null;
             });
         }
@@ -1018,23 +1110,11 @@
         });
         Log.d(TAG, "Pruned " + stalePackages + " unknown packages");
 
-        // Delete any expired content; we're cautious about wildly changing
-        // clocks, so only delete items within the last week
-        final long from = ((System.currentTimeMillis() - DateUtils.WEEK_IN_MILLIS) / 1000);
-        final long to = (System.currentTimeMillis() / 1000);
-        final int expiredMedia = mExternalDatabase.runWithTransaction((db) -> {
-            try (Cursor c = db.query(true, "files", new String[] { "volume_name", "_id" },
-                    FileColumns.DATE_EXPIRES + " BETWEEN " + from + " AND " + to, null,
-                    null, null, null, null, signal)) {
-                while (c.moveToNext()) {
-                    final String volumeName = c.getString(0);
-                    final long id = c.getLong(1);
-                    delete(Files.getContentUri(volumeName, id), null, null);
-                }
-                return c.getCount();
-            }
-        });
-        Log.d(TAG, "Deleted " + expiredMedia + " expired items");
+        // Delete the expired items or extend them on mounted volumes
+        final int[] result = deleteOrExtendExpiredItems(signal);
+        final int deletedExpiredMedia = result[0];
+        Log.d(TAG, "Deleted " + deletedExpiredMedia + " expired items");
+        Log.d(TAG, "Extended " + result[1] + " expired items");
 
         // Forget any stale volumes
         mExternalDatabase.runWithTransaction((db) -> {
@@ -1068,9 +1148,94 @@
 
         final long durationMillis = (SystemClock.elapsedRealtime() - startTime);
         Metrics.logIdleMaintenance(MediaStore.VOLUME_EXTERNAL, itemCount,
-                durationMillis, staleThumbnails, expiredMedia);
+                durationMillis, staleThumbnails, deletedExpiredMedia);
     }
 
+    /**
+     * Delete any expired content on mounted volumes. The expired content on unmounted
+     * volumes will be deleted when we forget any stale volumes; we're cautious about
+     * wildly changing clocks, so only delete items within the last week.
+     * If the items are expired more than one week, extend the expired time of them
+     * another one week to avoid data loss with incorrect time zone data. We will
+     * delete it when it is expired next time.
+     *
+     * @param signal the cancellation signal
+     * @return the integer array includes total deleted count and total extended count
+     */
+    @NonNull
+    private int[] deleteOrExtendExpiredItems(@NonNull CancellationSignal signal) {
+        final long expiredOneWeek =
+                ((System.currentTimeMillis() - DateUtils.WEEK_IN_MILLIS) / 1000);
+        final long now = (System.currentTimeMillis() / 1000);
+        final Long extendedTime = now + (FileUtils.DEFAULT_DURATION_EXTENDED / 1000);
+        final int result[] = mExternalDatabase.runWithTransaction((db) -> {
+            String selection = FileColumns.DATE_EXPIRES + " < " + now;
+            selection += " AND volume_name in " + bindList(MediaStore.getExternalVolumeNames(
+                    getContext()).toArray());
+            String[] projection = new String[]{"volume_name", "_id",
+                    FileColumns.DATE_EXPIRES, FileColumns.DATA};
+            try (Cursor c = db.query(true, "files", projection, selection, null, null, null, null,
+                    null, signal)) {
+                int totalDeleteCount = 0;
+                int totalExtendedCount = 0;
+                while (c.moveToNext()) {
+                    final String volumeName = c.getString(0);
+                    final long id = c.getLong(1);
+                    final long dateExpires = c.getLong(2);
+                    // we only delete the items that expire in one week
+                    if (dateExpires > expiredOneWeek) {
+                        totalDeleteCount += delete(Files.getContentUri(volumeName, id), null, null);
+                    } else {
+                        final String oriPath = c.getString(3);
+                        final boolean success = extendExpiredItem(db, oriPath, id, extendedTime);
+                        if (success) {
+                            totalExtendedCount++;
+                        }
+                    }
+                }
+                return new int[]{totalDeleteCount, totalExtendedCount};
+            }
+        });
+        return result;
+    }
+
+    /**
+     * 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 extendedTime) {
+        final String newPath = FileUtils.getAbsoluteExtendedPath(originalPath, extendedTime);
+        if (newPath == null) {
+            return false;
+        }
+
+        try {
+            Os.rename(originalPath, newPath);
+            invalidateFuseDentry(originalPath);
+            invalidateFuseDentry(newPath);
+        } catch (ErrnoException e) {
+            final String errorMessage = "Rename " + originalPath + " to " + newPath + " failed.";
+            Log.e(TAG, errorMessage, e);
+            return false;
+        }
+
+        final ContentValues values = new ContentValues();
+        values.put(FileColumns.DATA, newPath);
+        values.put(FileColumns.DATE_EXPIRES, extendedTime);
+        final int count = db.update("files", values, "_id=?", new String[]{String.valueOf(id)});
+        return count == 1;
+    }
+
+    public void onIdleMaintenanceStopped() {
+        mMediaScanner.onIdleScanStopped();
+    }
+
+    /**
+     * Orphan any content of the given package. This will delete Android/media orphaned files from
+     * the database.
+     */
     public void onPackageOrphaned(String packageName) {
         mExternalDatabase.runWithTransaction((db) -> {
             onPackageOrphaned(db, packageName);
@@ -1078,14 +1243,28 @@
         });
     }
 
+    /**
+     * Orphan any content of the given package from the given database. This will delete
+     * Android/media orphaned files from the database.
+     */
     public void onPackageOrphaned(@NonNull SQLiteDatabase db, @NonNull String packageName) {
+        // Delete files from Android/media.
+        String relativePath = "Android/media/" + DatabaseUtils.escapeForLike(packageName) + "/%";
+        final int countDeleted = db.delete(
+                "files",
+                "relative_path LIKE ? ESCAPE '\\' AND owner_package_name=?",
+                new String[] {relativePath, packageName});
+        Log.d(TAG, "Deleted " + countDeleted + " Android/media items belonging to "
+                + packageName + " on " + db.getPath());
+
+        // Orphan rest of files.
         final ContentValues values = new ContentValues();
         values.putNull(FileColumns.OWNER_PACKAGE_NAME);
 
-        final int count = db.update("files", values,
+        final int countOrphaned = db.update("files", values,
                 "owner_package_name=?", new String[] { packageName });
-        if (count > 0) {
-            Log.d(TAG, "Orphaned " + count + " items belonging to "
+        if (countOrphaned > 0) {
+            Log.d(TAG, "Orphaned " + countOrphaned + " items belonging to "
                     + packageName + " on " + db.getPath());
         }
     }
@@ -1095,22 +1274,20 @@
     }
 
     public Uri scanFile(File file, int reason) {
-        return mMediaScanner.scanFile(file, reason);
+        return scanFile(file, reason, null);
     }
 
     public Uri scanFile(File file, int reason, String ownerPackage) {
         return mMediaScanner.scanFile(file, reason, ownerPackage);
     }
 
-    /**
-     * Makes MediaScanner scan the given file.
-     * @param file path of the file to be scanned
-     *
-     * Called from JNI in jni/MediaProviderWrapper.cpp
-     */
-    @Keep
-    public void scanFileForFuse(String file) {
-        scanFile(new File(file), REASON_DEMAND);
+    private Uri scanFileAsMediaProvider(File file, int reason) {
+        final LocalCallingIdentity tokenInner = clearLocalCallingIdentity();
+        try {
+            return scanFile(file, REASON_DEMAND);
+        } finally {
+            restoreLocalCallingIdentity(tokenInner);
+        }
     }
 
     /**
@@ -1130,6 +1307,282 @@
         });
     }
 
+    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);
+            return (Boolean) isAppCloneUserPair.invoke(mStorageManager, userId1, userId2);
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            Log.w(TAG, "isAppCloneUserPair failed. Users: " + userId1 + " and " + userId2);
+            return false;
+        }
+    }
+
+    /**
+     * Determines whether the passed in userId forms an app clone user pair with user 0.
+     *
+     * @param userId user ID to check
+     *
+     * Called from JNI in jni/MediaProviderWrapper.cpp
+     */
+    @Keep
+    public boolean isAppCloneUserForFuse(int userId) {
+        if (!isCrossUserEnabled()) {
+            Log.d(TAG, "CrossUser not enabled.");
+            return false;
+        }
+        boolean result = isAppCloneUserPair(0, userId);
+
+        Log.w(TAG, "isAppCloneUserPair for user " + userId + ": " + result);
+
+        return result;
+    }
+
+    /**
+     * Determines if to allow FUSE_LOOKUP for uid. Might allow uids that don't belong to the
+     * MediaProvider user, depending on OEM configuration.
+     *
+     * @param uid linux uid to check
+     *
+     * Called from JNI in jni/MediaProviderWrapper.cpp
+     */
+    @Keep
+    public boolean shouldAllowLookupForFuse(int uid, int pathUserId) {
+        int callingUserId = uid / PER_USER_RANGE;
+        if (!isCrossUserEnabled()) {
+            Log.d(TAG, "CrossUser not enabled. Users: " + callingUserId + " and " + pathUserId);
+            return false;
+        }
+
+        if (callingUserId != pathUserId && callingUserId != 0 && pathUserId != 0) {
+            Log.w(TAG, "CrossUser at least one user is 0 check failed. Users: " + callingUserId
+                    + " and " + pathUserId);
+            return false;
+        }
+
+        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);
+            return false;
+        }
+
+        boolean result = isAppCloneUserPair(pathUserId, callingUserId);
+        if (result) {
+            Log.i(TAG, "CrossUser allowed. Users: " + callingUserId + " and " + pathUserId);
+        } else {
+            Log.w(TAG, "CrossUser isAppCloneUserPair check failed. Users: " + callingUserId
+                    + " and " + pathUserId);
+        }
+
+        return result;
+    }
+
+    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;
+            }
+        }
+
+        synchronized (mNonWorkProfileUsers) {
+            mNonWorkProfileUsers.add(userId);
+            return false;
+        }
+    }
+
+    /**
+     * 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);
+                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;
+        }
+    }
+
     /**
      * Returns true if the app denoted by the given {@code uid} and {@code packageName} is allowed
      * to clear other apps' cache directories.
@@ -1342,16 +1795,23 @@
      * <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)) {
-            final String callingPackage = getCallingPackageOrSelf();
+            // 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 matchSharedPackagesClause = FileColumns.OWNER_PACKAGE_NAME + " IN "
                     + getSharedPackages();
             // Include owned pending files from Fuse
-            return String.format("%s=0 OR (%s=1 AND %s AND %s)", column, column,
+            final String pendingFromFuse = String.format("(%s=1 AND %s AND %s)", column,
                     MATCH_PENDING_FROM_FUSE, matchSharedPackagesClause);
+            return "(" + notPending + " OR " + pendingFromFuse + ")";
         }
         return column + "=0";
     }
@@ -1473,22 +1933,25 @@
     public String[] getFilesInDirectoryForFuse(String path, int uid) {
         final LocalCallingIdentity token =
                 clearLocalCallingIdentity(getCachedCallingIdentityForFuse(uid));
+        PulledMetrics.logFileAccessViaFuse(getCallingUidOrSelf(), path);
 
         try {
-            if (isPrivatePackagePathNotOwnedByCaller(path)) {
-                return new String[] {""};
-            }
-
-            // Do not allow apps to list Android/data or Android/obb dirs. Installer and
-            // MOUNT_EXTERNAL_ANDROID_WRITABLE apps won't be blocked by this, as their OBB dirs
-            // are mounted to lowerfs directly.
-            if (isDataOrObbPath(path)) {
+            if (isPrivatePackagePathNotAccessibleByCaller(path)) {
                 return new String[] {""};
             }
 
             if (shouldBypassFuseRestrictions(/*forWrite*/ false, path)) {
                 return new String[] {"/"};
             }
+
+            // Do not allow apps to list Android/data or Android/obb dirs.
+            // On primary volumes, apps that get special access to these directories get it via
+            // mount views of lowerfs. On secondary volumes, such apps would return early from
+            // shouldBypassFuseRestrictions above.
+            if (isDataOrObbPath(path)) {
+                return new String[] {""};
+            }
+
             // 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()) {
@@ -1540,13 +2003,8 @@
      * </ul>
      */
     private void scanRenamedDirectoryForFuse(@NonNull String oldPath, @NonNull String newPath) {
-        final LocalCallingIdentity token = clearLocalCallingIdentity();
-        try {
-            scanFile(new File(oldPath), REASON_DEMAND);
-            scanFile(new File(newPath), REASON_DEMAND);
-        } finally {
-            restoreLocalCallingIdentity(token);
-        }
+        scanFileAsMediaProvider(new File(oldPath), REASON_DEMAND);
+        scanFileAsMediaProvider(new File(newPath), REASON_DEMAND);
     }
 
     /**
@@ -1626,16 +2084,25 @@
         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) {
-        final Uri uriOldPath = FileUtils.getContentUriForPath(oldPath);
+            @NonNull Bundle qbExtras, Uri uriOldPath) {
         boolean allowHidden = isCallingPackageAllowedHidden();
         final SQLiteQueryBuilder qbForUpdate = getQueryBuilder(TYPE_UPDATE,
                 matchUri(uriOldPath, allowHidden), uriOldPath, qbExtras, null);
+        if (values.containsKey(FileColumns._MODIFIER)) {
+            qbForUpdate.allowColumn(FileColumns._MODIFIER);
+        }
         final String selection = MediaColumns.DATA + " =? ";
         int count = 0;
         boolean retryUpdateWithReplace = false;
@@ -1670,17 +2137,25 @@
      * Gets {@link ContentValues} for updating database entry to {@code path}.
      */
     private ContentValues getContentValuesForFuseRename(String path, String newMimeType,
-            boolean checkHidden) {
+            boolean wasHidden, boolean isHidden, boolean isSameMimeType) {
         ContentValues values = new ContentValues();
         values.put(MediaColumns.MIME_TYPE, newMimeType);
         values.put(MediaColumns.DATA, path);
 
-        if (checkHidden && shouldFileBeHidden(new File(path))) {
+        if (isHidden) {
             values.put(FileColumns.MEDIA_TYPE, FileColumns.MEDIA_TYPE_NONE);
         } else {
             int mediaType = MimeUtils.resolveMediaType(newMimeType);
             values.put(FileColumns.MEDIA_TYPE, mediaType);
         }
+
+        if ((!isHidden && wasHidden) || !isSameMimeType) {
+            // Set the modifier as MODIFIER_FUSE so that apps can scan the file to update the
+            // metadata. Otherwise, scan will skip scanning this file because rename() doesn't
+            // change lastModifiedTime and scan assumes there is no change in the file.
+            values.put(FileColumns._MODIFIER, FileColumns._MODIFIER_FUSE);
+        }
+
         final boolean allowHidden = isCallingPackageAllowedHidden();
         if (!newMimeType.equalsIgnoreCase("null") &&
                 matchUri(getContentUriForFile(path, newMimeType), allowHidden) == AUDIO_MEDIA) {
@@ -1694,12 +2169,12 @@
     private ArrayList<String> getIncludedDefaultDirectories() {
         final ArrayList<String> includedDefaultDirs = new ArrayList<>();
         if (checkCallingPermissionVideo(/*forWrite*/ true, null)) {
-            includedDefaultDirs.add(DIRECTORY_DCIM);
-            includedDefaultDirs.add(DIRECTORY_PICTURES);
-            includedDefaultDirs.add(DIRECTORY_MOVIES);
+            includedDefaultDirs.add(Environment.DIRECTORY_DCIM);
+            includedDefaultDirs.add(Environment.DIRECTORY_PICTURES);
+            includedDefaultDirs.add(Environment.DIRECTORY_MOVIES);
         } else if (checkCallingPermissionImages(/*forWrite*/ true, null)) {
-            includedDefaultDirs.add(DIRECTORY_DCIM);
-            includedDefaultDirs.add(DIRECTORY_PICTURES);
+            includedDefaultDirs.add(Environment.DIRECTORY_DCIM);
+            includedDefaultDirs.add(Environment.DIRECTORY_PICTURES);
         }
         return includedDefaultDirs;
     }
@@ -1864,12 +2339,15 @@
             final Bundle qbExtras = new Bundle();
             qbExtras.putStringArrayList(INCLUDED_DEFAULT_DIRECTORIES,
                     getIncludedDefaultDirectories());
+            final boolean wasHidden = FileUtils.isDirectoryHidden(new File(oldPath));
+            final boolean isHidden = FileUtils.isDirectoryHidden(new File(newPath));
             for (String filePath : fileList) {
                 final String newFilePath = newPath + "/" + filePath;
                 final String mimeType = MimeUtils.resolveMimeType(new File(newFilePath));
                 if(!updateDatabaseForFuseRename(helper, oldPath + "/" + filePath, newFilePath,
-                        getContentValuesForFuseRename(newFilePath, mimeType,
-                                false /* checkHidden  - will be fixed up below */), qbExtras)) {
+                        getContentValuesForFuseRename(newFilePath, mimeType, wasHidden, isHidden,
+                                /* isSameMimeType */ true),
+                        qbExtras)) {
                     Log.e(TAG, "Calling package doesn't have write permission to rename file.");
                     return OsConstants.EPERM;
                 }
@@ -1943,14 +2421,24 @@
             throw new IllegalStateException("Failed to update database row with " + oldPath, e);
         }
 
+        final boolean wasHidden = shouldFileBeHidden(new File(oldPath));
+        final boolean isHidden = shouldFileBeHidden(new File(newPath));
         helper.beginTransaction();
         try {
             final String newMimeType = MimeUtils.resolveMimeType(new File(newPath));
-            if (!updateDatabaseForFuseRename(helper, oldPath, newPath,
-                    getContentValuesForFuseRename(newPath, newMimeType, true /* checkHidden */))) {
+            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 (!bypassRestrictions) {
-                    Log.e(TAG, "Calling package doesn't have write permission to rename file.");
-                    return OsConstants.EPERM;
+                    // 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;
+                    }
                 } else if (!maybeRemoveOwnerPackageForFuseRename(helper, newPath)) {
                     Log.wtf(TAG, "Couldn't clear owner package name for " + newPath);
                     return OsConstants.EPERM;
@@ -1978,15 +2466,31 @@
         // 3) /sdcard/foo/bar.mp3 => /sdcard/foo/.nomedia
         //    in this case, we need to scan all of /sdcard/foo
         if (extractDisplayName(oldPath).equals(".nomedia")) {
-            scanFile(new File(oldPath).getParentFile(), REASON_DEMAND);
+            scanFileAsMediaProvider(new File(oldPath).getParentFile(), REASON_DEMAND);
         }
         if (extractDisplayName(newPath).equals(".nomedia")) {
-            scanFile(new File(newPath).getParentFile(), REASON_DEMAND);
+            scanFileAsMediaProvider(new File(newPath).getParentFile(), REASON_DEMAND);
         }
+
         return 0;
     }
 
     /**
+     * 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.
@@ -2022,10 +2526,11 @@
         final String errorMessage = "Rename " + oldPath + " to " + newPath + " failed. ";
         final LocalCallingIdentity token =
                 clearLocalCallingIdentity(getCachedCallingIdentityForFuse(uid));
+        PulledMetrics.logFileAccessViaFuse(getCallingUidOrSelf(), oldPath);
 
         try {
-            if (isPrivatePackagePathNotOwnedByCaller(oldPath)
-                    || isPrivatePackagePathNotOwnedByCaller(newPath)) {
+            if (isPrivatePackagePathNotAccessibleByCaller(oldPath)
+                    || isPrivatePackagePathNotAccessibleByCaller(newPath)) {
                 return OsConstants.EACCES;
             }
 
@@ -2034,7 +2539,7 @@
                 return OsConstants.EPERM;
             }
 
-            if (shouldBypassDatabaseForFuse(uid)) {
+            if (shouldBypassDatabaseAndSetDirtyForFuse(uid, newPath)) {
                 return renameInLowerFs(oldPath, newPath);
             }
 
@@ -2072,10 +2577,11 @@
                 return OsConstants.EPERM;
             }
 
+            // TODO(b/177049768): We shouldn't use getExternalStorageDirectory for these checks.
             final File directoryAndroid = new File(Environment.getExternalStorageDirectory(),
-                    DIRECTORY_ANDROID);
+                    DIRECTORY_ANDROID_LOWER_CASE);
             final File directoryAndroidMedia = new File(directoryAndroid, DIRECTORY_MEDIA);
-            if (directoryAndroidMedia.getAbsolutePath().equals(oldPath)) {
+            if (directoryAndroidMedia.getAbsolutePath().equalsIgnoreCase(oldPath)) {
                 // Don't allow renaming 'Android/media' directory.
                 // Android/[data|obb] are bind mounted and these paths don't go through FUSE.
                 Log.e(TAG, errorMessage +  oldPath + " is a default folder in app external "
@@ -2115,7 +2621,16 @@
     public int checkUriPermission(@NonNull Uri uri, int uid,
             /* @Intent.AccessUriMode */ int modeFlags) {
         final LocalCallingIdentity token = clearLocalCallingIdentity(
-                LocalCallingIdentity.fromExternal(getContext(), uid));
+                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);
+        }
 
         try {
             final boolean allowHidden = isCallingPackageAllowedHidden();
@@ -2178,9 +2693,14 @@
 
     @Override
     public Cursor query(Uri uri, String[] projection, Bundle queryArgs, CancellationSignal signal) {
+        return query(uri, projection, queryArgs, signal, /* forSelf */ false);
+    }
+
+    private Cursor query(Uri uri, String[] projection, Bundle queryArgs,
+            CancellationSignal signal, boolean forSelf) {
         Trace.beginSection("query");
         try {
-            return queryInternal(uri, projection, queryArgs, signal);
+            return queryInternal(uri, projection, queryArgs, signal, forSelf);
         } catch (FallbackException e) {
             return e.translateForQuery(getCallingPackageTargetSdkVersion());
         } finally {
@@ -2189,7 +2709,9 @@
     }
 
     private Cursor queryInternal(Uri uri, String[] projection, Bundle queryArgs,
-            CancellationSignal signal) throws FallbackException {
+            CancellationSignal signal, boolean forSelf) throws FallbackException {
+        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.
@@ -2198,9 +2720,17 @@
         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);
@@ -2290,8 +2820,14 @@
             }
         }
 
+        // Update locale if necessary.
+        if (helper == mInternalDatabase && !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) {
+        if (c != null && !forSelf) {
             // As a performance optimization, only configure notifications when
             // resulting cursor will leave our process
             final boolean callerIsRemote = mCallingIdentity.get().pid != android.os.Process.myPid();
@@ -2304,9 +2840,131 @@
                     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);
@@ -2410,13 +3068,25 @@
                 defaultMimeType = "audio/mpeg";
                 defaultMediaType = FileColumns.MEDIA_TYPE_AUDIO;
                 defaultPrimary = Environment.DIRECTORY_MUSIC;
-                allowedPrimary = Arrays.asList(
-                        Environment.DIRECTORY_ALARMS,
-                        Environment.DIRECTORY_AUDIOBOOKS,
-                        Environment.DIRECTORY_MUSIC,
-                        Environment.DIRECTORY_NOTIFICATIONS,
-                        Environment.DIRECTORY_PODCASTS,
-                        Environment.DIRECTORY_RINGTONES);
+                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);
+                }
                 break;
             case VIDEO_MEDIA:
             case VIDEO_MEDIA_ID:
@@ -2563,6 +3233,7 @@
         mimeType = values.getAsString(MediaColumns.MIME_TYPE);
         // Quick 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
@@ -2641,9 +3312,9 @@
 
             // Next, consider allowing based on allowed primary directory
             final String[] relativePath = values.getAsString(MediaColumns.RELATIVE_PATH).split("/");
-            final String primary = (relativePath.length > 0) ? relativePath[0] : null;
+            final String primary = extractTopLevelDir(relativePath);
             if (!validPath) {
-                validPath = allowedPrimary.contains(primary);
+                validPath = containsIgnoreCase(allowedPrimary, primary);
             }
 
             // Next, consider allowing paths when referencing a related item
@@ -2711,20 +3382,33 @@
                                 + "; allowed directories are " + allowedPrimary);
             }
 
+            boolean isFuseThread = isFuseThread();
+            // Check if the following are true:
+            // 1. Not a FUSE thread
+            // 2. |res| is a child of a default dir and the default dir is missing
+            // If true, we want to update the mTime of the volume root, after creating the dir
+            // on the lower filesystem. This fixes some FileManagers relying on the mTime change
+            // for UI updates
+            File defaultDirVolumePath =
+                    isFuseThread ? null : checkDefaultDirMissing(resolvedVolumeName, res);
             // Ensure all parent folders of result file exist
             res.getParentFile().mkdirs();
             if (!res.getParentFile().exists()) {
                 throw new IllegalStateException("Failed to create directory: " + res);
             }
+            touchFusePath(defaultDirVolumePath);
+
             values.put(MediaColumns.DATA, res.getAbsolutePath());
             // buildFile may have changed the file name, compute values to extract new DISPLAY_NAME.
             // Note: We can't extract displayName from res.getPath() because for pending & trashed
             // files DISPLAY_NAME will not be same as file name.
-            FileUtils.computeValuesFromData(values, isFuseThread());
+            FileUtils.computeValuesFromData(values, isFuseThread);
         } else {
             assertFileColumnsConsistent(match, uri, values);
         }
 
+        assertPrivatePathNotInValues(values);
+
         // Drop columns that aren't relevant for special tables
         switch (match) {
             case AUDIO_ALBUMART:
@@ -2744,6 +3428,76 @@
     }
 
     /**
+     * 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.
+     */
+    private File checkDefaultDirMissing(String volumeName, File file) {
+        String topLevelDir = FileUtils.extractTopLevelDir(file.getPath());
+        if (topLevelDir != null && FileUtils.isDefaultDirectoryName(topLevelDir)) {
+            try {
+                File volumePath = getVolumePath(volumeName);
+                if (!new File(volumePath, topLevelDir).exists()) {
+                    return volumePath;
+                }
+            } catch (FileNotFoundException e) {
+                Log.w(TAG, "Failed to checkDefaultDirMissing for " + file, e);
+            }
+        }
+        return null;
+    }
+
+    /** Updates mTime of {@code path} on the FUSE filesystem */
+    private void touchFusePath(@Nullable File path) {
+        if (path != null) {
+            // Touch root of volume to update mTime on FUSE filesystem
+            // This allows FileManagers that may be relying on mTime changes to update their UI
+            File fusePath = getFuseFile(path);
+            if (fusePath != null) {
+                Log.i(TAG, "Touching FUSE path " + fusePath);
+                fusePath.setLastModified(System.currentTimeMillis());
+            }
+        }
+    }
+
+    /**
      * Check that any requested {@link MediaColumns#DATA} paths actually
      * live on the storage volume being targeted.
      */
@@ -2754,7 +3508,7 @@
         final String volumeName = resolveVolumeName(uri);
         try {
             // Quick check that the requested path actually lives on volume
-            final Collection<File> allowed = getVolumeScanPaths(volumeName);
+            final Collection<File> allowed = getAllowedVolumePaths(volumeName);
             final File actual = new File(values.getAsString(MediaColumns.DATA))
                     .getCanonicalFile();
             if (!FileUtils.contains(allowed, actual)) {
@@ -2766,7 +3520,7 @@
     }
 
     @Override
-    public int bulkInsert(Uri uri, ContentValues values[]) {
+    public int bulkInsert(Uri uri, ContentValues[] values) {
         final int targetSdkVersion = getCallingPackageTargetSdkVersion();
         final boolean allowHidden = isCallingPackageAllowedHidden();
         final int match = matchUri(uri, allowHidden);
@@ -2775,6 +3529,28 @@
             return super.bulkInsert(uri, values);
         }
 
+        if (match == AUDIO_PLAYLISTS_ID || match == AUDIO_PLAYLISTS_ID_MEMBERS) {
+            final String resolvedVolumeName = resolveVolumeName(uri);
+
+            final long playlistId = Long.parseLong(uri.getPathSegments().get(3));
+            final Uri playlistUri = ContentUris.withAppendedId(
+                    MediaStore.Audio.Playlists.getContentUri(resolvedVolumeName), playlistId);
+
+            final String audioVolumeName =
+                    MediaStore.VOLUME_INTERNAL.equals(resolvedVolumeName)
+                            ? MediaStore.VOLUME_INTERNAL : MediaStore.VOLUME_EXTERNAL;
+
+            // Require that caller has write access to underlying media
+            enforceCallingPermission(playlistUri, Bundle.EMPTY, true);
+            for (ContentValues each : values) {
+                final long audioId = each.getAsLong(Audio.Playlists.Members.AUDIO_ID);
+                final Uri audioUri = Audio.Media.getContentUri(audioVolumeName, audioId);
+                enforceCallingPermission(audioUri, Bundle.EMPTY, false);
+            }
+
+            return bulkInsertPlaylist(playlistUri, values);
+        }
+
         final DatabaseHelper helper;
         try {
             helper = getDatabaseForUri(uri);
@@ -2792,6 +3568,25 @@
         }
     }
 
+    private int bulkInsertPlaylist(@NonNull Uri uri, @NonNull ContentValues[] values) {
+        Trace.beginSection("bulkInsertPlaylist");
+        try {
+            try {
+                return addPlaylistMembers(uri, values);
+            } catch (SQLiteConstraintException e) {
+                if (getCallingPackageTargetSdkVersion() >= Build.VERSION_CODES.R) {
+                    throw e;
+                } else {
+                    return 0;
+                }
+            }
+        } catch (FallbackException e) {
+            return e.translateForBulkInsert(getCallingPackageTargetSdkVersion());
+        } finally {
+            Trace.endSection();
+        }
+    }
+
     private long insertDirectory(@NonNull SQLiteDatabase db, @NonNull String path) {
         if (LOGV) Log.v(TAG, "inserting directory " + path);
         ContentValues values = new ContentValues();
@@ -2948,8 +3743,15 @@
     }
 
     public void onLocaleChanged() {
+        onLocaleChanged(true);
+    }
+
+    private void onLocaleChanged(boolean forceUpdate) {
         mInternalDatabase.runWithTransaction((db) -> {
-            localizeTitles(db);
+            if (forceUpdate || !mLastLocale.equals(Locale.getDefault())) {
+                localizeTitles(db);
+                mLastLocale = Locale.getDefault();
+            }
             return null;
         });
     }
@@ -3047,7 +3849,30 @@
             values.put(FileColumns.MEDIA_TYPE, mediaType);
         }
 
+        qb.allowColumn(FileColumns._MODIFIER);
+        if (isCallingPackageSelf() && values.containsKey(FileColumns._MODIFIER)) {
+            // We can't identify if the call is coming from media scan, hence
+            // we let ModernMediaScanner send FileColumns._MODIFIER value.
+        } else if (isFuseThread()) {
+            values.put(FileColumns._MODIFIER, FileColumns._MODIFIER_FUSE);
+        } else {
+            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.mInternal) {
+            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);
@@ -3074,6 +3899,12 @@
                         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);
@@ -3084,7 +3915,7 @@
             }
         }
 
-        return ContentUris.withAppendedId(uri, rowId);
+        return ContentUris.withAppendedId(newUri, rowId);
     }
 
     /**
@@ -3253,7 +4084,12 @@
 
     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);
@@ -3262,7 +4098,6 @@
         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()
@@ -3278,15 +4113,23 @@
 
         if (match == VOLUMES) {
             String name = initialValues.getAsString("name");
-            Uri attachedVolume = attachVolume(name, /* validate */ true);
-            if (mMediaScannerVolume != null && mMediaScannerVolume.equals(name)) {
-                final DatabaseHelper helper = getDatabaseForUri(
-                        MediaStore.Files.getContentUri(mMediaScannerVolume));
-                helper.mScanStartTime = SystemClock.elapsedRealtime();
+            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;
             }
-            return attachedVolume;
         }
 
+        final DatabaseHelper helper = getDatabaseForUri(uri);
         switch (match) {
             case AUDIO_PLAYLISTS_ID:
             case AUDIO_PLAYLISTS_ID_MEMBERS: {
@@ -3296,8 +4139,11 @@
 
                 final long audioId = initialValues
                         .getAsLong(MediaStore.Audio.Playlists.Members.AUDIO_ID);
+                final String audioVolumeName =
+                        MediaStore.VOLUME_INTERNAL.equals(resolvedVolumeName)
+                                ? MediaStore.VOLUME_INTERNAL : MediaStore.VOLUME_EXTERNAL;
                 final Uri audioUri = ContentUris.withAppendedId(
-                        MediaStore.Audio.Media.getContentUri(resolvedVolumeName), audioId);
+                        MediaStore.Audio.Media.getContentUri(audioVolumeName), audioId);
 
                 // Require that caller has write access to underlying media
                 enforceCallingPermission(playlistUri, Bundle.EMPTY, true);
@@ -3307,6 +4153,8 @@
                 // files on disk to ensure that we can reliably migrate between
                 // devices and recover from database corruption
                 final long id = addPlaylistMembers(playlistUri, initialValues);
+                acceptWithExpansion(helper::notifyInsert, resolvedVolumeName, playlistId,
+                        FileColumns.MEDIA_TYPE_PLAYLIST, false);
                 return ContentUris.withAppendedId(MediaStore.Audio.Playlists.Members
                         .getContentUri(originalVolumeName, playlistId), id);
             }
@@ -3388,7 +4236,6 @@
         long rowId = -1;
         Uri newUri = null;
 
-        final DatabaseHelper helper = getDatabaseForUri(uri);
         final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_INSERT, match, uri, extras, null);
 
         switch (match) {
@@ -3541,7 +4388,7 @@
         mCallingIdentity.get().setOwned(rowId, true);
 
         if (path != null && path.toLowerCase(Locale.ROOT).endsWith("/.nomedia")) {
-            mMediaScanner.scanFile(new File(path).getParentFile(), REASON_DEMAND);
+            scanFileAsMediaProvider(new File(path).getParentFile(), REASON_DEMAND);
         }
 
         return newUri;
@@ -3624,6 +4471,14 @@
         }
     }
 
+    /**
+     * 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();
@@ -3649,6 +4504,23 @@
     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>
@@ -3702,7 +4574,7 @@
         final String volumeName = MediaStore.getVolumeName(uri);
         final String includeVolumes;
         if (MediaStore.VOLUME_EXTERNAL.equals(volumeName)) {
-            includeVolumes = bindList(getExternalVolumeNames().toArray());
+            includeVolumes = bindList(mVolumeCache.getExternalVolumeNames().toArray());
         } else {
             includeVolumes = bindList(volumeName);
         }
@@ -3710,7 +4582,18 @@
         final String matchSharedPackagesClause = FileColumns.OWNER_PACKAGE_NAME + " IN "
                 + sharedPackages;
 
-        final boolean allowGlobal = checkCallingPermissionGlobal(uri, forWrite);
+        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 allowLegacy =
                 forWrite ? isCallingPackageLegacyWrite() : isCallingPackageLegacyRead();
         final boolean allowLegacyRead = allowLegacy && !forWrite;
@@ -3742,7 +4625,9 @@
         // Handle callers using legacy filtering
         final String filter = uri.getQueryParameter("filter");
 
-        boolean includeAllVolumes = false;
+        // 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 String callingPackage = getCallingPackageOrSelf();
 
         switch (match) {
@@ -3945,6 +4830,13 @@
                     qb.setProjectionMap(projectionMap);
 
                     appendWhereStandalone(qb, "audio._id = audio_id");
+                    // Since we use audio table along with audio_playlists_map
+                    // for querying, we should only include database rows of
+                    // the attached volumes.
+                    if (!includeAllVolumes) {
+                        appendWhereStandalone(qb, FileColumns.VOLUME_NAME + " IN "
+                             + includeVolumes);
+                    }
                 } else {
                     qb.setTables("audio_playlists_map");
                     qb.setProjectionMap(getProjectionMap(Audio.Playlists.Members.class));
@@ -3980,7 +4872,7 @@
             }
             case AUDIO_ARTISTS_ID_ALBUMS: {
                 if (type == TYPE_QUERY) {
-                    qb.setTables("audio_albums");
+                    qb.setTables("audio_artists_albums");
                     qb.setProjectionMap(getProjectionMap(Audio.Artists.Albums.class));
 
                     final String artistId = uri.getPathSegments().get(3);
@@ -4213,6 +5105,36 @@
     }
 
     /**
+     * @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.
      */
@@ -4258,7 +5180,17 @@
 
     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);
@@ -4267,7 +5199,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:
@@ -4295,7 +5227,6 @@
 
         int count = 0;
 
-        final String volumeName = getVolumeName(uri);
         final int targetSdkVersion = getCallingPackageTargetSdkVersion();
 
         // handle MEDIA_SCANNER before calling getDatabaseForUri()
@@ -4318,6 +5249,7 @@
             count = 1;
         }
 
+        final DatabaseHelper helper = getDatabaseForUri(uri);
         switch (match) {
             case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
                 extras.putString(QUERY_ARG_SQL_SELECTION,
@@ -4331,11 +5263,15 @@
                 // Playlist contents are always persisted directly into playlist
                 // files on disk to ensure that we can reliably migrate between
                 // devices and recover from database corruption
-                return removePlaylistMembers(playlistUri, extras);
+                int numOfRemovedPlaylistMembers = removePlaylistMembers(playlistUri, extras);
+                if (numOfRemovedPlaylistMembers > 0) {
+                    acceptWithExpansion(helper::notifyDelete, volumeName, playlistId,
+                            FileColumns.MEDIA_TYPE_PLAYLIST, false);
+                }
+                return numOfRemovedPlaylistMembers;
             }
         }
 
-        final DatabaseHelper helper = getDatabaseForUri(uri);
         final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_DELETE, match, uri, extras, null);
 
         {
@@ -4357,6 +5293,7 @@
             };
             final boolean isFilesTable = qb.getTables().equals("files");
             final LongSparseArray<String> deletedDownloadIds = new LongSparseArray<>();
+            final int[] countPerMediaType = new int[FileColumns.MEDIA_TYPE_COUNT];
             if (isFilesTable) {
                 String deleteparam = uri.getQueryParameter(MediaStore.PARAM_DELETE_DATA);
                 if (deleteparam == null || ! deleteparam.equals("false")) {
@@ -4370,34 +5307,22 @@
                             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);
 
                             deleteIfAllowed(uri, extras, data);
-                            count += qb.delete(helper, BaseColumns._ID + "=" + id, null);
-
-                            // Only need to inform DownloadProvider about the downloads deleted on
-                            // external volume.
-                            if (isDownload == 1) {
-                                deletedDownloadIds.put(id, mimeType);
+                            int res = qb.delete(helper, BaseColumns._ID + "=" + id, null);
+                            count += res;
+                            // Avoid ArrayIndexOutOfBounds if more mediaTypes are added,
+                            // but mediaTypeSize is not updated
+                            if (res > 0 && mediaType < countPerMediaType.length) {
+                                countPerMediaType[mediaType] += res;
                             }
 
-                            // Update any playlists that reference this item
-                            if ((mediaType == FileColumns.MEDIA_TYPE_AUDIO)
-                                    && helper.isExternal()) {
-                                helper.runWithTransaction((db) -> {
-                                    try (Cursor cc = db.query("audio_playlists_map",
-                                            new String[] { "playlist_id" }, "audio_id=" + id,
-                                            null, "playlist_id", null, null)) {
-                                        while (cc.moveToNext()) {
-                                            final Uri playlistUri = ContentUris.withAppendedId(
-                                                    Playlists.getContentUri(volumeName),
-                                                    cc.getLong(0));
-                                            resolvePlaylistMembers(playlistUri);
-                                        }
-                                    }
-                                    return null;
-                                });
+                            if (isDownload == 1) {
+                                deletedDownloadIds.put(id, mimeType);
                             }
                         }
                     } finally {
@@ -4438,23 +5363,73 @@
             }
 
             if (deletedDownloadIds.size() > 0) {
-                // Do this on a background thread, since we don't want to make binder
-                // calls as part of a FUSE call.
-                helper.postBackground(() -> {
-                    getContext().getSystemService(DownloadManager.class)
-                            .onMediaStoreDownloadsDeleted(deletedDownloadIds);
-                });
+                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);
             }
 
             if (isFilesTable && !isCallingPackageSelf()) {
                 Metrics.logDeletion(volumeName, mCallingIdentity.get().uid,
-                        getCallingPackageOrSelf(), count);
+                        getCallingPackageOrSelf(), count, countPerMediaType);
             }
         }
 
         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
@@ -4478,6 +5453,65 @@
         });
     }
 
+    @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");
@@ -4532,6 +5566,7 @@
             }
             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 {
@@ -4544,7 +5579,13 @@
                         }
                         case MediaStore.SCAN_VOLUME_CALL: {
                             final String volumeName = arg;
-                            MediaService.onScanVolume(getContext(), volumeName, REASON_DEMAND);
+                            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);
+                            }
                             break;
                         }
                     }
@@ -4608,7 +5649,7 @@
 
                 try (ContentProviderClient client = getContext().getContentResolver()
                         .acquireUnstableContentProviderClient(
-                                MediaStore.EXTERNAL_STORAGE_PROVIDER_AUTHORITY)) {
+                                getExternalStorageProviderAuthority())) {
                     extras.putParcelable(MediaStore.EXTRA_URI, fileUri);
                     return client.call(method, null, extras);
                 } catch (RemoteException e) {
@@ -4620,24 +5661,61 @@
                 getContext().enforceCallingUriPermission(documentUri,
                         Intent.FLAG_GRANT_READ_URI_PERMISSION, TAG);
 
-                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);
+                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 LocalCallingIdentity token = clearLocalCallingIdentity();
-                try {
+                try (ContentProviderClient client = getContext().getContentResolver()
+                        .acquireUnstableContentProviderClient(authority)) {
+                    final Bundle clientRes = client.call(method, null, extras);
+                    final Uri fileUri = clientRes.getParcelable(MediaStore.EXTRA_URI);
                     final Bundle res = new Bundle();
-                    res.putParcelable(MediaStore.EXTRA_URI,
-                            queryForMediaUri(new File(fileUri.getPath()), null));
+                    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);
                     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);
                 }
@@ -4651,11 +5729,87 @@
                 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);
+                }
             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++) {
@@ -4665,6 +5819,26 @@
     }
 
     /**
+     * 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
      * before creating the privileged {@link PendingIntent}.
@@ -4679,9 +5853,17 @@
                 case IMAGES_MEDIA_ID:
                 case AUDIO_MEDIA_ID:
                 case VIDEO_MEDIA_ID:
+                case AUDIO_PLAYLISTS_ID:
                     // Caller is requesting a specific media item by its ID,
                     // which means it's valid for requests
                     break;
+                case FILES_ID:
+                    // Allow only subtitle files
+                    if (!isSubtitleFile(uri)) {
+                        throw new IllegalArgumentException(
+                                "All requested items must be Media items");
+                    }
+                    break;
                 default:
                     throw new IllegalArgumentException(
                             "All requested items must be referenced by specific ID");
@@ -4720,6 +5902,22 @@
     }
 
     /**
+     * @return true if the given Files uri has media_type=MEDIA_TYPE_SUBTITLE
+     */
+    private boolean isSubtitleFile(Uri uri) {
+        final LocalCallingIdentity tokenInner = clearLocalCallingIdentity();
+        try (Cursor cursor = queryForSingleItem(uri, new String[]{FileColumns.MEDIA_TYPE}, null,
+                null, null)) {
+            return cursor.getInt(0) == FileColumns.MEDIA_TYPE_SUBTITLE;
+        } catch (FileNotFoundException e) {
+            Log.e(TAG, "Couldn't find database row for requested uri " + uri, e);
+        } finally {
+            restoreLocalCallingIdentity(tokenInner);
+        }
+        return false;
+    }
+
+    /**
      * Ensure that all local databases have a custom collator registered for the
      * given {@link ULocale} locale.
      *
@@ -4764,12 +5962,12 @@
         final long[] knownIdsRaw = knownIds.toArray();
         Arrays.sort(knownIdsRaw);
 
-        for (String volumeName : getExternalVolumeNames()) {
+        for (MediaVolume volume : mVolumeCache.getExternalVolumes()) {
             final List<File> thumbDirs;
             try {
-                thumbDirs = getThumbnailDirectories(volumeName);
+                thumbDirs = getThumbnailDirectories(volume);
             } catch (FileNotFoundException e) {
-                Log.w(TAG, "Failed to resolve volume " + volumeName, e);
+                Log.w(TAG, "Failed to resolve volume " + volume.getName(), e);
                 continue;
             }
 
@@ -4911,12 +6109,13 @@
         }
     };
 
-    private List<File> getThumbnailDirectories(String volumeName) throws FileNotFoundException {
-        final File volumePath = getVolumePath(volumeName);
+    private List<File> getThumbnailDirectories(MediaVolume volume) throws FileNotFoundException {
+        final File volumePath = volume.getPath();
         return Arrays.asList(
-                FileUtils.buildPath(volumePath, DIRECTORY_MUSIC, DIRECTORY_THUMBNAILS),
-                FileUtils.buildPath(volumePath, DIRECTORY_MOVIES, DIRECTORY_THUMBNAILS),
-                FileUtils.buildPath(volumePath, DIRECTORY_PICTURES, DIRECTORY_THUMBNAILS));
+                FileUtils.buildPath(volumePath, Environment.DIRECTORY_MUSIC, DIRECTORY_THUMBNAILS),
+                FileUtils.buildPath(volumePath, Environment.DIRECTORY_MOVIES, DIRECTORY_THUMBNAILS),
+                FileUtils.buildPath(volumePath, Environment.DIRECTORY_PICTURES,
+                        DIRECTORY_THUMBNAILS));
     }
 
     private void invalidateThumbnails(Uri uri) {
@@ -4988,7 +6187,17 @@
 
     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
@@ -5016,10 +6225,10 @@
 
         int count;
 
-        final String volumeName = getVolumeName(uri);
         final int targetSdkVersion = getCallingPackageTargetSdkVersion();
         final boolean allowHidden = isCallingPackageAllowedHidden();
         final int match = matchUri(uri, allowHidden);
+        final DatabaseHelper helper = getDatabaseForUri(uri);
 
         switch (match) {
             case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
@@ -5030,7 +6239,6 @@
                 final long playlistId = Long.parseLong(uri.getPathSegments().get(3));
                 final Uri playlistUri = ContentUris.withAppendedId(
                         MediaStore.Audio.Playlists.getContentUri(volumeName), playlistId);
-
                 if (uri.getBooleanQueryParameter("move", false)) {
                     // Convert explicit request into query; sigh, moveItem()
                     // uses zero-based indexing instead of one-based indexing
@@ -5062,11 +6270,13 @@
                     values.put(Playlists.Members.PLAY_ORDER, (index + 1));
                     addPlaylistMembers(playlistUri, values);
                 }
+
+                acceptWithExpansion(helper::notifyUpdate, volumeName, playlistId,
+                        FileColumns.MEDIA_TYPE_PLAYLIST, false);
                 return 1;
             }
         }
 
-        final DatabaseHelper helper = getDatabaseForUri(uri);
         final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_UPDATE, match, uri, extras, null);
 
         // Give callers interacting with a specific media item a chance to
@@ -5080,6 +6290,7 @@
 
         boolean triggerInvalidate = false;
         boolean triggerScan = false;
+        boolean isUriPublished = false;
         if (initialValues != null) {
             // IDs are forever; nobody should be editing them
             initialValues.remove(MediaColumns._ID);
@@ -5166,7 +6377,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);
@@ -5301,7 +6512,22 @@
                 initialValues.remove(MediaColumns.DATA);
                 ensureUniqueFileColumns(match, uri, extras, initialValues, beforePath);
 
-                final String afterPath = initialValues.getAsString(MediaColumns.DATA);
+                String afterPath = initialValues.getAsString(MediaColumns.DATA);
+
+                if (isCrossUserEnabled()) {
+                    String afterVolume = extractVolumeName(afterPath);
+                    String afterVolumePath =  extractVolumePath(afterPath);
+                    String beforeVolumePath = extractVolumePath(beforePath);
+
+                    if (MediaStore.VOLUME_EXTERNAL_PRIMARY.equals(beforeVolume)
+                            && beforeVolume.equals(afterVolume)
+                            && !beforeVolumePath.equals(afterVolumePath)) {
+                        // On cross-user enabled devices, it can happen that a rename intended as
+                        // /storage/emulated/999/foo -> /storage/emulated/999/foo can end up as
+                        // /storage/emulated/999/foo -> /storage/emulated/0/foo. We now fix-up
+                        afterPath = afterPath.replaceFirst(afterVolumePath, beforeVolumePath);
+                    }
+                }
 
                 Log.d(TAG, "Moving " + beforePath + " to " + afterPath);
                 try {
@@ -5325,6 +6551,8 @@
             Trace.endSection();
         }
 
+        assertPrivatePathNotInValues(initialValues);
+
         // Make sure any updated paths look consistent
         assertFileColumnsConsistent(match, uri, initialValues);
 
@@ -5387,6 +6615,31 @@
             }
         }
 
+        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
@@ -5406,14 +6659,22 @@
                         try (Cursor c = queryForSingleItem(updatedUri,
                                 new String[] { FileColumns.DATA }, null, null, null)) {
                             final File file = new File(c.getString(0));
-                            helper.postBlocking(() -> {
-                                final LocalCallingIdentity tokenInner = clearLocalCallingIdentity();
-                                try {
-                                    mMediaScanner.scanFile(file, REASON_DEMAND);
-                                } finally {
-                                    restoreLocalCallingIdentity(tokenInner);
-                                }
-                            });
+                            final boolean notifyTranscodeHelper = isUriPublished;
+                            if (deferScan) {
+                                helper.postBackground(() -> {
+                                    scanFileAsMediaProvider(file, REASON_DEMAND);
+                                    if (notifyTranscodeHelper) {
+                                        notifyTranscodeHelperOnUriPublished(updatedUri);
+                                    }
+                                });
+                            } else {
+                                helper.postBlocking(() -> {
+                                    scanFileAsMediaProvider(file, REASON_DEMAND);
+                                    if (notifyTranscodeHelper) {
+                                        notifyTranscodeHelperOnUriPublished(updatedUri);
+                                    }
+                                });
+                            }
                         } catch (Exception e) {
                             Log.w(TAG, "Failed to update metadata for " + updatedUri, e);
                         }
@@ -5428,6 +6689,29 @@
         return count;
     }
 
+    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.
@@ -5512,8 +6796,8 @@
             @NonNull SQLiteDatabase db) {
         try {
             // Refresh playlist members based on what we parse from disk
-            final String volumeName = getVolumeName(playlistUri);
             final long playlistId = ContentUris.parseId(playlistUri);
+            final Map<String, Long> membersMap = getAllPlaylistMembers(playlistId);
             db.delete("audio_playlists_map", "playlist_id=" + playlistId, null);
 
             final Path playlistPath = queryForDataFile(playlistUri, null).toPath();
@@ -5524,7 +6808,7 @@
             for (int i = 0; i < members.size(); i++) {
                 try {
                     final Path audioPath = playlistPath.getParent().resolve(members.get(i));
-                    final long audioId = queryForPlaylistMember(volumeName, audioPath);
+                    final long audioId = queryForPlaylistMember(audioPath, membersMap);
 
                     final ContentValues values = new ContentValues();
                     values.put(Playlists.Members.PLAY_ORDER, i + 1);
@@ -5540,18 +6824,42 @@
         }
     }
 
+    private Map<String, Long> getAllPlaylistMembers(long playlistId) {
+        final Map<String, Long> membersMap = new ArrayMap<>();
+
+        final Uri uri = Playlists.Members.getContentUri(MediaStore.VOLUME_EXTERNAL, playlistId);
+        final String[] projection = new String[] {
+                Playlists.Members.DATA,
+                Playlists.Members.AUDIO_ID
+        };
+        try (Cursor c = query(uri, projection, null, null)) {
+            if (c == null) {
+                Log.e(TAG, "Cursor is null, failed to create cached playlist member info.");
+                return membersMap;
+            }
+            while (c.moveToNext()) {
+                membersMap.put(c.getString(0), c.getLong(1));
+            }
+        }
+        return membersMap;
+    }
+
     /**
      * Make two attempts to query this playlist member: first based on the exact
      * path, and if that fails, fall back to picking a single item matching the
      * display name. When there are multiple items with the same display name,
      * we can't resolve between them, and leave this member unresolved.
      */
-    private long queryForPlaylistMember(@NonNull String volumeName, @NonNull Path path)
+    private long queryForPlaylistMember(@NonNull Path path, @NonNull Map<String, Long> membersMap)
             throws IOException {
-        final Uri audioUri = Audio.Media.getContentUri(volumeName);
+        final String data = path.toFile().getCanonicalPath();
+        if (membersMap.containsKey(data)) {
+            return membersMap.get(data);
+        }
+        final Uri audioUri = Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
         try (Cursor c = queryForSingleItem(audioUri,
                 new String[] { BaseColumns._ID }, MediaColumns.DATA + "=?",
-                new String[] { path.toFile().getCanonicalPath() }, null)) {
+                new String[] { data }, null)) {
             return c.getLong(0);
         } catch (FileNotFoundException ignored) {
         }
@@ -5572,7 +6880,9 @@
     private long addPlaylistMembers(@NonNull Uri playlistUri, @NonNull ContentValues values)
             throws FallbackException {
         final long audioId = values.getAsLong(Audio.Playlists.Members.AUDIO_ID);
-        final Uri audioUri = Audio.Media.getContentUri(getVolumeName(playlistUri), audioId);
+        final String volumeName = MediaStore.VOLUME_INTERNAL.equals(getVolumeName(playlistUri))
+                ? MediaStore.VOLUME_INTERNAL : MediaStore.VOLUME_EXTERNAL;
+        final Uri audioUri = Audio.Media.getContentUri(volumeName, audioId);
 
         Integer playOrder = values.getAsInteger(Playlists.Members.PLAY_ORDER);
         playOrder = (playOrder != null) ? (playOrder - 1) : Integer.MAX_VALUE;
@@ -5586,12 +6896,13 @@
             playOrder = playlist.add(playOrder,
                     playlistFile.toPath().getParent().relativize(audioFile.toPath()));
             playlist.write(playlistFile);
+            invalidateFuseDentry(playlistFile);
 
             resolvePlaylistMembers(playlistUri);
 
             // Callers are interested in the actual ID we generated
-            final Uri membersUri = Playlists.Members.getContentUri(
-                    getVolumeName(playlistUri), ContentUris.parseId(playlistUri));
+            final Uri membersUri = Playlists.Members.getContentUri(volumeName,
+                    ContentUris.parseId(playlistUri));
             try (Cursor c = query(membersUri, new String[] { BaseColumns._ID },
                     Playlists.Members.PLAY_ORDER + "=" + (playOrder + 1), null, null)) {
                 c.moveToFirst();
@@ -5603,6 +6914,39 @@
         }
     }
 
+    private int addPlaylistMembers(@NonNull Uri playlistUri, @NonNull ContentValues[] initialValues)
+            throws FallbackException {
+        final String volumeName = getVolumeName(playlistUri);
+        final String audioVolumeName =
+                MediaStore.VOLUME_INTERNAL.equals(volumeName)
+                        ? MediaStore.VOLUME_INTERNAL : MediaStore.VOLUME_EXTERNAL;
+
+        try {
+            final File playlistFile = queryForDataFile(playlistUri, null);
+            final Playlist playlist = new Playlist();
+            playlist.read(playlistFile);
+
+            for (ContentValues values : initialValues) {
+                final long audioId = values.getAsLong(Audio.Playlists.Members.AUDIO_ID);
+                final Uri audioUri = Audio.Media.getContentUri(audioVolumeName, audioId);
+                final File audioFile = queryForDataFile(audioUri, null);
+
+                Integer playOrder = values.getAsInteger(Playlists.Members.PLAY_ORDER);
+                playOrder = (playOrder != null) ? (playOrder - 1) : Integer.MAX_VALUE;
+                playlist.add(playOrder,
+                        playlistFile.toPath().getParent().relativize(audioFile.toPath()));
+            }
+            playlist.write(playlistFile);
+
+            resolvePlaylistMembers(playlistUri);
+        } catch (IOException e) {
+            throw new FallbackException("Failed to update playlist", e,
+                    android.os.Build.VERSION_CODES.R);
+        }
+
+        return initialValues.length;
+    }
+
     /**
      * Move an audio item within the given playlist.
      */
@@ -5621,6 +6965,7 @@
             playlist.read(playlistFile);
             final int finalIndex = playlist.move(fromIndex, toIndex);
             playlist.write(playlistFile);
+            invalidateFuseDentry(playlistFile);
 
             resolvePlaylistMembers(playlistUri);
             return finalIndex;
@@ -5631,25 +6976,26 @@
     }
 
     /**
-     * Remove an audio item from the given playlist.
+     * Removes an audio item or multiple audio items(if targetSDK<R) from the given playlist.
      */
     private int removePlaylistMembers(@NonNull Uri playlistUri, @NonNull Bundle queryArgs)
             throws FallbackException {
-        final int index = resolvePlaylistIndex(playlistUri, queryArgs);
+        final int[] indexes = resolvePlaylistIndexes(playlistUri, queryArgs);
         try {
             final File playlistFile = queryForDataFile(playlistUri, null);
 
             final Playlist playlist = new Playlist();
             playlist.read(playlistFile);
             final int count;
-            if (index == -1) {
-                count = playlist.asList().size();
-                playlist.clear();
+            if (indexes.length == 0) {
+                // This means either no playlist members match the query or VolumeNotFoundException
+                // was thrown. So we don't have anything to delete.
+                count = 0;
             } else {
-                count = 1;
-                playlist.remove(index);
+                count = playlist.removeMultiple(indexes);
             }
             playlist.write(playlistFile);
+            invalidateFuseDentry(playlistFile);
 
             resolvePlaylistMembers(playlistUri);
             return count;
@@ -5660,10 +7006,37 @@
     }
 
     /**
-     * Resolve query arguments that are designed to select a specific playlist
-     * item using its {@link Playlists.Members#PLAY_ORDER}.
+     * Remove an audio item from the given playlist since the playlist file or the audio file is
+     * already removed.
      */
-    private int resolvePlaylistIndex(@NonNull Uri playlistUri, @NonNull Bundle queryArgs) {
+    private void removePlaylistMembers(int mediaType, long id) {
+        final DatabaseHelper helper;
+        try {
+            helper = getDatabaseForUri(Audio.Media.EXTERNAL_CONTENT_URI);
+        } catch (VolumeNotFoundException e) {
+            Log.w(TAG, e);
+            return;
+        }
+
+        helper.runWithTransaction((db) -> {
+            final String where;
+            if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) {
+                where = "playlist_id=?";
+            } else {
+                where = "audio_id=?";
+            }
+            db.delete("audio_playlists_map", where, new String[] { "" + id });
+            return null;
+        });
+    }
+
+    /**
+     * Resolve query arguments that are designed to select specific playlist
+     * items using the playlist's {@link Playlists.Members#PLAY_ORDER}.
+     *
+     * @return an array of the indexes that match the query.
+     */
+    private int[] resolvePlaylistIndexes(@NonNull Uri playlistUri, @NonNull Bundle queryArgs) {
         final Uri membersUri = Playlists.Members.getContentUri(
                 getVolumeName(playlistUri), ContentUris.parseId(playlistUri));
 
@@ -5674,32 +7047,62 @@
             qb = getQueryBuilder(TYPE_DELETE, AUDIO_PLAYLISTS_ID_MEMBERS,
                     membersUri, queryArgs, null);
         } catch (VolumeNotFoundException ignored) {
-            return -1;
+            return new int[0];
         }
 
         try (Cursor c = qb.query(helper,
                 new String[] { Playlists.Members.PLAY_ORDER }, queryArgs, null)) {
-            if ((c.getCount() == 1) && c.moveToFirst()) {
-                return c.getInt(0) - 1;
+            if ((c.getCount() >= 1) && c.moveToFirst()) {
+                int size = c.getCount();
+                int[] res = new int[size];
+                for (int i = 0; i < size; ++i) {
+                    res[i] = c.getInt(0) - 1;
+                    c.moveToNext();
+                }
+                return res;
             } else {
-                return -1;
+                // Cursor size is 0
+                return new int[0];
             }
         }
     }
 
+    /**
+     * Resolve query arguments that are designed to select a specific playlist
+     * item using its {@link Playlists.Members#PLAY_ORDER}.
+     *
+     * @return if there's only 1 item that matches the query, returns its index. Returns -1
+     * otherwise.
+     */
+    private int resolvePlaylistIndex(@NonNull Uri playlistUri, @NonNull Bundle queryArgs) {
+        int[] indexes = resolvePlaylistIndexes(playlistUri, queryArgs);
+        if (indexes.length == 1) {
+            return indexes[0];
+        }
+        return -1;
+    }
+
     @Override
     public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
-        return openFileCommon(uri, mode, null);
+        return openFileCommon(uri, mode, /*signal*/ null, /*opts*/ null);
     }
 
     @Override
     public ParcelFileDescriptor openFile(Uri uri, String mode, CancellationSignal signal)
             throws FileNotFoundException {
-        return openFileCommon(uri, mode, signal);
+        return openFileCommon(uri, mode, signal, /*opts*/ null);
     }
 
-    private ParcelFileDescriptor openFileCommon(Uri uri, String mode, CancellationSignal signal)
+    private ParcelFileDescriptor openFileCommon(Uri uri, String mode, CancellationSignal signal,
+            @Nullable Bundle opts)
             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);
 
         final boolean allowHidden = isCallingPackageAllowedHidden();
@@ -5707,34 +7110,40 @@
         final String volumeName = getVolumeName(uri);
 
         // Handle some legacy cases where we need to redirect thumbnails
-        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);
+        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);
+                }
             }
-            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);
-            }
+        } finally {
+            // We have to log separately here because openFileAndEnforcePathPermissionsHelper calls
+            // a public MediaProvider API and so logs the access there.
+            PulledMetrics.logVolumeAccessViaMediaProvider(getCallingUidOrSelf(), volumeName);
         }
 
-        return openFileAndEnforcePathPermissionsHelper(uri, match, mode, signal);
+        return openFileAndEnforcePathPermissionsHelper(uri, match, mode, signal, opts);
     }
 
     @Override
@@ -5753,6 +7162,21 @@
             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);
+        }
+
         // TODO: enforce that caller has access to this uri
 
         // Offer thumbnail of media, when requested
@@ -5764,7 +7188,7 @@
         }
 
         // Worst case, return the underlying file
-        return new AssetFileDescriptor(openFileCommon(uri, "r", signal), 0,
+        return new AssetFileDescriptor(openFileCommon(uri, "r", signal, opts), 0,
                 AssetFileDescriptor.UNKNOWN_LENGTH);
     }
 
@@ -5921,6 +7345,23 @@
     }
 
     /**
+     * Query the given {@link Uri} as MediaProvider, expecting only a single item to be found.
+     *
+     * @throws FileNotFoundException if no items were found, or multiple items
+     *             were found, or there was trouble reading the data.
+     */
+    Cursor queryForSingleItemAsMediaProvider(Uri uri, String[] projection, String selection,
+            String[] selectionArgs, CancellationSignal signal)
+            throws FileNotFoundException {
+        final LocalCallingIdentity tokenInner = clearLocalCallingIdentity();
+        try {
+            return queryForSingleItem(uri, projection, selection, selectionArgs, signal);
+        } finally {
+            restoreLocalCallingIdentity(tokenInner);
+        }
+    }
+
+    /**
      * Query the given {@link Uri}, expecting only a single item to be found.
      *
      * @throws FileNotFoundException if no items were found, or multiple items
@@ -5929,7 +7370,7 @@
     Cursor queryForSingleItem(Uri uri, String[] projection, String selection,
             String[] selectionArgs, CancellationSignal signal) throws FileNotFoundException {
         final Cursor c = query(uri, projection,
-                DatabaseUtils.createSqlQueryBundle(selection, selectionArgs, null), signal);
+                DatabaseUtils.createSqlQueryBundle(selection, selectionArgs, null), signal, true);
         if (c == null) {
             throw new FileNotFoundException("Missing cursor for " + uri);
         } else if (c.getCount() < 1) {
@@ -5962,12 +7403,36 @@
         }
     }
 
-    private File getFuseFile(File file) {
+    public File getFuseFile(File file) {
         String filePath = file.getPath().replaceFirst(
                 "/storage/", "/mnt/user/" + UserHandle.myUserId() + "/");
         return new File(filePath);
     }
 
+    private ParcelFileDescriptor openWithFuse(String filePath, int uid, int mediaCapabilitiesUid,
+            int modeBits, boolean shouldRedact, boolean shouldTranscode, int transcodeReason)
+            throws FileNotFoundException {
+        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));
@@ -6006,10 +7471,16 @@
      * a "/mnt/user" path.
      */
     private ParcelFileDescriptor openFileAndEnforcePathPermissionsHelper(Uri uri, int match,
-            String mode, CancellationSignal signal) throws FileNotFoundException {
+            String mode, CancellationSignal signal, @NonNull Bundle opts)
+            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;
@@ -6041,7 +7512,11 @@
             restoreLocalCallingIdentity(token);
         }
 
-        checkAccess(uri, Bundle.EMPTY, file, forWrite);
+        if (redactedUri == null) {
+            checkAccess(uri, Bundle.EMPTY, file, forWrite);
+        } else {
+            checkAccess(redactedUri, Bundle.EMPTY, file, false);
+        }
 
         // We don't check ownership for files with IS_PENDING set by FUSE
         if (isPending && !isPendingFromFuse(file)) {
@@ -6050,12 +7525,13 @@
 
         final boolean callerIsOwner = Objects.equals(getCallingPackageOrSelf(), ownerPackageName);
         // Figure out if we need to redact contents
-        final boolean redactionNeeded = callerIsOwner ? false : isRedactionNeeded(uri);
+        final boolean redactionNeeded =
+                (redactedUri != null) || (!callerIsOwner && 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);
         }
 
@@ -6083,7 +7559,7 @@
                         update(uri, values, null, null);
                         break;
                     default:
-                        mMediaScanner.scanFile(file, REASON_DEMAND);
+                        scanFileAsMediaProvider(file, REASON_DEMAND);
                         break;
                 }
             } catch (Exception e2) {
@@ -6095,33 +7571,23 @@
             // 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 (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);
-                }
+                // 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);
             } else {
                 FuseDaemon daemon = null;
                 try {
@@ -6132,22 +7598,23 @@
                 // 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 (SystemProperties.getBoolean(PROP_FUSE, false) && shouldOpenWithFuse) {
+                if (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
-                    Log.w(TAG, "Using FUSE for " + filePath);
-                    pfd = FileUtils.openSafely(getFuseFile(file), modeBits);
+                    pfd = openWithFuse(filePath, uid, mediaCapabilitiesUid, modeBits,
+                            false /* shouldRedact */, shouldTranscode, transcodeReason);
                     try {
                         lowerFsFd.close();
                     } catch (IOException e) {
                         Log.w(TAG, "Failed to close lower filesystem fd " + file.getPath(), e);
                     }
                 } else {
-                    Log.i(TAG, "Using lower FS for " + filePath);
+                    Log.i(TAG, "Open with lower FS for " + filePath + ". Uid: " + uid);
                     if (forWrite) {
                         // When opening for write on the lower filesystem, invalidate the VFS dentry
                         // so subsequent open/getattr calls will return correctly.
@@ -6227,6 +7694,27 @@
         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);
@@ -6253,7 +7741,7 @@
      * <li>the calling identity is an app targeting Q or older versions AND is requesting legacy
      * storage
      * <li>the calling identity holds {@code MANAGE_EXTERNAL_STORAGE}
-     * <li>the calling identity owns filePath (eg /Android/data/com.foo)
+     * <li>the calling identity owns or has access to the filePath (eg /Android/data/com.foo)
      * <li>the calling identity has permission to write images and the given file is an image file
      * <li>the calling identity has permission to write video and the given file is an video file
      * </ul>
@@ -6269,9 +7757,8 @@
             return true;
         }
 
-        // Files under the apps own private directory
-        final String appSpecificDir = extractPathOwnerPackageName(filePath);
-        if (appSpecificDir != null && isCallingIdentitySharedPackageName(appSpecificDir)) {
+        // Check if the caller has access to private app directories.
+        if (isUidAllowedAccessToDataOrObbPathForFuse(mCallingIdentity.get().uid, filePath)) {
             return true;
         }
 
@@ -6286,9 +7773,10 @@
 
     /**
      * Returns true if the passed in path is an application-private data directory
-     * (such as Android/data/com.foo or Android/obb/com.foo) that does not belong to the caller.
+     * (such as Android/data/com.foo or Android/obb/com.foo) that does not belong to the caller and
+     * the caller does not have special access.
      */
-    private boolean isPrivatePackagePathNotOwnedByCaller(String path) {
+    private boolean isPrivatePackagePathNotAccessibleByCaller(String path) {
         // Files under the apps own private directory
         final String appSpecificDir = extractPathOwnerPackageName(path);
 
@@ -6296,33 +7784,33 @@
             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 (relativePath.startsWith("Android/media")) {
+        if (isExternalMediaDirectory(path)) {
             return false;
         }
-
-        // This is a private-package path; return true if not owned by the caller
-        return !isCallingIdentitySharedPackageName(appSpecificDir);
+        return !isUidAllowedAccessToDataOrObbPathForFuse(mCallingIdentity.get().uid, path);
     }
 
-    private boolean shouldBypassDatabaseForFuse(int uid) {
-        final LocalCallingIdentity token =
-                clearLocalCallingIdentity(getCachedCallingIdentityForFuse(uid));
-        try {
-            if (uid != android.os.Process.SHELL_UID && isCallingPackageManager()) {
-                return true;
+    private boolean shouldBypassDatabaseAndSetDirtyForFuse(int uid, String path) {
+        if (shouldBypassDatabase(uid)) {
+            synchronized (mNonHiddenPaths) {
+                File file = new File(path);
+                String key = file.getParent();
+                boolean maybeHidden = !mNonHiddenPaths.containsKey(key);
+
+                if (maybeHidden) {
+                    File topNoMediaDir = FileUtils.getTopLevelNoMedia(new File(path));
+                    if (topNoMediaDir == null) {
+                        mNonHiddenPaths.put(key, 0);
+                    } else {
+                        mMediaScanner.onDirectoryDirty(topNoMediaDir);
+                    }
+                }
             }
-            // We bypass db operations for legacy system galleries with W_E_S (see b/167307393).
-            // Tracking a longer term solution in b/168784136.
-            if (isCallingPackageLegacyWrite() && isCallingPackageSystemGallery()) {
-                return true;
-            }
-            return false;
-        } finally {
-            restoreLocalCallingIdentity(token);
+            return true;
         }
+        return false;
     }
 
     /**
@@ -6384,6 +7872,34 @@
         }
     }
 
+    private static class LRUCache<K, V> extends LinkedHashMap<K, V> {
+        private final int mMaxSize;
+
+        public LRUCache(int maxSize) {
+            this.mMaxSize = maxSize;
+        }
+
+        @Override
+        protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
+            return size() > mMaxSize;
+        }
+    }
+
+    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.
@@ -6394,44 +7910,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
-    public long[] getRedactionRangesForFuse(String path, int uid, int tid) throws IOException {
-        final File file = new File(path);
+    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);
 
-        // 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];
+        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];
+                }
             }
         }
 
         final LocalCallingIdentity token =
                 clearLocalCallingIdentity(getCachedCallingIdentityForFuse(uid));
-
-        long[] res = new long[0];
         try {
             if (!isRedactionNeeded()
                     || shouldBypassFuseRestrictions(/*forWrite*/ false, path)) {
-                return res;
+                return new long[0];
             }
 
             final Uri contentUri = FileUtils.getContentUriForPath(path);
             final String[] projection = new String[]{
                     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 Uri item;
             try (final Cursor c = queryForSingleItem(contentUri, projection, selection,
@@ -6450,17 +7973,22 @@
 
             final boolean callerIsOwner = Objects.equals(getCallingPackageOrSelf(),
                     ownerPackageName);
+
+            if (callerIsOwner) {
+                return new long[0];
+            }
+
             final boolean callerHasUriPermission = getContext().checkUriPermission(
                     item, mCallingIdentity.get().pid, mCallingIdentity.get().uid,
                     Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == PERMISSION_GRANTED;
-
-            if (!callerIsOwner && !callerHasUriPermission) {
-                res = getRedactionRanges(file).redactionRanges;
+            if (callerHasUriPermission) {
+                return new long[0];
             }
+
+            return getRedactionRanges(file).redactionRanges;
         } finally {
             restoreLocalCallingIdentity(token);
         }
-        return res;
     }
 
     /**
@@ -6534,73 +8062,200 @@
      * @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 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.
+     * @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.
      *
      * Called from JNI in jni/MediaProviderWrapper.cpp
      */
     @Keep
-    public int isOpenAllowedForFuse(String path, int uid, boolean forWrite) {
+    public FileOpenResult onFileOpenForFuse(String path, String ioPath, int uid, int tid,
+            int transformsReason, boolean forWrite, boolean redact, boolean logTransformsMetrics) {
         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 {
-            if (isPrivatePackagePathNotOwnedByCaller(path)) {
+            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 OsConstants.ENOENT;
+                return new FileOpenResult(OsConstants.ENOENT, originalUid, mediaCapabilitiesUid,
+                        new long[0]);
             }
 
             if (shouldBypassFuseRestrictions(forWrite, path)) {
-                return 0;
+                isSuccess = true;
+                return new FileOpenResult(0 /* status */, originalUid, mediaCapabilitiesUid,
+                        redact ? getRedactionRangesForFuse(path, ioPath, originalUid, uid, tid,
+                                forceRedaction) : new long[0]);
             }
             // Legacy apps that made is this far don't have the right storage permission and hence
             // are not allowed to access anything other than their external app directory
             if (isCallingPackageRequestingLegacy()) {
-                return OsConstants.EACCES;
+                return new FileOpenResult(OsConstants.EACCES /* status */, originalUid,
+                        mediaCapabilitiesUid, new long[0]);
             }
 
             final Uri contentUri = FileUtils.getContentUriForPath(path);
             final String[] projection = new String[]{
                     MediaColumns._ID,
                     MediaColumns.OWNER_PACKAGE_NAME,
-                    MediaColumns.IS_PENDING};
+                    MediaColumns.IS_PENDING,
+                    FileColumns.MEDIA_TYPE};
             final String selection = MediaColumns.DATA + "=?";
-            final String[] selectionArgs = new String[] { path };
-            final Uri fileUri;
+            final String[] selectionArgs = new String[]{path};
+            final long id;
+            final int mediaType;
             final boolean isPending;
             String ownerPackageName = null;
-            try (final Cursor c = queryForSingleItem(contentUri, projection, selection,
+            try (final Cursor c = queryForSingleItemAsMediaProvider(contentUri, projection,
+                    selection,
                     selectionArgs, null)) {
-                fileUri = ContentUris.withAppendedId(contentUri, c.getInt(0));
+                id = c.getLong(0);
                 ownerPackageName = c.getString(1);
                 isPending = c.getInt(2) != 0;
+                mediaType = c.getInt(3);
             }
-
             final File file = new File(path);
-            checkAccess(fileUri, Bundle.EMPTY, file, forWrite);
-
+            Uri fileUri = MediaStore.Files.getContentUri(extractVolumeName(path), id);
             // We don't check ownership for files with IS_PENDING set by FUSE
             if (isPending && !isPendingFromFuse(new File(path))) {
                 requireOwnershipForItem(ownerPackageName, fileUri);
             }
-            return 0;
-        } catch (FileNotFoundException e) {
+
+            // Check that path looks consistent before uri checks
+            if (!FileUtils.contains(Environment.getStorageDirectory(), file)) {
+                checkWorldReadAccess(file.getAbsolutePath());
+            }
+
+            try {
+                // checkAccess throws FileNotFoundException only from checkWorldReadAccess(),
+                // which we already check above. Hence, handling only SecurityException.
+                if (redactedUriId != null) {
+                    fileUri = ContentUris.removeId(fileUri).buildUpon().appendPath(
+                            redactedUriId).build();
+                }
+                checkAccess(fileUri, Bundle.EMPTY, file, forWrite);
+            } catch (SecurityException e) {
+                // Check for other Uri formats only when the single uri check flow fails.
+                // Throw the previous exception if the multi-uri checks failed.
+                final String uriId = redactedUriId == null ? Long.toString(id) : redactedUriId;
+                if (getOtherUriGrantsForPath(path, mediaType, uriId, forWrite) == null) {
+                    throw e;
+                }
+            }
+            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
-            // * App doesn't have read permission to the requested path, hence queryForSingleItem
-            //   couldn't return a valid db row, or,
             // * There is no db row corresponding to the requested path, which is more unlikely.
-            // In both of these cases, it means that app doesn't have access permission to the file.
-            Log.e(TAG, "Couldn't find file: " + path);
-            return OsConstants.EACCES;
+            // * 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]);
         } catch (IllegalStateException | SecurityException e) {
             Log.e(TAG, "Permission to access file: " + path + " is denied");
-            return OsConstants.EACCES;
+            return new FileOpenResult(OsConstants.EACCES /* status */, originalUid,
+                    mediaCapabilitiesUid, new long[0]);
         } 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>();
+        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);
+    }
+
+    @NonNull
+    private Uri getMediaUriForFuse(@NonNull String volumeName, int mediaType, String id) {
+        Uri uri = MediaStore.Files.getContentUri(volumeName);
+        switch (mediaType) {
+            case FileColumns.MEDIA_TYPE_IMAGE:
+                uri = MediaStore.Images.Media.getContentUri(volumeName);
+                break;
+            case FileColumns.MEDIA_TYPE_VIDEO:
+                uri = MediaStore.Video.Media.getContentUri(volumeName);
+                break;
+            case FileColumns.MEDIA_TYPE_AUDIO:
+                uri = MediaStore.Audio.Media.getContentUri(volumeName);
+                break;
+            case FileColumns.MEDIA_TYPE_PLAYLIST:
+                uri = MediaStore.Audio.Playlists.getContentUri(volumeName);
+                break;
+        }
+
+        return uri.buildUpon().appendPath(id).build();
+    }
+
     /**
      * Returns {@code true} if {@link #mCallingIdentity#getSharedPackages(String)} contains the
      * given package name, {@code false} otherwise.
@@ -6622,23 +8277,31 @@
      */
     @NonNull
     private Uri getContentUriForFile(@NonNull String filePath, @NonNull String mimeType) {
-        final String volName = FileUtils.getVolumeName(getContext(), new File(filePath));
+        final String volName;
+        try {
+            volName = FileUtils.getVolumeName(getContext(), new File(filePath));
+        } catch (FileNotFoundException e) {
+            throw new IllegalStateException("Couldn't get volume name for " + filePath);
+        }
         Uri uri = Files.getContentUri(volName);
-        final String topLevelDir = extractTopLevelDir(filePath);
+        String topLevelDir = extractTopLevelDir(filePath);
         if (topLevelDir == null) {
             // If the file path doesn't match the external storage directory, we use the files URI
             // as default and let #insert enforce the restrictions
             return uri;
         }
+        topLevelDir = topLevelDir.toLowerCase(Locale.ROOT);
+
         switch (topLevelDir) {
-            case DIRECTORY_PODCASTS:
-            case DIRECTORY_RINGTONES:
-            case DIRECTORY_ALARMS:
-            case DIRECTORY_NOTIFICATIONS:
-            case DIRECTORY_AUDIOBOOKS:
+            case DIRECTORY_PODCASTS_LOWER_CASE:
+            case DIRECTORY_RINGTONES_LOWER_CASE:
+            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:
+            case DIRECTORY_MUSIC_LOWER_CASE:
                 if (MimeUtils.isPlaylistMimeType(mimeType)) {
                     uri = Audio.Playlists.getContentUri(volName);
                 } else if (!MimeUtils.isSubtitleMimeType(mimeType)) {
@@ -6646,7 +8309,7 @@
                     uri = Audio.Media.getContentUri(volName);
                 }
                 break;
-            case DIRECTORY_MOVIES:
+            case DIRECTORY_MOVIES_LOWER_CASE:
                 if (MimeUtils.isPlaylistMimeType(mimeType)) {
                     uri = Audio.Playlists.getContentUri(volName);
                 } else if (!MimeUtils.isSubtitleMimeType(mimeType)) {
@@ -6654,16 +8317,16 @@
                     uri = Video.Media.getContentUri(volName);
                 }
                 break;
-            case DIRECTORY_DCIM:
-            case DIRECTORY_PICTURES:
+            case DIRECTORY_DCIM_LOWER_CASE:
+            case DIRECTORY_PICTURES_LOWER_CASE:
                 if (MimeUtils.isImageMimeType(mimeType)) {
                     uri = Images.Media.getContentUri(volName);
                 } else {
                     uri = Video.Media.getContentUri(volName);
                 }
                 break;
-            case DIRECTORY_DOWNLOADS:
-            case DIRECTORY_DOCUMENTS:
+            case DIRECTORY_DOWNLOADS_LOWER_CASE:
+            case DIRECTORY_DOCUMENTS_LOWER_CASE:
                 break;
             default:
                 Log.w(TAG, "Forgot to handle a top level directory in getContentUriForFile?");
@@ -6671,6 +8334,15 @@
         return uri;
     }
 
+    private boolean containsIgnoreCase(@Nullable List<String> stringsList, @Nullable String item) {
+        if (item == null || stringsList == null) return false;
+
+        for (String current : stringsList) {
+            if (item.equalsIgnoreCase(current)) return true;
+        }
+        return false;
+    }
+
     private boolean fileExists(@NonNull String absolutePath) {
         // We don't care about specific columns in the match,
         // we just want to check IF there's a match
@@ -6690,14 +8362,6 @@
         }
     }
 
-    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();
@@ -6717,7 +8381,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 is added to the shared storage, creates a
+     * app with the given {@code uid}. If the file is added to the shared storage, creates a
      * database entry for it.
      * <p> Does NOT create file.
      *
@@ -6741,9 +8405,10 @@
     public int insertFileIfNecessaryForFuse(@NonNull String path, int uid) {
         final LocalCallingIdentity token =
                 clearLocalCallingIdentity(getCachedCallingIdentityForFuse(uid));
+        PulledMetrics.logFileAccessViaFuse(getCallingUidOrSelf(), path);
 
         try {
-            if (isPrivatePackagePathNotOwnedByCaller(path)) {
+            if (isPrivatePackagePathNotAccessibleByCaller(path)) {
                 Log.e(TAG, "Can't create a file in another app's external directory");
                 return OsConstants.ENOENT;
             }
@@ -6753,7 +8418,14 @@
                 return OsConstants.EPERM;
             }
 
-            if (shouldBypassDatabaseForFuse(uid)) {
+            if (shouldBypassDatabaseAndSetDirtyForFuse(uid, path)) {
+                if (path.endsWith("/.nomedia")) {
+                    File parent = new File(path).getParentFile();
+                    synchronized (mNonHiddenPaths) {
+                        mNonHiddenPaths.keySet().removeIf(
+                                k -> FileUtils.contains(parent, new File(k)));
+                    }
+                }
                 return 0;
             }
 
@@ -6859,13 +8531,15 @@
     public int deleteFileForFuse(@NonNull String path, int uid) throws IOException {
         final LocalCallingIdentity token =
                 clearLocalCallingIdentity(getCachedCallingIdentityForFuse(uid));
+        PulledMetrics.logFileAccessViaFuse(getCallingUidOrSelf(), path);
+
         try {
-            if (isPrivatePackagePathNotOwnedByCaller(path)) {
+            if (isPrivatePackagePathNotAccessibleByCaller(path)) {
                 Log.e(TAG, "Can't delete a file in another app's external directory!");
                 return OsConstants.ENOENT;
             }
 
-            if (shouldBypassDatabaseForFuse(uid)) {
+            if (shouldBypassDatabaseAndSetDirtyForFuse(uid, path)) {
                 return deleteFileUnchecked(path);
             }
 
@@ -6921,10 +8595,11 @@
             @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.
-            if (isPrivatePackagePathNotOwnedByCaller(path)) {
+            if (isPrivatePackagePathNotAccessibleByCaller(path)) {
                 Log.e(TAG, "Can't modify another app's external directory!");
                 return OsConstants.EACCES;
             }
@@ -6974,25 +8649,28 @@
     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;
             }
-            if (isPrivatePackagePathNotOwnedByCaller(path)) {
+            if (isPrivatePackagePathNotAccessibleByCaller(path)) {
                 Log.e(TAG, "Can't access another app's external directory!");
                 return OsConstants.ENOENT;
             }
 
-            // Do not allow apps to open Android/data or Android/obb dirs. Installer and
-            // MOUNT_EXTERNAL_ANDROID_WRITABLE apps won't be blocked by this, as their OBB dirs
-            // are mounted to lowerfs directly.
-            if (isDataOrObbPath(path)) {
-                return OsConstants.EACCES;
-            }
-
             if (shouldBypassFuseRestrictions(forWrite, path)) {
                 return 0;
             }
+
+            // Do not allow apps to open Android/data or Android/obb dirs.
+            // On primary volumes, apps that get special access to these directories get it via
+            // mount views of lowerfs. On secondary volumes, such apps would return early from
+            // shouldBypassFuseRestrictions above.
+            if (isDataOrObbPath(path)) {
+                return OsConstants.EACCES;
+            }
+
             // 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()) {
@@ -7026,16 +8704,143 @@
     }
 
     @Keep
-    public boolean isUidForPackageForFuse(@NonNull String packageName, int uid) {
+    public boolean isUidAllowedAccessToDataOrObbPathForFuse(int uid, String path) {
         final LocalCallingIdentity token =
                 clearLocalCallingIdentity(getCachedCallingIdentityForFuse(uid));
         try {
-            return isCallingIdentitySharedPackageName(packageName);
+            // Files under the apps own private directory
+            final String appSpecificDir = extractPathOwnerPackageName(path);
+
+            if (appSpecificDir != null && isCallingIdentitySharedPackageName(appSpecificDir)) {
+                return true;
+            }
+            // This is a private-package path; return true if accessible by the caller
+            return isUidAllowedSpecialPrivatePathAccess(uid, path);
         } finally {
             restoreLocalCallingIdentity(token);
         }
     }
 
+    /**
+     * @return true iff the caller has installer privileges which gives write access to obb dirs.
+     * <p> Assumes that {@code mCallingIdentity} has been properly set to reflect the calling
+     * package.
+     */
+    private boolean isCallingIdentityAllowedInstallerAccess(int uid) {
+        final boolean hasWrite = mCallingIdentity.get().
+                hasPermission(PERMISSION_WRITE_EXTERNAL_STORAGE);
+
+        if (!hasWrite) {
+            return false;
+        }
+
+        // We're only willing to give out installer access if they also hold
+        // runtime permission; this is a firm CDD requirement
+        final boolean hasInstall = mCallingIdentity.get().
+                hasPermission(PERMISSION_INSTALL_PACKAGES);
+
+        if (hasInstall) {
+            return true;
+        }
+        // OPSTR_REQUEST_INSTALL_PACKAGES is granted/denied per package but vold can't
+        // update mountpoints of a specific package. So, check the appop for all packages
+        // sharing the uid and allow same level of storage access for all packages even if
+        // one of the packages has the appop granted.
+        // To maintain consistency of access in primary volume and secondary volumes use the same
+        // logic as we do for Zygote.MOUNT_EXTERNAL_INSTALLER view.
+        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;
+    }
+
+    private boolean isCallingIdentityExternalStorageProvider(int uid) {
+        return uid == mExternalStorageAuthorityAppId;
+    }
+
+    private boolean isCallingIdentityMtp(int uid) {
+        return mCallingIdentity.get().hasPermission(PERMISSION_ACCESS_MTP);
+    }
+
+    /**
+     * 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+).
+     *
+     * Installer apps can only access private-app directories on Android/obb.
+     *
+     * @param uid UID of the calling package
+     * @param path the path of the file to access
+     */
+    private boolean isUidAllowedSpecialPrivatePathAccess(int uid, String path) {
+        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));
+            }
+        } 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()) {
@@ -7063,14 +8868,49 @@
         }
 
         // Outstanding grant means they get access
-        if (getContext().checkUriPermission(uri, mCallingIdentity.get().pid,
-                mCallingIdentity.get().uid, forWrite
-                        ? Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-                        : Intent.FLAG_GRANT_READ_URI_PERMISSION) == PERMISSION_GRANTED) {
-            return true;
-        }
+        return isUriPermissionGranted(uri, forWrite);
+    }
 
-        return false;
+    /**
+     * Returns any uri that is granted from the set of Uris passed.
+     */
+    private @Nullable Uri getPermissionGrantedUri(@NonNull List<Uri> uris, boolean forWrite) {
+        if (SdkLevel.isAtLeastS()) {
+            int[] res = checkUriPermissions(uris, mCallingIdentity.get().pid,
+                    mCallingIdentity.get().uid, forWrite);
+            if (res.length != uris.size()) {
+                return null;
+            }
+            for (int i = 0; i < uris.size(); i++) {
+                if (res[i] == PERMISSION_GRANTED) {
+                    return uris.get(i);
+                }
+            }
+        } else {
+            for (Uri uri : uris) {
+                if (isUriPermissionGranted(uri, forWrite)) {
+                    return uri;
+                }
+            }
+        }
+        return null;
+    }
+
+    @RequiresApi(Build.VERSION_CODES.S)
+    private int[] checkUriPermissions(@NonNull List<Uri> uris, int pid, int uid, boolean forWrite) {
+        final int modeFlags = forWrite
+                ? Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+                : Intent.FLAG_GRANT_READ_URI_PERMISSION;
+        return getContext().checkUriPermissions(uris, pid, uid, modeFlags);
+    }
+
+    private boolean isUriPermissionGranted(Uri uri, boolean forWrite) {
+        final int modeFlags = forWrite
+                ? Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+                : Intent.FLAG_GRANT_READ_URI_PERMISSION;
+        int uriPermission = getContext().checkUriPermission(uri, mCallingIdentity.get().pid,
+                mCallingIdentity.get().uid, modeFlags);
+        return uriPermission == PERMISSION_GRANTED;
     }
 
     @VisibleForTesting
@@ -7078,6 +8918,72 @@
         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) {
@@ -7126,6 +9032,12 @@
         }
     }
 
+    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);
@@ -7138,6 +9050,16 @@
             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);
@@ -7151,7 +9073,8 @@
         // First, check to see if caller has direct write access
         if (forWrite) {
             final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_UPDATE, table, uri, extras, null);
-            try (Cursor c = qb.query(helper, new String[0],
+            qb.allowColumn(SQLiteQueryBuilder.ROWID_COLUMN);
+            try (Cursor c = qb.query(helper, new String[] { SQLiteQueryBuilder.ROWID_COLUMN },
                     null, null, null, null, null, null, null)) {
                 if (c.moveToFirst()) {
                     // Direct write access granted, yay!
@@ -7174,7 +9097,8 @@
 
         // Second, check to see if caller has direct read access
         final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_QUERY, table, uri, extras, null);
-        try (Cursor c = qb.query(helper, new String[0],
+        qb.allowColumn(SQLiteQueryBuilder.ROWID_COLUMN);
+        try (Cursor c = qb.query(helper, new String[] { SQLiteQueryBuilder.ROWID_COLUMN },
                 null, null, null, null, null, null, null)) {
             if (c.moveToFirst()) {
                 if (!forWrite) {
@@ -7201,6 +9125,7 @@
             }
         }
 
+        if (redactedUri != null) uri = redactedUri;
         throw new SecurityException(getCallingPackageOrSelf() + " has no access to " + uri);
     }
 
@@ -7327,6 +9252,15 @@
             }
         }
 
+        public int translateForBulkInsert(int targetSdkVersion) {
+            if (targetSdkVersion >= mThrowSdkVersion) {
+                throw new IllegalArgumentException(getMessage());
+            } else {
+                Log.w(TAG, getMessage());
+                return 0;
+            }
+        }
+
         public int translateForUpdateDelete(int targetSdkVersion) {
             if (targetSdkVersion >= mThrowSdkVersion) {
                 throw new IllegalArgumentException(getMessage());
@@ -7352,10 +9286,30 @@
         }
     }
 
+    /**
+     * 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 (mAttachedVolumeNames) {
-            if (!mAttachedVolumeNames.contains(volumeName)) {
+        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);
                 throw new VolumeNotFoundException(volumeName);
             }
         }
@@ -7390,42 +9344,43 @@
         return MediaStore.AUTHORITY_URI.buildUpon().appendPath(volumeName).build();
     }
 
-    public Uri attachVolume(String volume, boolean validate) {
+    public Uri attachVolume(MediaVolume 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 check for shady volume names
-        MediaStore.checkArgumentVolumeName(volume);
+        MediaStore.checkArgumentVolumeName(volumeName);
 
         // Quick check that volume actually exists
-        if (!MediaStore.VOLUME_INTERNAL.equals(volume) && validate) {
+        if (!MediaStore.VOLUME_INTERNAL.equals(volumeName) && validate) {
             try {
-                getVolumePath(volume);
+                getVolumePath(volumeName);
             } catch (IOException e) {
                 throw new IllegalArgumentException(
                         "Volume " + volume + " currently unavailable", e);
             }
         }
 
-        synchronized (mAttachedVolumeNames) {
-            mAttachedVolumeNames.add(volume);
+        synchronized (mAttachedVolumes) {
+            mAttachedVolumes.add(volume);
         }
 
         final ContentResolver resolver = getContext().getContentResolver();
-        final Uri uri = getBaseContentUri(volume);
-        resolver.notifyChange(getBaseContentUri(volume), null);
+        final Uri uri = getBaseContentUri(volumeName);
+        // TODO(b/182396009) we probably also want to notify clone profile (and vice versa)
+        resolver.notifyChange(getBaseContentUri(volumeName), null);
 
         if (LOGV) Log.v(TAG, "Attached volume: " + volume);
-        if (!MediaStore.VOLUME_INTERNAL.equals(volume)) {
+        if (!MediaStore.VOLUME_INTERNAL.equals(volumeName)) {
             // Also notify on synthetic view of all devices
             resolver.notifyChange(getBaseContentUri(MediaStore.VOLUME_EXTERNAL), null);
 
             ForegroundThread.getExecutor().execute(() -> {
-                final DatabaseHelper helper = MediaStore.VOLUME_INTERNAL.equals(volume)
-                        ? mInternalDatabase : mExternalDatabase;
-                helper.runWithTransaction((db) -> {
+                mExternalDatabase.runWithTransaction((db) -> {
                     ensureDefaultFolders(volume, db);
                     ensureThumbnailsValid(volume, db);
                     return null;
@@ -7434,26 +9389,39 @@
                 // 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(), volume);
+                MediaDocumentsProvider.onMediaStoreReady(getContext(), volumeName);
             });
         }
         return uri;
     }
 
     private void detachVolume(Uri uri) {
-        detachVolume(MediaStore.getVolumeName(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) ;
+        }
     }
 
-    public void detachVolume(String volume) {
+    public boolean isVolumeAttached(MediaVolume volume) {
+        synchronized (mAttachedVolumes) {
+            return mAttachedVolumes.contains(volume);
+        }
+    }
+
+    public void detachVolume(MediaVolume volume) {
         if (mCallingIdentity.get().pid != android.os.Process.myPid()) {
             throw new SecurityException(
                     "Opening and closing databases not allowed.");
         }
 
-        // Quick check for shady volume names
-        MediaStore.checkArgumentVolumeName(volume);
+        final String volumeName = volume.getName();
 
-        if (MediaStore.VOLUME_INTERNAL.equals(volume)) {
+        // Quick check for shady volume names
+        MediaStore.checkArgumentVolumeName(volumeName);
+
+        if (MediaStore.VOLUME_INTERNAL.equals(volumeName)) {
             throw new UnsupportedOperationException(
                     "Deleting the internal volume is not allowed");
         }
@@ -7461,24 +9429,24 @@
         // Signal any scanning to shut down
         mMediaScanner.onDetachVolume(volume);
 
-        synchronized (mAttachedVolumeNames) {
-            mAttachedVolumeNames.remove(volume);
+        synchronized (mAttachedVolumes) {
+            mAttachedVolumes.remove(volume);
         }
 
         final ContentResolver resolver = getContext().getContentResolver();
-        final Uri uri = getBaseContentUri(volume);
-        resolver.notifyChange(getBaseContentUri(volume), null);
+        final Uri uri = getBaseContentUri(volumeName);
+        resolver.notifyChange(getBaseContentUri(volumeName), null);
 
-        if (!MediaStore.VOLUME_INTERNAL.equals(volume)) {
+        if (!MediaStore.VOLUME_INTERNAL.equals(volumeName)) {
             // Also notify on synthetic view of all devices
             resolver.notifyChange(getBaseContentUri(MediaStore.VOLUME_EXTERNAL), null);
         }
 
-        if (LOGV) Log.v(TAG, "Detached volume: " + volume);
+        if (LOGV) Log.v(TAG, "Detached volume: " + volumeName);
     }
 
-    @GuardedBy("mAttachedVolumeNames")
-    private final ArraySet<String> mAttachedVolumeNames = new ArraySet<>();
+    @GuardedBy("mAttachedVolumes")
+    private final ArraySet<MediaVolume> mAttachedVolumes = new ArraySet<>();
     @GuardedBy("mCustomCollators")
     private final ArraySet<String> mCustomCollators = new ArraySet<>();
 
@@ -7486,6 +9454,7 @@
 
     private DatabaseHelper mInternalDatabase;
     private DatabaseHelper mExternalDatabase;
+    private TranscodeHelper mTranscodeHelper;
 
     // name of the volume currently being scanned by the media scanner (or null)
     private String mMediaScannerVolume;
@@ -7543,6 +9512,9 @@
     static final int DOWNLOADS = 800;
     static final int DOWNLOADS_ID = 801;
 
+    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[] {
@@ -7642,7 +9614,7 @@
      */
     private static final ArraySet<String> sMutableColumns = new ArraySet<>();
 
-    {
+    static {
         sMutableColumns.add(MediaStore.MediaColumns.DATA);
         sMutableColumns.add(MediaStore.MediaColumns.RELATIVE_PATH);
         sMutableColumns.add(MediaStore.MediaColumns.DISPLAY_NAME);
@@ -7673,7 +9645,7 @@
      */
     private static final ArraySet<String> sPlacementColumns = new ArraySet<>();
 
-    {
+    static {
         sPlacementColumns.add(MediaStore.MediaColumns.DATA);
         sPlacementColumns.add(MediaStore.MediaColumns.RELATIVE_PATH);
         sPlacementColumns.add(MediaStore.MediaColumns.DISPLAY_NAME);
@@ -7755,6 +9727,10 @@
         return mCallingIdentity.get().hasPermission(PERMISSION_IS_SYSTEM_GALLERY);
     }
 
+    private int getCallingUidOrSelf() {
+        return mCallingIdentity.get().uid;
+    }
+
     @Deprecated
     private String getCallingPackageOrSelf() {
         return mCallingIdentity.get().getPackageName();
@@ -7804,11 +9780,20 @@
     @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         writer.println("mThumbSize=" + mThumbSize);
-        synchronized (mAttachedVolumeNames) {
-            writer.println("mAttachedVolumeNames=" + mAttachedVolumeNames);
+        synchronized (mAttachedVolumes) {
+            writer.println("mAttachedVolumes=" + mAttachedVolumes);
         }
         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 d7c6bab..4178aa4 100644
--- a/src/com/android/providers/media/MediaService.java
+++ b/src/com/android/providers/media/MediaService.java
@@ -27,7 +27,10 @@
 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;
 
@@ -41,6 +44,21 @@
 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);
     }
@@ -68,7 +86,13 @@
                     break;
                 }
                 case Intent.ACTION_MEDIA_MOUNTED: {
-                    onScanVolume(this, intent.getData(), REASON_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);
                     break;
                 }
                 default: {
@@ -100,21 +124,42 @@
         }
     }
 
-    private static void onScanVolume(Context context, Uri uri, int reason)
+    private static void onMediaMountedBroadcast(Context context, Intent intent)
             throws IOException {
-        final File file = new File(uri.getPath()).getCanonicalFile();
-        final String volumeName = FileUtils.getVolumeName(context, file);
-
-        onScanVolume(context, volumeName, reason);
+        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");
+        }
     }
 
-    public static void onScanVolume(Context context, String volumeName, int reason)
+    public static void onScanVolume(Context context, MediaVolume volume, 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, MediaStore.VOLUME_INTERNAL, reason);
+            onScanVolume(context, MediaVolume.fromInternal(), reason);
             RingtoneManager.ensureDefaultRingtones(context);
         }
 
@@ -123,7 +168,7 @@
         // in the situation where a volume is ejected mid-scan
         final Uri broadcastUri;
         if (!MediaStore.VOLUME_INTERNAL.equals(volumeName)) {
-            broadcastUri = Uri.fromFile(FileUtils.getVolumePath(context, volumeName));
+            broadcastUri = Uri.fromFile(volume.getPath());
         } else {
             broadcastUri = null;
         }
@@ -131,7 +176,7 @@
         try (ContentProviderClient cpc = context.getContentResolver()
                 .acquireContentProviderClient(MediaStore.AUTHORITY)) {
             final MediaProvider provider = ((MediaProvider) cpc.getLocalContentProvider());
-            provider.attachVolume(volumeName, /* validate */ true);
+            provider.attachVolume(volume, /* validate */ true);
 
             final ContentResolver resolver = ContentResolver.wrap(cpc.getLocalContentProvider());
 
@@ -140,20 +185,24 @@
             Uri scanUri = resolver.insert(MediaStore.getMediaScannerUri(), values);
 
             if (broadcastUri != null) {
-                context.sendBroadcast(
-                        new Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, broadcastUri));
+                context.sendBroadcastAsUser(
+                        new Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, broadcastUri), owner);
             }
 
-            for (File dir : FileUtils.getVolumeScanPaths(context, volumeName)) {
-                provider.scanDirectory(dir, reason);
+            if (MediaStore.VOLUME_INTERNAL.equals(volumeName)) {
+                for (File dir : FileUtils.getVolumeScanPaths(context, volumeName)) {
+                    provider.scanDirectory(dir, reason);
+                }
+            } else {
+                provider.scanDirectory(volume.getPath(), reason);
             }
 
             resolver.delete(scanUri, null, null);
 
         } finally {
             if (broadcastUri != null) {
-                context.sendBroadcast(
-                        new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, broadcastUri));
+                context.sendBroadcastAsUser(
+                        new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, broadcastUri), owner);
             }
         }
     }
diff --git a/src/com/android/providers/media/MediaUpgradeReceiver.java b/src/com/android/providers/media/MediaUpgradeReceiver.java
index 5864b78..abb326a 100644
--- a/src/com/android/providers/media/MediaUpgradeReceiver.java
+++ b/src/com/android/providers/media/MediaUpgradeReceiver.java
@@ -23,6 +23,7 @@
 import android.provider.Column;
 import android.util.Log;
 
+import com.android.providers.media.util.ForegroundThread;
 import com.android.providers.media.util.Metrics;
 
 import java.io.File;
@@ -45,7 +46,14 @@
     public void onReceive(Context context, Intent intent) {
         // We are now running with the system up, but no apps started,
         // so can do whatever cleanup after an upgrade that we want.
+        ForegroundThread.getExecutor().execute(() -> {
+            // Run database migration on a separate thread so that main thread
+            // is available for handling other MediaService requests.
+            tryMigratingDatabases(context);
+        });
+    }
 
+    private void tryMigratingDatabases(Context context) {
         // Lookup the last known database version
         SharedPreferences prefs = context.getSharedPreferences(TAG, Context.MODE_PRIVATE);
         int prefVersion = prefs.getInt(PREF_DB_VERSION, 0);
diff --git a/src/com/android/providers/media/MediaVolume.java b/src/com/android/providers/media/MediaVolume.java
new file mode 100644
index 0000000..1ebf5d9
--- /dev/null
+++ b/src/com/android/providers/media/MediaVolume.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+        return Objects.equals(mName, that.mName) &&
+                Objects.equals(mUser, that.mUser) &&
+                Objects.equals(mPath, that.mPath) &&
+                Objects.equals(mId, that.mId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mName, mUser, mPath, 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 6a3cced..ee37448 100644
--- a/src/com/android/providers/media/PermissionActivity.java
+++ b/src/com/android/providers/media/PermissionActivity.java
@@ -22,10 +22,14 @@
 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.ProgressDialog;
+import android.app.Dialog;
 import android.content.ContentProviderOperation;
 import android.content.ContentResolver;
 import android.content.ContentValues;
@@ -50,7 +54,6 @@
 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;
@@ -60,10 +63,12 @@
 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;
@@ -101,16 +106,30 @@
     private String volumeName;
     private ApplicationInfo appInfo;
 
-    private ProgressDialog progressDialog;
+    private AlertDialog actionDialog;
+    private AsyncTask<Void, Void, Void> positiveActionTask;
+    private Dialog 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;
 
-    private static final String VERB_WRITE = "write";
-    private static final String VERB_TRASH = "trash";
+    @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_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";
@@ -124,6 +143,8 @@
     private static final int ORDER_AUDIO = 3;
     private static final int ORDER_GENERIC = 4;
 
+    private static final int MAX_THUMBS = 3;
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -132,6 +153,12 @@
         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
@@ -150,30 +177,21 @@
             return;
         }
 
-        progressDialog = new ProgressDialog(this);
+        mHandler = new Handler(getMainLooper());
+        // Create Progress dialog
+        createProgressDialog();
 
-        // 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;
-            }
+        if (!shouldShowActionDialog(this, -1 /* pid */, appInfo.uid, getCallingPackage(),
+                null /* attributionTag */, verb)) {
+            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());
@@ -182,11 +200,11 @@
         builder.setCancelable(false);
         builder.setView(bodyView);
 
-        final AlertDialog dialog = builder.show();
+        actionDialog = builder.show();
 
         // The title is being set as a message above.
         // We need to style it like the default AlertDialog title
-        TextView dialogMessage = (TextView) dialog.findViewById(
+        TextView dialogMessage = (TextView) actionDialog.findViewById(
                 android.R.id.message);
         if (dialogMessage != null) {
             dialogMessage.setTextAppearance(R.style.PermissionAlertDialogTitle);
@@ -194,15 +212,51 @@
             Log.w(TAG, "Couldn't find message element");
         }
 
-        final WindowManager.LayoutParams params = dialog.getWindow().getAttributes();
+        final WindowManager.LayoutParams params = actionDialog.getWindow().getAttributes();
         params.width = getResources().getDimensionPixelSize(R.dimen.permission_dialog_width);
-        dialog.getWindow().setAttributes(params);
+        actionDialog.getWindow().setAttributes(params);
 
         // Hunt around to find the title of our newly created dialog so we can
         // adjust accessibility focus once descriptions have been loaded
-        titleView = (TextView) findViewByPredicate(dialog.getWindow().getDecorView(), (view) -> {
-            return (view instanceof TextView) && view.isImportantForAccessibility();
-        });
+        titleView = (TextView) findViewByPredicate(actionDialog.getWindow().getDecorView(),
+                (view) -> {
+                    return (view instanceof TextView) && view.isImportantForAccessibility();
+                });
+    }
+
+    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:
+        // 1. the task potentially runs again after the configuration is changed
+        // 2. the task completed successfully, but the activity doesn't return
+        // the response.
+        if (positiveActionTask != null) {
+            positiveActionTask.cancel(true /* mayInterruptIfRunning */);
+        }
+        // Dismiss the dialogs to avoid the window is leaked
+        if (actionDialog != null) {
+            actionDialog.dismiss();
+        }
+        if (progressDialog != null) {
+            progressDialog.dismiss();
+        }
     }
 
     private void onPositiveAction(@Nullable DialogInterface dialog, int which) {
@@ -212,9 +266,11 @@
             ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_NEGATIVE).setEnabled(false);
         }
 
-        progressDialog.show();
         final long startTime = System.currentTimeMillis();
-        new AsyncTask<Void, Void, Void>() {
+
+        mHandler.postDelayed(mShowProgressDialogRunnable, BEFORE_SHOW_PROGRESS_TIME_MS);
+
+        positiveActionTask = new AsyncTask<Void, Void, Void>() {
             @Override
             protected Void doInBackground(Void... params) {
                 Log.d(TAG, "User allowed grant for " + uris);
@@ -257,23 +313,30 @@
                 } catch (Exception e) {
                     Log.w(TAG, e);
                 }
+
                 return null;
             }
 
             @Override
             protected void onPostExecute(Void result) {
                 setResult(Activity.RESULT_OK);
-                // 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();
+                mHandler.removeCallbacks(mShowProgressDialogRunnable);
+
+                if (!progressDialog.isShowing()) {
                     finish();
                 } else {
-                    Handler handler = new Handler(getMainLooper());
-                    handler.postDelayed(() -> {
+                    // 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) {
                         progressDialog.dismiss();
                         finish();
-                    }, LEAST_SHOW_PROGRESS_TIME_MS - duration);
+                    } else {
+                        mHandler.postDelayed(() -> {
+                            progressDialog.dismiss();
+                            finish();
+                        }, LEAST_SHOW_PROGRESS_TIME_MS - duration);
+                    }
                 }
             }
         }.execute();
@@ -309,6 +372,67 @@
         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;
+        }
+        if (uris.size() == 1) {
+            // Set visible to the thumb_full to avoid the size
+            // changed of the dialog in full decoding.
+            final ImageView thumbFull = bodyView.requireViewById(R.id.thumb_full);
+            thumbFull.setVisibility(View.VISIBLE);
+        } else {
+            // If the size equals 2, we will remove thumb1 later.
+            // Set visible to the thumb2 and thumb3 first to avoid
+            // the size changed of the dialog.
+            ImageView thumb = bodyView.requireViewById(R.id.thumb2);
+            thumb.setVisibility(View.VISIBLE);
+            thumb = bodyView.requireViewById(R.id.thumb3);
+            thumb.setVisibility(View.VISIBLE);
+            // If the count of thumbs equals to MAX_THUMBS, set visible to thumb1.
+            if (uris.size() == MAX_THUMBS) {
+                thumb = bodyView.requireViewById(R.id.thumb1);
+                thumb.setVisibility(View.VISIBLE);
+            } else if (uris.size() > MAX_THUMBS) {
+                // If the count is larger than MAX_THUMBS, set visible to
+                // thumb_more_container.
+                final View container = bodyView.requireViewById(R.id.thumb_more_container);
+                container.setVisibility(View.VISIBLE);
+            }
+        }
+    }
+
     /**
      * Resolve a label that represents the app denoted by given {@link ApplicationInfo}.
      */
@@ -393,43 +517,24 @@
     }
 
     /**
-     * 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.
+     * Resolve the progress message string to be displayed to the user. All
+     * arguments have been bound and this string is ready to be displayed.
      */
-    private @Nullable CharSequence resolveMessageText() {
-        final String resName = "permission_" + verb + "_" + data + "_info";
+    private @Nullable CharSequence resolveProgressMessageText() {
+        final String resName = "permission_progress_" + verb + "_" + data;
         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, label, String.valueOf(count),
-                    String.valueOf(durationDays));
+            return TextUtils.expandTemplate(text, String.valueOf(count));
         } else {
-            // Only some actions have a secondary message string; it's okay if
+            // Only some actions have a progress 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.
@@ -456,8 +561,6 @@
      * displayed in the body of the dialog.
      */
     private class DescriptionTask extends AsyncTask<List<Uri>, Void, List<Description>> {
-        private static final int MAX_THUMBS = 3;
-
         private View bodyView;
         private Resources res;
 
@@ -482,29 +585,7 @@
 
             // If we're only asking for single item, load the full image
             if (uris.size() == 1) {
-                // Set visible to the thumb_full to avoid the size
-                // changed of the dialog in full decoding.
-                final ImageView thumbFull = bodyView.requireViewById(R.id.thumb_full);
-                thumbFull.setVisibility(View.VISIBLE);
                 loadFlags |= Description.LOAD_FULL;
-            } else {
-                // If the size equals 2, we will remove thumb1 later.
-                // Set visible to the thumb2 and thumb3 first to avoid
-                // the size changed of the dialog.
-                ImageView thumb = bodyView.requireViewById(R.id.thumb2);
-                thumb.setVisibility(View.VISIBLE);
-                thumb = bodyView.requireViewById(R.id.thumb3);
-                thumb.setVisibility(View.VISIBLE);
-                // If the count of thumbs equals to MAX_THUMBS, set visible to thumb1.
-                if (uris.size() == MAX_THUMBS) {
-                    thumb = bodyView.requireViewById(R.id.thumb1);
-                    thumb.setVisibility(View.VISIBLE);
-                } else if (uris.size() > MAX_THUMBS) {
-                    // If the count is larger than MAX_THUMBS, set visible to
-                    // thumb_more_container.
-                    final View container = bodyView.requireViewById(R.id.thumb_more_container);
-                    container.setVisibility(View.VISIBLE);
-                }
             }
 
             // Sort the uris in DATA_GENERIC case (Image, Video, Audio, Others)
@@ -643,6 +724,9 @@
         private void bindAsText(@NonNull List<Description> results) {
             final List<CharSequence> list = new ArrayList<>();
             for (int i = 0; i < results.size(); i++) {
+                if (TextUtils.isEmpty(results.get(i).contentDescription)) {
+                    continue;
+                }
                 list.add(results.get(i).contentDescription);
 
                 if (list.size() >= MAX_THUMBS && results.size() > list.size()) {
@@ -653,10 +737,11 @@
                     break;
                 }
             }
-
-            final TextView text = bodyView.requireViewById(R.id.list);
-            text.setText(TextUtils.join("\n", list));
-            text.setVisibility(View.VISIBLE);
+            if (!list.isEmpty()) {
+                final TextView text = bodyView.requireViewById(R.id.list);
+                text.setText(TextUtils.join("\n", list));
+                text.setVisibility(View.VISIBLE);
+            }
         }
     }
 
@@ -708,7 +793,9 @@
                 Log.w(TAG, e);
                 if (thumbnail == null && full == null) {
                     final String mimeType = resolver.getType(uri);
-                    mimeIcon = resolver.getTypeInfo(mimeType).getIcon();
+                    if (mimeType != null) {
+                        mimeIcon = resolver.getTypeInfo(mimeType).getIcon();
+                    }
                 }
             }
         }
diff --git a/src/com/android/providers/media/TranscodeHelper.java b/src/com/android/providers/media/TranscodeHelper.java
new file mode 100644
index 0000000..488c40d
--- /dev/null
+++ b/src/com/android/providers/media/TranscodeHelper.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+/** 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);
+}
diff --git a/src/com/android/providers/media/TranscodeHelperImpl.java b/src/com/android/providers/media/TranscodeHelperImpl.java
new file mode 100644
index 0000000..feeddeb
--- /dev/null
+++ b/src/com/android/providers/media/TranscodeHelperImpl.java
@@ -0,0 +1,1857 @@
+/*
+ * 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.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.BackgroundThread;
+import com.android.modules.utils.build.SdkLevel;
+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.LinkedHashMap;
+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;
+    @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);
+
+        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) {
+        File file = new File(path);
+        String name = file.getName();
+        final String cameraRelativePath =
+                String.format("%s/%s/", Environment.DIRECTORY_DCIM, DIRECTORY_CAMERA);
+
+        return !isTranscodeFile(path) && name.toLowerCase(Locale.ROOT).endsWith(".mp4")
+                && path.startsWith("/storage/emulated/")
+                && cameraRelativePath.equalsIgnoreCase(FileUtils.extractRelativePath(path));
+    }
+
+    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);
+
+            dumpFinishedSessions(writer);
+        }
+    }
+
+    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
new file mode 100644
index 0000000..355f21d
--- /dev/null
+++ b/src/com/android/providers/media/TranscodeHelperNoOp.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+/**
+ * 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) {}
+}
diff --git a/src/com/android/providers/media/VolumeCache.java b/src/com/android/providers/media/VolumeCache.java
new file mode 100644
index 0000000..5495a26
--- /dev/null
+++ b/src/com/android/providers/media/VolumeCache.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.media;
+
+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 android.util.LongSparseArray;
+
+import androidx.annotation.GuardedBy;
+import androidx.annotation.NonNull;
+
+import com.android.providers.media.util.FileUtils;
+import com.android.providers.media.util.UserCache;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+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;
+        }
+    }
+
+    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 0c1cb94..1f8f3cd 100644
--- a/src/com/android/providers/media/fuse/ExternalStorageServiceImpl.java
+++ b/src/com/android/providers/media/fuse/ExternalStorageServiceImpl.java
@@ -18,6 +18,7 @@
 
 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;
@@ -32,11 +33,14 @@
 
 import com.android.providers.media.MediaProvider;
 import com.android.providers.media.MediaService;
+import com.android.providers.media.MediaVolume;
 
 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.
@@ -48,9 +52,14 @@
     private static final Map<String, FuseDaemon> sFuseDaemons = new HashMap<>();
 
     @Override
-    public void onStartSession(String sessionId, /* @SessionFlag */ int flag,
+    public void onStartSession(@NonNull 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) {
@@ -70,22 +79,25 @@
     }
 
     @Override
-    public void onVolumeStateChanged(StorageVolume vol) throws IOException {
+    public void onVolumeStateChanged(@NonNull StorageVolume vol) throws IOException {
+        Objects.requireNonNull(vol);
+
         MediaProvider mediaProvider = getMediaProvider();
-        String volumeName = vol.getMediaStoreVolumeName();
 
         switch(vol.getState()) {
             case Environment.MEDIA_MOUNTED:
-                mediaProvider.attachVolume(volumeName, /* validate */ false);
+                MediaVolume volume = MediaVolume.fromStorageVolume(vol);
+                mediaProvider.attachVolume(volume, /* validate */ false);
+                MediaService.queueVolumeScan(mediaProvider.getContext(), volume, REASON_MOUNTED);
                 break;
             case Environment.MEDIA_UNMOUNTED:
             case Environment.MEDIA_EJECTING:
             case Environment.MEDIA_REMOVED:
             case Environment.MEDIA_BAD_REMOVAL:
-                mediaProvider.detachVolume(volumeName);
+                mediaProvider.detachVolume(MediaVolume.fromStorageVolume(vol));
                 break;
             default:
-                Log.i(TAG, "Ignoring volume state for vol:" + volumeName
+                Log.i(TAG, "Ignoring volume state for vol:" + vol.getMediaStoreVolumeName()
                         + ". State: " + vol.getState());
         }
         // Check for invalidation of cached volumes
@@ -93,7 +105,9 @@
     }
 
     @Override
-    public void onEndSession(String sessionId) {
+    public void onEndSession(@NonNull String sessionId) {
+        Objects.requireNonNull(sessionId);
+
         FuseDaemon daemon = onExitSession(sessionId);
 
         if (daemon == null) {
@@ -108,7 +122,24 @@
         }
     }
 
-    public FuseDaemon onExitSession(String sessionId) {
+    @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);
+
         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 9a433c9..1175249 100644
--- a/src/com/android/providers/media/fuse/FuseDaemon.java
+++ b/src/com/android/providers/media/fuse/FuseDaemon.java
@@ -24,6 +24,8 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.providers.media.MediaProvider;
 
+import java.io.File;
+import java.io.IOException;
 import java.util.Objects;
 
 /**
@@ -82,6 +84,20 @@
 
         // 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() {
@@ -151,6 +167,16 @@
         }
     }
 
+    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!
@@ -161,5 +187,7 @@
             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
new file mode 100644
index 0000000..599eee5
--- /dev/null
+++ b/src/com/android/providers/media/metrics/PulledMetrics.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.media.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.modules.utils.BackgroundThread;
+import com.android.providers.media.fuse.FuseDaemon;
+
+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/StorageAccessMetrics.java b/src/com/android/providers/media/metrics/StorageAccessMetrics.java
new file mode 100644
index 0000000..ce54e3f
--- /dev/null
+++ b/src/com/android/providers/media/metrics/StorageAccessMetrics.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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
new file mode 100644
index 0000000..4c4e1d8
--- /dev/null
+++ b/src/com/android/providers/media/metrics/TranscodeMetrics.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.media.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
new file mode 100644
index 0000000..66182bc
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/PhotoPickerActivity.java
@@ -0,0 +1,131 @@
+/*
+ * 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.photopicker;
+
+import android.app.Activity;
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.ContentUris;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.MediaStore;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+
+import com.android.providers.media.R;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Photo Picker allows users to choose one or more photos and/or videos to share with an app. The
+ * app does not get access to all photos/videos.
+ */
+public class PhotoPickerActivity extends Activity {
+
+    public static final String TAG = "PhotoPickerActivity";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // TODO(b/168001592) Change layout to show photos & options.
+        setContentView(R.layout.photo_picker);
+        Button button = findViewById(R.id.button);
+        button.setOnClickListener(v -> respondEmpty());
+
+        // TODO(b/168001592) Handle multiple selection option.
+
+        // TODO(b/168001592) Filter using given mime type.
+
+        // TODO(b/168001592) Show a photo grid instead of  ListView.
+        ListView photosList = findViewById(R.id.names_list);
+        ArrayAdapter<PhotoEntry> photosAdapter = new ArrayAdapter<>(
+                this, android.R.layout.simple_list_item_1);
+        photosList.setAdapter(photosAdapter);
+        // Clicking an item in the list returns its URI for now.
+        photosList.setOnItemClickListener((parent, view, position, id) -> {
+            respondPhoto(photosAdapter.getItem(position));
+        });
+
+        // Show the list of photo names for now.
+        ImmutableList.Builder<PhotoEntry> imageRowsBuilder = ImmutableList.builder();
+        String[] projection = new String[] {
+                MediaStore.MediaColumns._ID,
+                MediaStore.MediaColumns.DISPLAY_NAME
+        };
+        // TODO(b/168001592) call query() from worker thread.
+        Cursor cursor = getApplicationContext().getContentResolver().query(
+                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+                projection, null, null);
+        int idColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID);
+        int nameColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME);
+        // TODO(b/168001592) Use better image loading (e.g. use paging, glide).
+        while (cursor.moveToNext()) {
+            imageRowsBuilder.add(
+                    new PhotoEntry(cursor.getLong(idColumn), cursor.getString(nameColumn)));
+        }
+        photosAdapter.addAll(imageRowsBuilder.build());
+    }
+
+    private void respondPhoto(PhotoEntry photoEntry) {
+        Uri contentUri = ContentUris.withAppendedId(
+                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+                photoEntry.id);
+
+        Intent response = new Intent();
+        // TODO(b/168001592) Confirm if this flag is enough to grant the access we want.
+        response.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+        // TODO(b/168001592) Use a better label and accurate mime types.
+        if (getIntent().getBooleanExtra(Intent.EXTRA_ALLOW_MULTIPLE, false)) {
+            ClipDescription clipDescription = new ClipDescription(
+                    "Photo Picker ClipData",
+                    new String[]{"image/*", "video/*"});
+            ClipData clipData = new ClipData(clipDescription, new ClipData.Item(contentUri));
+            response.setClipData(clipData);
+        } else {
+            response.setData(contentUri);
+        }
+
+        setResult(Activity.RESULT_OK, response);
+        finish();
+    }
+
+
+    private void respondEmpty() {
+        setResult(Activity.RESULT_OK);
+        finish();
+    }
+
+    private static class PhotoEntry {
+        private long id;
+        private String name;
+
+        PhotoEntry(long id, String name) {
+            this.id = id;
+            this.name = name;
+        }
+
+        @Override
+        public String toString() {
+            return name;
+        }
+    }
+}
diff --git a/src/com/android/providers/media/playlist/Playlist.java b/src/com/android/providers/media/playlist/Playlist.java
index 53bdeeb..fd0b570 100644
--- a/src/com/android/providers/media/playlist/Playlist.java
+++ b/src/com/android/providers/media/playlist/Playlist.java
@@ -29,8 +29,10 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.file.InvalidPathException;
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -61,6 +63,9 @@
             PlaylistPersister.resolvePersister(file).read(in, mItems);
         } catch (FileNotFoundException e) {
             Log.w(TAG, "Treating missing file as empty playlist");
+        } catch (InvalidPathException e) {
+            Log.w(TAG, "Broken playlist file", e);
+            clear();
         }
     }
 
@@ -109,6 +114,28 @@
         return index;
     }
 
+    /**
+     * Removes existing playlist items that correspond to the given indexes.
+     * If an index is out of bounds, then it's ignored.
+     *
+     * @return the number of deleted items
+     */
+    public int removeMultiple(int... indexes) {
+        int res = 0;
+        Arrays.sort(indexes);
+
+        for (int i = indexes.length - 1; i >= 0; --i) {
+            final int size = mItems.size();
+            // Ignore items that are out of bounds
+            if (indexes[i] >=0 && indexes[i] < size) {
+                mItems.remove(indexes[i]);
+                res++;
+            }
+        }
+
+        return res;
+    }
+
     private static int constrain(int amount, int low, int high) {
         return amount < low ? low : (amount > high ? high : amount);
     }
diff --git a/src/com/android/providers/media/scan/LegacyMediaScanner.java b/src/com/android/providers/media/scan/LegacyMediaScanner.java
index 8d84569..d8d3bed 100644
--- a/src/com/android/providers/media/scan/LegacyMediaScanner.java
+++ b/src/com/android/providers/media/scan/LegacyMediaScanner.java
@@ -21,6 +21,8 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.providers.media.MediaVolume;
+
 import java.io.File;
 
 @Deprecated
@@ -52,7 +54,17 @@
     }
 
     @Override
-    public void onDetachVolume(String volumeName) {
+    public void onDetachVolume(MediaVolume volume) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void onIdleScanStopped() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void onDirectoryDirty(File file) {
         throw new UnsupportedOperationException();
     }
 }
diff --git a/src/com/android/providers/media/scan/MediaScanner.java b/src/com/android/providers/media/scan/MediaScanner.java
index b217da1..45d2a24 100644
--- a/src/com/android/providers/media/scan/MediaScanner.java
+++ b/src/com/android/providers/media/scan/MediaScanner.java
@@ -26,6 +26,8 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.providers.media.MediaVolume;
+
 import java.io.File;
 
 public interface MediaScanner {
@@ -38,5 +40,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(String volumeName);
+    public void onDetachVolume(MediaVolume volume);
+    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 9658827..8927bfa 100644
--- a/src/com/android/providers/media/scan/ModernMediaScanner.java
+++ b/src/com/android/providers/media/scan/ModernMediaScanner.java
@@ -37,6 +37,7 @@
 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;
@@ -47,6 +48,8 @@
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 
+import static com.android.providers.media.util.Metrics.translateReason;
+
 import android.content.ContentProviderClient;
 import android.content.ContentProviderOperation;
 import android.content.ContentProviderResult;
@@ -58,6 +61,7 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.drm.DrmManagerClient;
 import android.drm.DrmSupportInfo;
+import android.graphics.BitmapFactory;
 import android.media.ExifInterface;
 import android.media.MediaMetadataRetriever;
 import android.mtp.MtpConstants;
@@ -88,11 +92,12 @@
 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;
@@ -100,6 +105,7 @@
 
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.nio.file.FileVisitResult;
 import java.nio.file.FileVisitor;
@@ -111,6 +117,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
@@ -147,21 +154,34 @@
     // TODO: deprecate playlist editing
     // TODO: deprecate PARENT column, since callers can't see directories
 
-    @GuardedBy("sDateFormat")
-    private static final SimpleDateFormat sDateFormat;
+    @GuardedBy("S_DATE_FORMAT")
+    private static final SimpleDateFormat S_DATE_FORMAT;
+    @GuardedBy("S_DATE_FORMAT_WITH_MILLIS")
+    private static final SimpleDateFormat S_DATE_FORMAT_WITH_MILLIS;
 
     static {
-        sDateFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
-        sDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+        S_DATE_FORMAT = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
+        S_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
+
+        S_DATE_FORMAT_WITH_MILLIS = new SimpleDateFormat("yyyyMMdd'T'HHmmss.SSS");
+        S_DATE_FORMAT_WITH_MILLIS.setTimeZone(TimeZone.getTimeZone("UTC"));
     }
 
     private static final int BATCH_SIZE = 32;
+    private static final int MAX_XMP_SIZE_BYTES = 1024 * 1024;
+    // |excludeDirs * 2| < 1000 which is the max SQL expression size
+    // Because we add |excludeDir| and |excludeDir/| in the SQL expression to match dir and subdirs
+    // See SQLITE_MAX_EXPR_DEPTH in sqlite3.c
+    @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/([^/]+))?$");
+            "(?i)^/storage/[^/]+(?:/[0-9]+)?$");
     private static final Pattern PATTERN_INVISIBLE = Pattern.compile(
-            "(?i)^/storage/[^/]+(?:/[0-9]+)?(?:/Android/sandbox/([^/]+))?/" +
-                    "(?:(?:Android/(?:data|obb)$)|(?:(?:Movies|Music|Pictures)/.thumbnails$))");
+            "(?i)^/storage/[^/]+(?:/[0-9]+)?/"
+                    + "(?:(?:Android/(?:data|obb|sandbox)$)|"
+                    + "(?:\\.transforms$)|"
+                    + "(?:(?:Movies|Music|Pictures)/.thumbnails$))");
 
     private static final Pattern PATTERN_YEAR = Pattern.compile("([1-9][0-9][0-9][0-9])");
 
@@ -170,13 +190,15 @@
 
     private final Context mContext;
     private final DrmManagerClient mDrmClient;
+    @GuardedBy("mPendingCleanDirectories")
+    private final Set<String> mPendingCleanDirectories = new ArraySet<>();
 
     /**
-     * Map from volume name to signals that can be used to cancel any active
-     * scan operations on those volumes.
+     * List of active scans.
      */
-    @GuardedBy("mSignals")
-    private final ArrayMap<String, CancellationSignal> mSignals = new ArrayMap<>();
+    @GuardedBy("mActiveScans")
+
+    private final List<Scan> mActiveScans = new ArrayList<>();
 
     /**
      * Holder that contains a reference count of the number of threads
@@ -225,6 +247,8 @@
         try (Scan scan = new Scan(file, reason, /*ownerPackage*/ null)) {
             scan.run();
         } catch (OperationCanceledException ignored) {
+        } catch (FileNotFoundException e) {
+           Log.e(TAG, "Couldn't find directory to scan", e) ;
         }
     }
 
@@ -240,27 +264,51 @@
             return scan.getFirstResult();
         } catch (OperationCanceledException ignored) {
             return null;
+        } catch (FileNotFoundException e) {
+            Log.e(TAG, "Couldn't find file to scan", e) ;
+            return null;
         }
     }
 
     @Override
-    public void onDetachVolume(String volumeName) {
-        synchronized (mSignals) {
-            final CancellationSignal signal = mSignals.remove(volumeName);
-            if (signal != null) {
-                signal.cancel();
+    public void onDetachVolume(MediaVolume volume) {
+        synchronized (mActiveScans) {
+            for (Scan scan : mActiveScans) {
+                if (volume.equals(scan.mVolume)) {
+                    scan.mSignal.cancel();
+                }
             }
         }
     }
 
-    private CancellationSignal getOrCreateSignal(String volumeName) {
-        synchronized (mSignals) {
-            CancellationSignal signal = mSignals.get(volumeName);
-            if (signal == null) {
-                signal = new CancellationSignal();
-                mSignals.put(volumeName, signal);
+    @Override
+    public void onIdleScanStopped() {
+        synchronized (mActiveScans) {
+            for (Scan scan : mActiveScans) {
+                if (scan.mReason == REASON_IDLE) {
+                    scan.mSignal.cancel();
+                }
             }
-            return signal;
+        }
+    }
+
+    @Override
+    public void onDirectoryDirty(File dir) {
+        synchronized (mPendingCleanDirectories) {
+            mPendingCleanDirectories.remove(dir.getPath());
+            FileUtils.setDirectoryDirty(dir, /*isDirty*/ true);
+        }
+    }
+
+    private void addActiveScan(Scan scan) {
+        synchronized (mActiveScans) {
+            mActiveScans.add(scan);
+        }
+    }
+
+    private void removeActiveScan(Scan scan) {
+        synchronized (mActiveScans) {
+            mActiveScans.remove(scan);
         }
     }
 
@@ -275,10 +323,12 @@
 
         private final File mRoot;
         private final int mReason;
+        private final MediaVolume mVolume;
         private final String mVolumeName;
         private final Uri mFilesUri;
         private final CancellationSignal mSignal;
         private final String mOwnerPackage;
+        private final List<String> mExcludeDirs;
 
         private final long mStartGeneration;
         private final boolean mSingleFile;
@@ -299,8 +349,15 @@
          * indicates that one or more of the current file's parents is a hidden directory.
          */
         private int mHiddenDirCount;
+        /**
+         * Indicates if the nomedia directory tree is dirty. When a nomedia directory is dirty, we
+         * mark the top level nomedia as dirty. Hence if one of the sub directory in the nomedia
+         * directory is dirty, we consider the whole top level nomedia directory tree as dirty.
+         */
+        private boolean mIsDirectoryTreeDirty;
 
-        public Scan(File root, int reason, @Nullable String ownerPackage) {
+        public Scan(File root, int reason, @Nullable String ownerPackage)
+                throws FileNotFoundException {
             Trace.beginSection("ctor");
 
             mClient = mContext.getContentResolver()
@@ -309,19 +366,35 @@
 
             mRoot = root;
             mReason = reason;
-            mVolumeName = FileUtils.getVolumeName(mContext, root);
+
+            if (FileUtils.contains(Environment.getStorageDirectory(), root)) {
+                mVolume = MediaVolume.fromStorageVolume(FileUtils.getStorageVolume(mContext, root));
+            } else {
+                mVolume = MediaVolume.fromInternal();
+            }
+            mVolumeName = mVolume.getName();
             mFilesUri = MediaStore.Files.getContentUri(mVolumeName);
-            mSignal = getOrCreateSignal(mVolumeName);
+            mSignal = new CancellationSignal();
 
             mStartGeneration = MediaStore.getGeneration(mResolver, mVolumeName);
             mSingleFile = mRoot.isFile();
             mOwnerPackage = ownerPackage;
+            mExcludeDirs = new ArrayList<>();
 
             Trace.endSection();
         }
 
         @Override
         public void run() {
+            addActiveScan(this);
+            try {
+                runInternal();
+            } finally {
+                removeActiveScan(this);
+            }
+        }
+
+        private void runInternal() {
             final long startTime = SystemClock.elapsedRealtime();
 
             // First, scan everything that should be visible under requested
@@ -377,6 +450,49 @@
             }
         }
 
+        private String buildExcludeDirClause(int count) {
+            if (count == 0) {
+                return "";
+            }
+            String notLikeClause = FileColumns.DATA + " NOT LIKE ? ESCAPE '\\'";
+            String andClause = " AND ";
+            StringBuilder sb = new StringBuilder();
+            sb.append("(");
+            for (int i = 0; i < count; i++) {
+                // Append twice because we want to match the path itself and the expanded path
+                // using the SQL % LIKE operator. For instance, to exclude /sdcard/foo and all
+                // subdirs, we need the following:
+                // "NOT LIKE '/sdcard/foo/%' AND "NOT LIKE '/sdcard/foo'"
+                // The first clause matches *just* subdirs, and the second clause matches the dir
+                // itself
+                sb.append(notLikeClause);
+                sb.append(andClause);
+                sb.append(notLikeClause);
+                if (i != count - 1) {
+                    sb.append(andClause);
+                }
+            }
+            sb.append(")");
+            return sb.toString();
+        }
+
+        private void addEscapedAndExpandedPath(String path, List<String> paths) {
+            String escapedPath = DatabaseUtils.escapeForLike(path);
+            paths.add(escapedPath + "/%");
+            paths.add(escapedPath);
+        }
+
+        private String[] buildSqlSelectionArgs() {
+            List<String> escapedPaths = new ArrayList<>();
+
+            addEscapedAndExpandedPath(mRoot.getAbsolutePath(), escapedPaths);
+            for (String dir : mExcludeDirs) {
+                addEscapedAndExpandedPath(dir, escapedPaths);
+            }
+
+            return escapedPaths.toArray(new String[0]);
+        }
+
         private void reconcileAndClean() {
             final long[] scannedIds = mScannedIds.toArray();
             Arrays.sort(scannedIds);
@@ -392,26 +508,45 @@
                     + MtpConstants.FORMAT_ABSTRACT_AV_PLAYLIST;
             final String dataClause = "(" + FileColumns.DATA + " LIKE ? ESCAPE '\\' OR "
                     + FileColumns.DATA + " LIKE ? ESCAPE '\\')";
+            final String excludeDirClause = buildExcludeDirClause(mExcludeDirs.size());
             final String generationClause = FileColumns.GENERATION_ADDED + " <= "
                     + mStartGeneration;
+            final String sqlSelection = formatClause + " AND " + dataClause + " AND "
+                    + generationClause
+                    + (excludeDirClause.isEmpty() ? "" : " AND " + excludeDirClause);
             final Bundle queryArgs = new Bundle();
-            queryArgs.putString(ContentResolver.QUERY_ARG_SQL_SELECTION,
-                    formatClause + " AND " + dataClause + " AND " + generationClause);
-            final String pathEscapedForLike = DatabaseUtils.escapeForLike(mRoot.getAbsolutePath());
+            queryArgs.putString(ContentResolver.QUERY_ARG_SQL_SELECTION, sqlSelection);
             queryArgs.putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS,
-                    new String[] {pathEscapedForLike + "/%", pathEscapedForLike});
+                    buildSqlSelectionArgs());
             queryArgs.putString(ContentResolver.QUERY_ARG_SQL_SORT_ORDER,
                     FileColumns._ID + " DESC");
-            queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_PENDING, MediaStore.MATCH_EXCLUDE);
+            queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_PENDING, MediaStore.MATCH_INCLUDE);
             queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_TRASHED, MediaStore.MATCH_INCLUDE);
             queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_FAVORITE, MediaStore.MATCH_INCLUDE);
 
-            try (Cursor c = mResolver.query(mFilesUri, new String[] { FileColumns._ID },
-                    queryArgs, mSignal)) {
+            final int[] countPerMediaType = new int[FileColumns.MEDIA_TYPE_COUNT];
+            try (Cursor c = mResolver.query(mFilesUri,
+                    new String[]{FileColumns._ID, FileColumns.MEDIA_TYPE, FileColumns.DATE_EXPIRES,
+                            FileColumns.IS_PENDING}, queryArgs, mSignal)) {
                 while (c.moveToNext()) {
                     final long id = c.getLong(0);
                     if (Arrays.binarySearch(scannedIds, id) < 0) {
+                        final long dateExpire = c.getLong(2);
+                        final boolean isPending = c.getInt(3) == 1;
+                        // Don't delete the pending item which is not expired.
+                        // If the scan is triggered between invoking
+                        // ContentResolver#insert() and ContentResolver#openFileDescriptor(),
+                        // it raises the FileNotFoundException b/166063754.
+                        if (isPending && dateExpire > System.currentTimeMillis() / 1000) {
+                            continue;
+                        }
                         mUnknownIds.add(id);
+                        final int mediaType = c.getInt(1);
+                        // Avoid ArrayIndexOutOfBounds if more mediaTypes are added,
+                        // but mediaTypeSize is not updated
+                        if (mediaType < countPerMediaType.length) {
+                            countPerMediaType[mediaType]++;
+                        }
                     }
                 }
             } finally {
@@ -433,6 +568,10 @@
                 }
                 applyPending();
             } finally {
+                if (mUnknownIds.size() > 0) {
+                    String scanReason = "scan triggered by reason: " + translateReason(mReason);
+                    Metrics.logDeletionPersistent(mVolumeName, scanReason, countPerMediaType);
+                }
                 Trace.endSection();
             }
         }
@@ -504,11 +643,6 @@
 
         @Override
         public void close() {
-            // Sanity check that we drained any pending operations
-            if (!mPending.isEmpty()) {
-                throw new IllegalStateException();
-            }
-
             // Release any locks we're still holding, typically when we
             // encountered an exception; we snapshot the original list so we're
             // not confused as it's mutated by release operations
@@ -529,6 +663,27 @@
                 return FileVisitResult.SKIP_SUBTREE;
             }
 
+            synchronized (mPendingCleanDirectories) {
+                if (mIsDirectoryTreeDirty) {
+                    // Directory tree is dirty, continue scanning subtree.
+                } 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
+                    // directory.
+                    mIsDirectoryTreeDirty = true;
+                    mPendingCleanDirectories.add(dir.toFile().getPath());
+                } else {
+                    Log.d(TAG, "Skipping preVisitDirectory " + dir.toFile());
+                    if (mExcludeDirs.size() <= MAX_EXCLUDE_DIRS) {
+                        mExcludeDirs.add(dir.toFile().getPath());
+                        return FileVisitResult.SKIP_SUBTREE;
+                    } else {
+                        Log.w(TAG, "ExcludeDir size exceeded, not skipping preVisitDirectory "
+                                + dir.toFile());
+                    }
+                }
+            }
+
             // Acquire lock on this directory to ensure parallel scans don't
             // overlap and confuse each other
             acquireDirectoryLock(dir);
@@ -567,11 +722,8 @@
                 actualMimeType = mDrmClient.getOriginalMimeType(realFile.getPath());
             }
 
-            int actualMediaType = FileColumns.MEDIA_TYPE_NONE;
-            if (actualMimeType != null) {
-                actualMediaType = resolveMediaTypeFromFilePath(realFile, actualMimeType,
-                        /*isHidden*/ mHiddenDirCount > 0);
-            }
+            int actualMediaType = mediaTypeFromMimeType(
+                    realFile, actualMimeType, FileColumns.MEDIA_TYPE_NONE);
 
             Trace.beginSection("checkChanged");
 
@@ -585,7 +737,7 @@
             queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_FAVORITE, MediaStore.MATCH_INCLUDE);
             final String[] projection = new String[] {FileColumns._ID, FileColumns.DATE_MODIFIED,
                     FileColumns.SIZE, FileColumns.MIME_TYPE, FileColumns.MEDIA_TYPE,
-                    FileColumns.IS_PENDING};
+                    FileColumns.IS_PENDING, FileColumns._MODIFIER};
 
             final Matcher matcher = FileUtils.PATTERN_EXPIRES_FILE.matcher(realFile.getName());
             // If IS_PENDING is set by FUSE, we should scan the file and update IS_PENDING to zero.
@@ -595,8 +747,6 @@
             try (Cursor c = mResolver.query(mFilesUri, projection, queryArgs, mSignal)) {
                 if (c.moveToFirst()) {
                     existingId = c.getLong(0);
-                    final long dateModified = c.getLong(1);
-                    final long size = c.getLong(2);
                     final String mimeType = c.getString(3);
                     final int mediaType = c.getInt(4);
                     isPendingFromFuse &= c.getInt(5) != 0;
@@ -611,18 +761,36 @@
                         mFirstId = existingId;
                     }
 
-                    final boolean sameTime = (lastModifiedTime(realFile, attrs) == dateModified);
-                    final boolean sameSize = (attrs.size() == size);
-                    final boolean sameMimeType = mimeType == null ? actualMimeType == null :
-                            mimeType.equalsIgnoreCase(actualMimeType);
-                    final boolean sameMediaType = (actualMediaType == mediaType);
-                    final boolean isSame = sameTime && sameSize && sameMediaType && sameMimeType
-                            && !isPendingFromFuse;
-                    if (attrs.isDirectory() || isSame) {
+                    if (attrs.isDirectory()) {
+                        if (LOGV) Log.v(TAG, "Skipping directory " + file);
+                        return FileVisitResult.CONTINUE;
+                    }
+
+                    final boolean sameMetadata =
+                            hasSameMetadata(attrs, realFile, isPendingFromFuse, c);
+                    final boolean sameMediaType = actualMediaType == mediaType;
+                    if (sameMetadata && sameMediaType) {
                         if (LOGV) Log.v(TAG, "Skipping unchanged " + file);
                         return FileVisitResult.CONTINUE;
                     }
+
+                    // For this special case we may have changed mime type from the file's metadata.
+                    // This is safe because mime_type cannot be changed outside of scanning.
+                    if (sameMetadata
+                            && "video/mp4".equalsIgnoreCase(actualMimeType)
+                            && "audio/mp4".equalsIgnoreCase(mimeType)) {
+                        if (LOGV) Log.v(TAG, "Skipping unchanged video/audio " + file);
+                        return FileVisitResult.CONTINUE;
+                    }
                 }
+
+                // Since we allow top-level mime type to be customised, we need to do this early
+                // on, so the file is later scanned as the appropriate type (otherwise, this
+                // audio filed would be scanned as video and it would be missing the correct
+                // metadata).
+                actualMimeType = updateM4aMimeType(realFile, actualMimeType);
+                actualMediaType =
+                        mediaTypeFromMimeType(realFile, actualMimeType, actualMediaType);
             } finally {
                 Trace.endSection();
             }
@@ -636,6 +804,7 @@
                 Trace.endSection();
             }
             if (op != null) {
+                op.withValue(FileColumns._MODIFIER, FileColumns._MODIFIER_MEDIA_SCAN);
                 // Add owner package name to new insertions when package name is provided.
                 if (op.build().isInsert() && !attrs.isDirectory() && mOwnerPackage != null) {
                     op.withValue(MediaColumns.OWNER_PACKAGE_NAME, mOwnerPackage);
@@ -651,6 +820,51 @@
             return FileVisitResult.CONTINUE;
         }
 
+        private int mediaTypeFromMimeType(
+                File file, String mimeType, int defaultMediaType) {
+            if (mimeType != null) {
+                return resolveMediaTypeFromFilePath(
+                        file, mimeType, /*isHidden*/ mHiddenDirCount > 0);
+            }
+            return defaultMediaType;
+        }
+
+        private boolean hasSameMetadata(
+                BasicFileAttributes attrs, File realFile, boolean isPendingFromFuse, Cursor c) {
+            final long dateModified = c.getLong(1);
+            final boolean sameTime = (lastModifiedTime(realFile, attrs) == dateModified);
+
+            final long size = c.getLong(2);
+            final boolean sameSize = (attrs.size() == size);
+
+            final boolean isScanned =
+                    c.getInt(6) == FileColumns._MODIFIER_MEDIA_SCAN;
+
+            return sameTime && sameSize && !isPendingFromFuse && isScanned;
+        }
+
+        /**
+         * For this one very narrow case, we allow mime types to be customised when the top levels
+         * differ. This opens the given file, so avoid calling unless really necessary. This
+         * returns the defaultMimeType for non-m4a files or if opening the file throws an exception.
+         */
+        private String updateM4aMimeType(File file, String defaultMimeType) {
+            if ("video/mp4".equalsIgnoreCase(defaultMimeType)) {
+                try (
+                    FileInputStream is = new FileInputStream(file);
+                    MediaMetadataRetriever mmr = new MediaMetadataRetriever()) {
+                    mmr.setDataSource(is.getFD());
+                    String refinedMimeType = mmr.extractMetadata(METADATA_KEY_MIMETYPE);
+                    if ("audio/mp4".equalsIgnoreCase(refinedMimeType)) {
+                        return refinedMimeType;
+                    }
+                } catch (Exception e) {
+                    return defaultMimeType;
+                }
+            }
+            return defaultMimeType;
+        }
+
         @Override
         public FileVisitResult visitFileFailed(Path file, IOException exc)
                 throws IOException {
@@ -673,6 +887,15 @@
             // allow other parallel scans to proceed
             releaseDirectoryLock(dir);
 
+            if (mIsDirectoryTreeDirty) {
+                synchronized (mPendingCleanDirectories) {
+                    if (mPendingCleanDirectories.remove(dir.toFile().getPath())) {
+                        // If |dir| is still clean, then persist
+                        FileUtils.setDirectoryDirty(dir.toFile(), false /* isDirty */);
+                        mIsDirectoryTreeDirty = false;
+                    }
+                }
+            }
             return FileVisitResult.CONTINUE;
         }
 
@@ -895,7 +1118,16 @@
         op.withValue(MediaColumns.DOCUMENT_ID, xmp.getDocumentId());
         op.withValue(MediaColumns.INSTANCE_ID, xmp.getInstanceId());
         op.withValue(MediaColumns.ORIGINAL_DOCUMENT_ID, xmp.getOriginalDocumentId());
-        op.withValue(MediaColumns.XMP, xmp.getRedactedXmp());
+        op.withValue(MediaColumns.XMP, maybeTruncateXmp(xmp));
+    }
+
+    private static byte[] maybeTruncateXmp(XmpInterface xmp) {
+        byte[] redacted = xmp.getRedactedXmp();
+        if (redacted.length > MAX_XMP_SIZE_BYTES) {
+            return new byte[0];
+        }
+
+        return redacted;
     }
 
     /**
@@ -935,6 +1167,40 @@
         }
     }
 
+    private static void withResolutionValues(
+            @NonNull ContentProviderOperation.Builder op,
+            @NonNull ExifInterface exif, @NonNull File file) {
+        final Optional<?> width = parseOptionalOrZero(
+                exif.getAttribute(ExifInterface.TAG_IMAGE_WIDTH));
+        final Optional<?> height = parseOptionalOrZero(
+                exif.getAttribute(ExifInterface.TAG_IMAGE_LENGTH));
+        final Optional<String> resolution = parseOptionalResolution(width, height);
+        if (resolution.isPresent()) {
+            withOptionalValue(op, MediaColumns.WIDTH, width);
+            withOptionalValue(op, MediaColumns.HEIGHT, height);
+            op.withValue(MediaColumns.RESOLUTION, resolution.get());
+        } else {
+            withBitmapResolutionValues(op, file);
+        }
+    }
+
+    private static void withBitmapResolutionValues(
+            @NonNull ContentProviderOperation.Builder op,
+            @NonNull File file) {
+        final BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
+        bitmapOptions.inSampleSize = 1;
+        bitmapOptions.inJustDecodeBounds = true;
+        bitmapOptions.outWidth = 0;
+        bitmapOptions.outHeight = 0;
+        BitmapFactory.decodeFile(file.getAbsolutePath(), bitmapOptions);
+
+        final Optional<?> width = parseOptionalOrZero(bitmapOptions.outWidth);
+        final Optional<?> height = parseOptionalOrZero(bitmapOptions.outHeight);
+        withOptionalValue(op, MediaColumns.WIDTH, width);
+        withOptionalValue(op, MediaColumns.HEIGHT, height);
+        withOptionalValue(op, MediaColumns.RESOLUTION, parseOptionalResolution(width, height));
+    }
+
     private static @NonNull ContentProviderOperation.Builder scanItemDirectory(long existingId,
             File file, BasicFileAttributes attrs, String mimeType, String volumeName) {
         final ContentProviderOperation.Builder op = newUpsert(volumeName, existingId);
@@ -958,6 +1224,11 @@
         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,
@@ -968,6 +1239,7 @@
 
         op.withValue(MediaColumns.ARTIST, UNKNOWN_STRING);
         op.withValue(MediaColumns.ALBUM, file.getParentFile().getName());
+        op.withValue(AudioColumns.TRACK, null);
 
         final String lowPath = file.getAbsolutePath().toLowerCase(Locale.ROOT);
         boolean anyMatch = false;
@@ -1045,6 +1317,7 @@
         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()) {
@@ -1067,6 +1340,8 @@
                         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
@@ -1091,12 +1366,8 @@
         try (FileInputStream is = new FileInputStream(file)) {
             final ExifInterface exif = new ExifInterface(is);
 
-            withOptionalValue(op, MediaColumns.WIDTH,
-                    parseOptionalOrZero(exif.getAttribute(ExifInterface.TAG_IMAGE_WIDTH)));
-            withOptionalValue(op, MediaColumns.HEIGHT,
-                    parseOptionalOrZero(exif.getAttribute(ExifInterface.TAG_IMAGE_LENGTH)));
-            withOptionalValue(op, MediaColumns.RESOLUTION,
-                    parseOptionalResolution(exif));
+            withResolutionValues(op, exif, file);
+
             withOptionalValue(op, MediaColumns.DATE_TAKEN,
                     parseOptionalDateTaken(exif, lastModifiedTime(file, attrs) * 1000));
             withOptionalValue(op, MediaColumns.ORIENTATION,
@@ -1256,11 +1527,7 @@
             @NonNull MediaMetadataRetriever mmr) {
         final Optional<?> width = parseOptional(mmr.extractMetadata(METADATA_KEY_VIDEO_WIDTH));
         final Optional<?> height = parseOptional(mmr.extractMetadata(METADATA_KEY_VIDEO_HEIGHT));
-        if (width.isPresent() && height.isPresent()) {
-            return Optional.of(width.get() + "\u00d7" + height.get());
-        } else {
-            return Optional.empty();
-        }
+        return parseOptionalResolution(width, height);
     }
 
     @VisibleForTesting
@@ -1268,11 +1535,7 @@
             @NonNull MediaMetadataRetriever mmr) {
         final Optional<?> width = parseOptional(mmr.extractMetadata(METADATA_KEY_IMAGE_WIDTH));
         final Optional<?> height = parseOptional(mmr.extractMetadata(METADATA_KEY_IMAGE_HEIGHT));
-        if (width.isPresent() && height.isPresent()) {
-            return Optional.of(width.get() + "\u00d7" + height.get());
-        } else {
-            return Optional.empty();
-        }
+        return parseOptionalResolution(width, height);
     }
 
     @VisibleForTesting
@@ -1282,26 +1545,46 @@
                 exif.getAttribute(ExifInterface.TAG_IMAGE_WIDTH));
         final Optional<?> height = parseOptionalOrZero(
                 exif.getAttribute(ExifInterface.TAG_IMAGE_LENGTH));
+        return parseOptionalResolution(width, height);
+    }
+
+    private static @NonNull Optional<String> parseOptionalResolution(
+            @NonNull Optional<?> width, @NonNull Optional<?> height) {
         if (width.isPresent() && height.isPresent()) {
             return Optional.of(width.get() + "\u00d7" + height.get());
-        } else {
-            return Optional.empty();
         }
+        return Optional.empty();
     }
 
     @VisibleForTesting
     static @NonNull Optional<Long> parseOptionalDate(@Nullable String date) {
         if (TextUtils.isEmpty(date)) return Optional.empty();
         try {
-            synchronized (sDateFormat) {
-                final long value = sDateFormat.parse(date).getTime();
-                return (value > 0) ? Optional.of(value) : Optional.empty();
+            synchronized (S_DATE_FORMAT_WITH_MILLIS) {
+                return parseDateWithFormat(date, S_DATE_FORMAT_WITH_MILLIS);
             }
         } catch (ParseException e) {
+            // Log and try without millis as well
+            Log.d(TAG, String.format(
+                    "Parsing date with millis failed for [%s]. We will retry without millis",
+                    date));
+        }
+        try {
+            synchronized (S_DATE_FORMAT) {
+                return parseDateWithFormat(date, S_DATE_FORMAT);
+            }
+        } catch (ParseException e) {
+            Log.d(TAG, String.format("Parsing date without millis failed for [%s]", date));
             return Optional.empty();
         }
     }
 
+    private static Optional<Long> parseDateWithFormat(
+            @Nullable String date, SimpleDateFormat dateFormat) throws ParseException {
+        final long value = dateFormat.parse(date).getTime();
+        return (value > 0) ? Optional.of(value) : Optional.empty();
+    }
+
     @VisibleForTesting
     static @NonNull Optional<Integer> parseOptionalYear(@Nullable String value) {
         final Optional<String> parsedValue = parseOptional(value);
@@ -1347,12 +1630,6 @@
 
         if (fileMimeType.regionMatches(true, 0, refinedMimeType, 0, refinedSplit + 1)) {
             return Optional.of(refinedMimeType);
-        } else if ("video/mp4".equalsIgnoreCase(fileMimeType)
-                && "audio/mp4".equalsIgnoreCase(refinedMimeType)) {
-            // We normally only allow MIME types to be customized when the
-            // top-level type agrees, but this one very narrow case is added to
-            // support a music service that was writing "m4a" files as "mp4".
-            return Optional.of(refinedMimeType);
         } else {
             return Optional.empty();
         }
diff --git a/src/com/android/providers/media/scan/NullMediaScanner.java b/src/com/android/providers/media/scan/NullMediaScanner.java
index 3dfe4a2..7a1a396 100644
--- a/src/com/android/providers/media/scan/NullMediaScanner.java
+++ b/src/com/android/providers/media/scan/NullMediaScanner.java
@@ -23,6 +23,8 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.providers.media.MediaVolume;
+
 import java.io.File;
 
 /**
@@ -61,7 +63,17 @@
     }
 
     @Override
-    public void onDetachVolume(String volumeName) {
+    public void onDetachVolume(MediaVolume volume) {
+        // Ignored
+    }
+
+    @Override
+    public void onIdleScanStopped() {
+        // Ignored
+    }
+
+    @Override
+    public void onDirectoryDirty(File file) {
         // Ignored
     }
 }
diff --git a/src/com/android/providers/media/util/ExifUtils.java b/src/com/android/providers/media/util/ExifUtils.java
index 7593ec8..f3a23eb 100644
--- a/src/com/android/providers/media/util/ExifUtils.java
+++ b/src/com/android/providers/media/util/ExifUtils.java
@@ -34,6 +34,7 @@
 
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
 
 import java.text.ParsePosition;
 import java.text.SimpleDateFormat;
@@ -145,19 +146,26 @@
             long msecs = datetime.getTime();
 
             if (subSecs != null) {
-                try {
-                    long sub = Long.parseLong(subSecs);
-                    while (sub > 1000) {
-                        sub /= 10;
-                    }
-                    msecs += sub;
-                } catch (NumberFormatException e) {
-                    // Ignored
-                }
+                msecs += parseSubSeconds(subSecs);
             }
             return msecs;
         } catch (IllegalArgumentException e) {
             return -1;
         }
     }
+
+    @VisibleForTesting
+    static @CurrentTimeMillisLong long parseSubSeconds(@NonNull String subSec) {
+        try {
+            final int len = Math.min(subSec.length(), 3);
+            long sub = Long.parseLong(subSec.substring(0, len));
+            for (int i = len; i < 3; i++) {
+                sub *= 10;
+            }
+            return sub;
+        } catch (NumberFormatException e) {
+            // Ignored
+        }
+        return 0L;
+    }
 }
diff --git a/src/com/android/providers/media/util/FileUtils.java b/src/com/android/providers/media/util/FileUtils.java
index 47c5bc1..faa80b6 100644
--- a/src/com/android/providers/media/util/FileUtils.java
+++ b/src/com/android/providers/media/util/FileUtils.java
@@ -45,10 +45,15 @@
 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;
 import android.provider.MediaStore.MediaColumns;
 import android.system.ErrnoException;
@@ -63,6 +68,8 @@
 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;
@@ -89,6 +96,11 @@
 import java.util.regex.Pattern;
 
 public class FileUtils {
+    // Even though vfat allows 255 UCS-2 chars, we might eventually write to
+    // ext4 through a FUSE layer, so use that limit.
+    @VisibleForTesting
+    static final int MAX_FILENAME_BYTES = 255;
+
     /**
      * Drop-in replacement for {@link ParcelFileDescriptor#open(File, int)}
      * which adds security features like {@link OsConstants#O_CLOEXEC} and
@@ -386,18 +398,31 @@
         }
     }
 
+    private static final int MAX_READ_STRING_SIZE = 4096;
+
     /**
      * Read given {@link File} as a single {@link String}. Returns
-     * {@link Optional#empty()} when the file doesn't exist.
+     * {@link Optional#empty()} when
+     * <ul>
+     * <li> the file doesn't exist or
+     * <li> the size of the file exceeds {@code MAX_READ_STRING_SIZE}
+     * </ul>
      */
     public static @NonNull Optional<String> readString(@NonNull File file) throws IOException {
         try {
-            final String value = new String(Files.readAllBytes(file.toPath()),
-                    StandardCharsets.UTF_8);
-            return Optional.of(value);
-        } catch (NoSuchFileException e) {
-            return Optional.empty();
+            if (file.length() <= MAX_READ_STRING_SIZE) {
+                final String value = new String(Files.readAllBytes(file.toPath()),
+                        StandardCharsets.UTF_8);
+                return Optional.of(value);
+            }
+            // When file size exceeds MAX_READ_STRING_SIZE, file is either
+            // corrupted or doesn't the contain expected data. Hence we return
+            // Optional.empty() which will be interpreted as empty file.
+            Logging.logPersistent(String.format("Ignored reading %s, file size exceeds %d", file,
+                    MAX_READ_STRING_SIZE));
+        } catch (NoSuchFileException ignored) {
         }
+        return Optional.empty();
     }
 
     /**
@@ -507,9 +532,8 @@
                 res.append('_');
             }
         }
-        // Even though vfat allows 255 UCS-2 chars, we might eventually write to
-        // ext4 through a FUSE layer, so use that limit.
-        trimFilename(res, 255);
+
+        trimFilename(res, MAX_FILENAME_BYTES);
         return res.toString();
     }
 
@@ -573,7 +597,7 @@
                     int i = Integer.parseInt(dcfStrict.group(2));
                     @Override
                     public String next() {
-                        final String res = String.format("%s%04d", prefix, i);
+                        final String res = String.format(Locale.US, "%s%04d", prefix, i);
                         i++;
                         return res;
                     }
@@ -589,11 +613,14 @@
                 // Generate names like "IMG_20190102_030405~2"
                 final String prefix = dcfRelaxed.group(1);
                 return new Iterator<String>() {
-                    int i = TextUtils.isEmpty(dcfRelaxed.group(2)) ? 1
+                    int i = TextUtils.isEmpty(dcfRelaxed.group(2))
+                            ? 1
                             : Integer.parseInt(dcfRelaxed.group(2));
                     @Override
                     public String next() {
-                        final String res = (i == 1) ? prefix : String.format("%s~%d", prefix, i);
+                        final String res = (i == 1)
+                            ? prefix
+                            : String.format(Locale.US, "%s~%d", prefix, i);
                         i++;
                         return res;
                     }
@@ -807,8 +834,15 @@
         }
 
         final Uri uri = MediaStore.Files.getContentUri(volumeName);
-        final File path = context.getSystemService(StorageManager.class).getStorageVolume(uri)
-                .getDirectory();
+        File path = null;
+
+        try {
+            path = context.getSystemService(StorageManager.class).getStorageVolume(uri)
+                    .getDirectory();
+        } catch (IllegalStateException e) {
+            Log.w("Ignoring volume not found exception", e);
+        }
+
         if (path != null) {
             return path;
         } else {
@@ -829,21 +863,49 @@
     }
 
     /**
+     * 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) {
+    public static @NonNull String getVolumeName(@NonNull Context context, @NonNull File path)
+            throws FileNotFoundException {
         if (contains(Environment.getStorageDirectory(), path)) {
-            return context.getSystemService(StorageManager.class).getStorageVolume(path)
-                    .getMediaStoreVolumeName();
+            StorageVolume volume = getStorageVolume(context, path);
+            return volume.getMediaStoreVolumeName();
         } else {
             return MediaStore.VOLUME_INTERNAL;
         }
     }
 
     public static final Pattern PATTERN_DOWNLOADS_FILE = Pattern.compile(
-            "(?i)^/storage/[^/]+/(?:[0-9]+/)?(?:Android/sandbox/[^/]+/)?Download/.+");
+            "(?i)^/storage/[^/]+/(?:[0-9]+/)?Download/.+");
     public static final Pattern PATTERN_DOWNLOADS_DIRECTORY = Pattern.compile(
-            "(?i)^/storage/[^/]+/(?:[0-9]+/)?(?:Android/sandbox/[^/]+/)?Download/?");
+            "(?i)^/storage/[^/]+/(?:[0-9]+/)?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(
@@ -871,6 +933,12 @@
      */
     public static final long DEFAULT_DURATION_TRASHED = 30 * DateUtils.DAY_IN_MILLIS;
 
+    /**
+     * Default duration that expired items should be extended in
+     * {@link #runIdleMaintenance}.
+     */
+    public static final long DEFAULT_DURATION_EXTENDED = 7 * DateUtils.DAY_IN_MILLIS;
+
     public static boolean isDownload(@NonNull String path) {
         return PATTERN_DOWNLOADS_FILE.matcher(path).matches();
     }
@@ -879,40 +947,87 @@
         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]+/)?Android/(?:data|media|obb|sandbox)/([^/]+)(/?.*)?");
+            "(?i)^/storage/[^/]+/(?:[0-9]+/)?"
+            + PROP_CROSS_USER_ROOT_PATTERN
+            + "Android/(?:data|media|obb)/([^/]+)(/?.*)?");
 
     /**
      * Regex that matches Android/obb or Android/data path.
      */
     public static final Pattern PATTERN_DATA_OR_OBB_PATH = Pattern.compile(
-            "(?i)^/storage/[^/]+/(?:[0-9]+/)?Android/(?:data|obb)/?$");
-
-    @VisibleForTesting
-    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,
-    };
+            "(?i)^/storage/[^/]+/(?:[0-9]+/)?"
+            + PROP_CROSS_USER_ROOT_PATTERN
+            + "Android/(?:data|obb)/?$");
 
     /**
-     * Regex that matches paths for {@link MediaColumns#RELATIVE_PATH}; it
-     * captures both top-level paths and sandboxed paths.
+     * 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)(/?.*)");
+
+    /**
+     * 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";
+
+    @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,
+            };
+        }
+    }
+
+    /**
+     * Regex that matches paths for {@link MediaColumns#RELATIVE_PATH}
      */
     private static final Pattern PATTERN_RELATIVE_PATH = Pattern.compile(
-            "(?i)^/storage/(?:emulated/[0-9]+/|[^/]+/)(Android/sandbox/([^/]+)/)?");
+            "(?i)^/storage/(?:emulated/[0-9]+/|[^/]+/)");
 
     /**
      * Regex that matches paths under well-known storage paths.
@@ -920,13 +1035,33 @@
     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);
@@ -1012,6 +1147,21 @@
         }
     }
 
+    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.
      */
@@ -1022,6 +1172,15 @@
     }
 
     /**
+     * Returns true if relative path is Android/obb path.
+     */
+    public static boolean isObbOrChildPath(String path) {
+        if (path == null) return false;
+        final Matcher m = PATTERN_OBB_OR_CHILD_PATH.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.
      */
@@ -1031,8 +1190,26 @@
         if (relativePath == null) {
             return null;
         }
-        final String[] relativePathSegments = relativePath.split("/");
-        return relativePathSegments.length > 0 ? relativePathSegments[0] : 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;
     }
 
     public static boolean isDefaultDirectoryName(@Nullable String dirName) {
@@ -1146,13 +1323,21 @@
         if (!isForFuse && getAsBoolean(values, MediaColumns.IS_PENDING, false)) {
             final long dateExpires = getAsLong(values, MediaColumns.DATE_EXPIRES,
                     (System.currentTimeMillis() + DEFAULT_DURATION_PENDING) / 1000);
-            resolvedDisplayName = String.format(".%s-%d-%s",
-                    FileUtils.PREFIX_PENDING, dateExpires, displayName);
+            final String combinedString = String.format(
+                    Locale.US, ".%s-%d-%s", FileUtils.PREFIX_PENDING, dateExpires, displayName);
+            // trim the file name to avoid ENAMETOOLONG error
+            // after trim the file, if the user unpending the file,
+            // the file name is not the original one
+            resolvedDisplayName = trimFilename(combinedString, MAX_FILENAME_BYTES);
         } else if (getAsBoolean(values, MediaColumns.IS_TRASHED, false)) {
             final long dateExpires = getAsLong(values, MediaColumns.DATE_EXPIRES,
                     (System.currentTimeMillis() + DEFAULT_DURATION_TRASHED) / 1000);
-            resolvedDisplayName = String.format(".%s-%d-%s",
-                    FileUtils.PREFIX_TRASHED, dateExpires, displayName);
+            final String combinedString = String.format(
+                    Locale.US, ".%s-%d-%s", FileUtils.PREFIX_TRASHED, dateExpires, displayName);
+            // trim the file name to avoid ENAMETOOLONG error
+            // after trim the file, if the user untrashes the file,
+            // the file name is not the original one
+            resolvedDisplayName = trimFilename(combinedString, MAX_FILENAME_BYTES);
         } else {
             resolvedDisplayName = displayName;
         }
@@ -1264,12 +1449,42 @@
             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
@@ -1322,4 +1537,85 @@
         }
         return status;
     }
+
+    /**
+     * @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");
+        if (nomedia.exists()) {
+            try {
+                Optional<String> expectedPath = readString(nomedia);
+                // Returns true If .nomedia file is empty or content doesn't match |dir|
+                // Returns false otherwise
+                return !expectedPath.isPresent()
+                        || !expectedPath.get().equals(dir.getPath());
+            } catch (IOException e) {
+                Log.w(TAG, "Failed to read directory dirty" + dir);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * {@code isDirty} == {@code true} will force {@code dir} scanning even if it's hidden
+     * {@code isDirty} == {@code false} will skip {@code dir} scanning on next scan.
+     */
+    public static void setDirectoryDirty(File dir, boolean isDirty) {
+        File nomedia = new File(dir, ".nomedia");
+        if (nomedia.exists()) {
+            try {
+                writeString(nomedia, isDirty ? Optional.of("") : Optional.of(dir.getPath()));
+            } catch (IOException e) {
+                Log.w(TAG, "Failed to change directory dirty: " + dir + ". isDirty: " + isDirty);
+            }
+        }
+    }
+
+    /**
+     * @return the folder containing the top-most .nomedia in {@code file} hierarchy.
+     * E.g input as /sdcard/foo/bar/ will return /sdcard/foo
+     * even if foo and bar contain .nomedia files.
+     *
+     * Returns {@code null} if there's no .nomedia in hierarchy
+     */
+    public static File getTopLevelNoMedia(@NonNull File file) {
+        File topNoMediaDir = null;
+
+        File parent = file;
+        while (parent != null) {
+            File nomedia = new File(parent, ".nomedia");
+            if (nomedia.exists()) {
+                topNoMediaDir = parent;
+            }
+            parent = parent.getParentFile();
+        }
+
+        return topNoMediaDir;
+    }
+
+    /**
+     * Generate the extended absolute path from the expired file path
+     * E.g. the input expiredFilePath is /storage/emulated/0/DCIM/.trashed-1621147340-test.jpg
+     * The returned result is /storage/emulated/0/DCIM/.trashed-1888888888-test.jpg
+     *
+     * @hide
+     */
+    @Nullable
+    public static String getAbsoluteExtendedPath(@NonNull String expiredFilePath,
+            long extendedTime) {
+        final String displayName = extractDisplayName(expiredFilePath);
+
+        final Matcher matcher = PATTERN_EXPIRES_FILE.matcher(displayName);
+        if (matcher.matches()) {
+            final String newDisplayName = String.format(Locale.US, ".%s-%d-%s", matcher.group(1),
+                    extendedTime, matcher.group(3));
+            final int lastSlash = expiredFilePath.lastIndexOf('/');
+            final String newPath = expiredFilePath.substring(0, lastSlash + 1).concat(
+                    newDisplayName);
+            return newPath;
+        }
+
+        return null;
+    }
 }
diff --git a/src/com/android/providers/media/util/IsoInterface.java b/src/com/android/providers/media/util/IsoInterface.java
index 5de4b6b..03b46c9 100644
--- a/src/com/android/providers/media/util/IsoInterface.java
+++ b/src/com/android/providers/media/util/IsoInterface.java
@@ -177,14 +177,25 @@
                 return null;
             }
 
-            box.data = new byte[(int) (len - box.headerSize)];
+            try {
+                box.data = new byte[(int) (len - box.headerSize)];
+            } catch (OutOfMemoryError e) {
+                Log.w(TAG, "Couldn't read large uuid box", e);
+                return null;
+            }
             Os.read(fd, box.data, 0, box.data.length);
         } else if (type == BOX_XMP) {
             if (len > Integer.MAX_VALUE) {
                 Log.w(TAG, "Skipping abnormally large xmp box");
                 return null;
             }
-            box.data = new byte[(int) (len - box.headerSize)];
+
+            try {
+                box.data = new byte[(int) (len - box.headerSize)];
+            } catch (OutOfMemoryError e) {
+                Log.w(TAG, "Couldn't read large xmp box", e);
+                return null;
+            }
             Os.read(fd, box.data, 0, box.data.length);
         } else if (type == BOX_META && len != headerSize) {
             // The format of this differs in ISO and QT encoding:
diff --git a/src/com/android/providers/media/util/Logging.java b/src/com/android/providers/media/util/Logging.java
index f62c361..ff69e5c 100644
--- a/src/com/android/providers/media/util/Logging.java
+++ b/src/com/android/providers/media/util/Logging.java
@@ -23,7 +23,9 @@
 import android.text.format.DateUtils;
 import android.util.Log;
 
+import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import java.io.File;
 import java.io.IOException;
@@ -50,16 +52,26 @@
     private static final int PERSISTENT_SIZE = 32 * 1024;
     private static final int PERSISTENT_COUNT = 4;
     private static final long PERSISTENT_AGE = DateUtils.WEEK_IN_MILLIS;
+    private static final SimpleDateFormat DATE_FORMAT =
+            new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+    private static final Object LOCK = new Object();
 
+    @GuardedBy("LOCK")
     private static Path sPersistentDir;
-    private static SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+    @GuardedBy("LOCK")
+    private static Path sPersistentFile;
+    @GuardedBy("LOCK")
+    private static Writer sWriter;
 
     /**
      * Initialize persistent logging which is then available through
      * {@link #logPersistent(String)} and {@link #dumpPersistent(PrintWriter)}.
      */
     public static void initPersistent(@NonNull File persistentDir) {
-        sPersistentDir = persistentDir.toPath();
+        synchronized (LOCK) {
+            sPersistentDir = persistentDir.toPath();
+            closeWriterAndUpdatePathLocked(null);
+        }
     }
 
     /**
@@ -68,28 +80,68 @@
     public static void logPersistent(@NonNull String msg) {
         Log.i(TAG, msg);
 
-        if (sPersistentDir == null) return;
-        try (Writer w = Files.newBufferedWriter(resolveCurrentPersistentFile(), CREATE, APPEND)) {
-            w.write(sDateFormat.format(new Date()) + " " + msg + "\n");
-        } catch (IOException e) {
-            Log.w(TAG, "Failed to persist: " + e);
+        synchronized (LOCK) {
+            if (sPersistentDir == null) return;
+
+            try {
+                Path path = resolveCurrentPersistentFileLocked();
+                if (!path.equals(sPersistentFile)) {
+                    closeWriterAndUpdatePathLocked(path);
+                }
+
+                if (sWriter == null) {
+                    sWriter = Files.newBufferedWriter(path, CREATE, APPEND);
+                }
+
+                sWriter.write(DATE_FORMAT.format(new Date()) + " " + msg + "\n");
+                // Flush to guarantee that all our writes have been sent to the filesystem
+                sWriter.flush();
+            } catch (IOException e) {
+                closeWriterAndUpdatePathLocked(null);
+                Log.w(TAG, "Failed to write: " + sPersistentFile, e);
+            }
         }
     }
 
+    @GuardedBy("LOCK")
+    private static void closeWriterAndUpdatePathLocked(@Nullable Path newPath) {
+        if (sWriter != null) {
+            try {
+                sWriter.close();
+            } catch (IOException ignored) {
+                Log.w(TAG, "Failed to close: " + sPersistentFile, ignored);
+            }
+            sWriter = null;
+        }
+        sPersistentFile = newPath;
+    }
+
     /**
      * Trim any persistent logs, typically called during idle maintenance.
      */
     public static void trimPersistent() {
-        if (sPersistentDir == null) return;
-        FileUtils.deleteOlderFiles(sPersistentDir.toFile(), PERSISTENT_COUNT, PERSISTENT_AGE);
+        File persistentDir = null;
+        synchronized (LOCK) {
+            if (sPersistentDir == null) return;
+            persistentDir = sPersistentDir.toFile();
+
+            closeWriterAndUpdatePathLocked(sPersistentFile);
+        }
+
+        FileUtils.deleteOlderFiles(persistentDir, PERSISTENT_COUNT, PERSISTENT_AGE);
     }
 
     /**
      * Dump any persistent logs.
      */
     public static void dumpPersistent(@NonNull PrintWriter pw) {
-        if (sPersistentDir == null) return;
-        try (Stream<Path> stream = Files.list(sPersistentDir)) {
+        Path persistentDir = null;
+        synchronized (LOCK) {
+            if (sPersistentDir == null) return;
+            persistentDir = sPersistentDir;
+        }
+
+        try (Stream<Path> stream = Files.list(persistentDir)) {
             stream.sorted().forEach((path) -> {
                 dumpPersistentFile(path, pw);
             });
@@ -117,14 +169,12 @@
      * starts new files when the current file is larger than
      * {@link #PERSISTENT_SIZE}.
      */
-    private static @NonNull Path resolveCurrentPersistentFile() throws IOException {
-        try (Stream<Path> stream = Files.list(sPersistentDir)) {
-            Optional<Path> latest = stream.max(Comparator.naturalOrder());
-            if (latest.isPresent() && latest.get().toFile().length() < PERSISTENT_SIZE) {
-                return latest.get();
-            } else {
-                return sPersistentDir.resolve(String.valueOf(System.currentTimeMillis()));
-            }
+    @GuardedBy("LOCK")
+    private static @NonNull Path resolveCurrentPersistentFileLocked() throws IOException {
+        if (sPersistentFile != null && sPersistentFile.toFile().length() < PERSISTENT_SIZE) {
+            return sPersistentFile;
         }
+
+        return sPersistentDir.resolve(String.valueOf(System.currentTimeMillis()));
     }
 }
diff --git a/src/com/android/providers/media/util/Metrics.java b/src/com/android/providers/media/util/Metrics.java
index 86a0302..024151d 100644
--- a/src/com/android/providers/media/util/Metrics.java
+++ b/src/com/android/providers/media/util/Metrics.java
@@ -60,12 +60,30 @@
                 normalizedInsertCount, normalizedUpdateCount, normalizedDeleteCount);
     }
 
-    public static void logDeletion(@NonNull String volumeName, int uid, String packageName,
-            int itemCount) {
-        Logging.logPersistent(String.format(
-                "Deleted %3$d items on %1$s due to %2$s",
-                volumeName, packageName, itemCount));
+    /**
+     * Logs persistent deletion logs on-device.
+     */
+    public static void logDeletionPersistent(@NonNull String volumeName, String reason,
+            int[] countPerMediaType) {
+        final StringBuilder builder = new StringBuilder("Deleted ");
+        for (int count: countPerMediaType) {
+            builder.append(count).append(' ');
+        }
+        builder.append("items on ")
+                .append(volumeName)
+                .append(" due to ")
+                .append(reason);
 
+        Logging.logPersistent(builder.toString());
+    }
+
+    /**
+     * Logs persistent deletion logs on-device and stats metrics. Count of items per-media-type
+     * are not uploaded to MediaProviderStats logs.
+     */
+    public static void logDeletion(@NonNull String volumeName, int uid, String packageName,
+            int itemCount, int[] countPerMediaType) {
+        logDeletionPersistent(volumeName, packageName, countPerMediaType);
         MediaProviderStatsLog.write(MEDIA_CONTENT_DELETED,
                 translateVolumeName(volumeName), uid, itemCount);
     }
@@ -93,10 +111,10 @@
     }
 
     public static void logSchemaChange(@NonNull String volumeName, int versionFrom, int versionTo,
-            long itemCount, long durationMillis) {
+            long itemCount, long durationMillis, @NonNull String databaseUuid) {
         Logging.logPersistent(String.format(
-                "Changed schema version on %s from %d to %d, %d items taking %dms",
-                volumeName, versionFrom, versionTo, itemCount, durationMillis));
+                "Changed schema version on %s from %d to %d, %d items taking %dms UUID %s",
+                volumeName, versionFrom, versionTo, itemCount, durationMillis, databaseUuid));
 
         final float normalizedDurationMillis = ((float) durationMillis) / itemCount;
 
diff --git a/src/com/android/providers/media/util/PermissionUtils.java b/src/com/android/providers/media/util/PermissionUtils.java
index adbe0e2..5b3639c 100644
--- a/src/com/android/providers/media/util/PermissionUtils.java
+++ b/src/com/android/providers/media/util/PermissionUtils.java
@@ -16,13 +16,19 @@
 
 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;
@@ -42,8 +48,6 @@
 
 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.
@@ -76,13 +80,9 @@
      */
     public static boolean checkPermissionManager(@NonNull Context context, int pid,
             int uid, @NonNull String packageName, @Nullable String attributionTag) {
-        if (checkPermissionForDataDelivery(context, MANAGE_EXTERNAL_STORAGE, pid, uid,
+        return checkPermissionForDataDelivery(context, MANAGE_EXTERNAL_STORAGE, pid, uid,
                 packageName, attributionTag,
-                generateAppOpMessage(packageName,sOpDescription.get()))) {
-            return true;
-        }
-        // Fallback to OPSTR_NO_ISOLATED_STORAGE app op.
-        return checkNoIsolatedStorageGranted(context, uid, packageName, attributionTag);
+                generateAppOpMessage(packageName,sOpDescription.get()));
     }
 
     /**
@@ -116,10 +116,34 @@
                 generateAppOpMessage(packageName,sOpDescription.get()));
     }
 
-    public static boolean checkIsLegacyStorageGranted(
-            @NonNull Context context, int uid, String packageName) {
-        return context.getSystemService(AppOpsManager.class)
-                .unsafeCheckOp(OPSTR_LEGACY_STORAGE, uid, packageName) == MODE_ALLOWED;
+    /**
+     * 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 checkPermissionReadAudio(@NonNull Context context, int pid, int uid,
@@ -185,6 +209,18 @@
                 generateAppOpMessage(packageName, sOpDescription.get()));
     }
 
+    public static boolean checkPermissionInstallPackages(@NonNull Context context, int pid, int uid,
+        @NonNull String packageName, @Nullable String attributionTag) {
+        return checkPermissionForDataDelivery(context, INSTALL_PACKAGES, pid,
+                uid, packageName, attributionTag, null);
+    }
+
+    public static boolean checkPermissionAccessMtp(@NonNull Context context, int pid, int uid,
+        @NonNull String packageName, @Nullable String attributionTag) {
+        return checkPermissionForDataDelivery(context, ACCESS_MTP, pid,
+                uid, packageName, attributionTag, null);
+    }
+
     /**
      * Returns {@code true} if the given package has write images or write video app op, which
      * indicates the package is a system gallery.
@@ -199,6 +235,20 @@
                 generateAppOpMessage(packageName, sOpDescription.get()));
     }
 
+    /**
+     * Returns {@code true} if any package for the given uid has request_install_packages app op.
+     */
+    public static boolean checkAppOpRequestInstallPackagesForSharedUid(@NonNull Context context,
+            int uid, @NonNull String[] sharedPackageNames, @Nullable String attributionTag) {
+        for (String packageName : sharedPackageNames) {
+            if (checkAppOp(context, OPSTR_REQUEST_INSTALL_PACKAGES, uid, packageName,
+                    attributionTag, generateAppOpMessage(packageName, sOpDescription.get()))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     @VisibleForTesting
     static boolean checkNoIsolatedStorageGranted(@NonNull Context context, int uid,
             @NonNull String packageName, @Nullable String attributionTag) {
@@ -379,6 +429,7 @@
     private static boolean isAppOpPermission(String permission) {
         switch (permission) {
             case MANAGE_EXTERNAL_STORAGE:
+            case MANAGE_MEDIA:
                 return true;
         }
         return false;
@@ -386,6 +437,7 @@
 
     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/SQLiteQueryBuilder.java b/src/com/android/providers/media/util/SQLiteQueryBuilder.java
index e68cb80..cedd353 100644
--- a/src/com/android/providers/media/util/SQLiteQueryBuilder.java
+++ b/src/com/android/providers/media/util/SQLiteQueryBuilder.java
@@ -45,6 +45,8 @@
 
 import com.android.providers.media.DatabaseHelper;
 
+import com.google.common.base.Strings;
+
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Iterator;
@@ -61,6 +63,9 @@
  * {@link SQLiteDatabase} objects.
  */
 public class SQLiteQueryBuilder {
+
+    public static final String ROWID_COLUMN = "rowid";
+
     private static final String TAG = "SQLiteQueryBuilder";
 
     private static final Pattern sAggregationPattern = Pattern.compile(
@@ -229,6 +234,14 @@
         }
     }
 
+    /** Adds {@code column} to the projection map. */
+    public void allowColumn(String column) {
+        if (mProjectionMap == null) {
+            mProjectionMap = new ArrayMap<>();
+        }
+        mProjectionMap.put(column, column);
+    }
+
     /**
      * Gets the projection map for the query, as last configured by
      * {@link #setProjectionMap(Map)}.
@@ -516,7 +529,7 @@
         if (isStrictGrammar()) {
             enforceStrictGrammar(selection, groupBy, having, sortOrder, limit);
         }
-        if (isStrict()) {
+        if (isStrict() && hasUserWhere(selection)) {
             // Validate the user-supplied selection to detect syntactic anomalies
             // in the selection string that could indicate a SQL injection attempt.
             // The idea is to ensure that the selection clause is a valid SQL expression
@@ -636,7 +649,7 @@
         if (isStrictGrammar()) {
             enforceStrictGrammar(selection, null, null, null, null);
         }
-        if (isStrict()) {
+        if (isStrict() && hasUserWhere(selection)) {
             // Validate the user-supplied selection to detect syntactic anomalies
             // in the selection string that could indicate a SQL injection attempt.
             // The idea is to ensure that the selection clause is a valid SQL expression
@@ -718,7 +731,7 @@
         if (isStrictGrammar()) {
             enforceStrictGrammar(selection, null, null, null, null);
         }
-        if (isStrict()) {
+        if (isStrict() && hasUserWhere(selection)) {
             // Validate the user-supplied selection to detect syntactic anomalies
             // in the selection string that could indicate a SQL injection attempt.
             // The idea is to ensure that the selection clause is a valid SQL expression
@@ -753,14 +766,24 @@
         return com.android.providers.media.util.DatabaseUtils.executeUpdateDelete(db, sql, sqlArgs);
     }
 
+    private static boolean hasUserWhere(@Nullable String selection) {
+        return !Strings.isNullOrEmpty(selection);
+    }
+
     private void enforceStrictColumns(@Nullable String[] projection) {
         Objects.requireNonNull(mProjectionMap, "No projection map defined");
+        if (!isStrictColumns()) {
+            return;
+        }
 
         computeProjection(projection);
     }
 
     private void enforceStrictColumns(@NonNull ContentValues values) {
         Objects.requireNonNull(mProjectionMap, "No projection map defined");
+        if (!isStrictColumns()) {
+            return;
+        }
 
         final ArrayMap<String, Object> rawValues = com.android.providers.media.util.DatabaseUtils
                 .getValues(values);
@@ -1026,7 +1049,10 @@
         if (column != null) {
             return column;
         } else {
-            throw new IllegalArgumentException("Invalid column " + userColumn);
+            if (isStrictColumns()) {
+                throw new IllegalArgumentException("Invalid column " + userColumn);
+            }
+            return userColumn;
         }
     }
 
@@ -1122,7 +1148,13 @@
         }
     }
 
-    private static boolean shouldAppendRowId(ContentValues values) {
-        return !values.containsKey(MediaColumns._ID) && values.containsKey(MediaColumns.DATA);
+    @VisibleForTesting
+    boolean shouldAppendRowId(ContentValues values) {
+        // When no projectionMap provided, don't add the row
+        final boolean hasIdInProjectionMap = mProjectionMap != null && mProjectionMap.containsKey(
+                MediaColumns._ID) && TextUtils.equals(mProjectionMap.get(MediaColumns._ID),
+                MediaColumns._ID);
+        return !values.containsKey(MediaColumns._ID) && values.containsKey(MediaColumns.DATA)
+                && hasIdInProjectionMap;
     }
 }
diff --git a/src/com/android/providers/media/util/UserCache.java b/src/com/android/providers/media/util/UserCache.java
new file mode 100644
index 0000000..4b7dec9
--- /dev/null
+++ b/src/com/android/providers/media/util/UserCache.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.media.util;
+
+import android.content.Context;
+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 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 @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/src/com/android/providers/media/util/XmpInterface.java b/src/com/android/providers/media/util/XmpInterface.java
index 1a316e7..f5ead46 100644
--- a/src/com/android/providers/media/util/XmpInterface.java
+++ b/src/com/android/providers/media/util/XmpInterface.java
@@ -65,7 +65,7 @@
     private static final String NAME_INSTANCE_ID = "InstanceID";
 
     private final LongArray mRedactedRanges = new LongArray();
-    private byte[] mRedactedXmp;
+    private @NonNull byte[] mRedactedXmp;
     private String mFormat;
     private String mDocumentId;
     private String mInstanceId;
@@ -223,7 +223,7 @@
         return mOriginalDocumentId;
     }
 
-    public @Nullable byte[] getRedactedXmp() {
+    public @NonNull byte[] getRedactedXmp() {
         return mRedactedXmp;
     }
 
diff --git a/tests/Android.bp b/tests/Android.bp
index 695793e..463e352 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -1,3 +1,79 @@
+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
@@ -26,9 +102,11 @@
         "main_res",
         "res",
     ],
+
     srcs: [
         ":framework-mediaprovider-sources",
         ":mediaprovider-sources",
+        ":mediaprovider-testutils",
         "src/**/*.java",
     ],
 
@@ -41,12 +119,14 @@
 
     static_libs: [
         "androidx.appcompat_appcompat",
+        "modules-utils-backgroundthread",
         "androidx.core_core",
         "androidx.test.rules",
         "guava",
         "mockito-target",
+        "modules-utils-build",
         "truth-prebuilt",
-        "modules-utils-backgroundthread",
+        "cts-install-lib",
     ],
 
     certificate: "media",
@@ -59,4 +139,18 @@
             "-Xep:MissingFail:ERROR",
         ],
     },
+
+    java_resources: [
+        ":MediaProviderTestAppWithStoragePerms",
+        ":MediaProviderTestAppWithoutPerms",
+        ":MediaProviderTestAppForPermissionActivity",
+        ":LegacyMediaProviderTestApp",
+    ],
+
+    min_sdk_version: "30",
+}
+
+filegroup {
+    name: "mediaprovider-testutils",
+    srcs: ["utils/**/*.java"],
 }
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index b5c556a..199f617 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -2,9 +2,11 @@
 <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" />
     <application android:label="MediaProvider Tests">
         <uses-library android:name="android.test.runner" />
 
diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml
index fe7de6a..1cae732 100644
--- a/tests/AndroidTest.xml
+++ b/tests/AndroidTest.xml
@@ -16,6 +16,10 @@
 <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 664bb78..1541da6 100644
--- a/tests/client/Android.bp
+++ b/tests/client/Android.bp
@@ -11,7 +11,7 @@
     name: "MediaProviderClientTests",
     test_suites: [
         "device-tests",
-        "mts",
+        "mts-mediaprovider",
     ],
     compile_multilib: "both",
 
@@ -19,6 +19,7 @@
 
     srcs: [
         "src/**/*.java",
+        ":mediaprovider-testutils",
     ],
 
     libs: [
diff --git a/tests/client/AndroidManifest.xml b/tests/client/AndroidManifest.xml
index 42175b6..f770559 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">
+    <application android:label="MediaProvider Tests" android:requestRawExternalStorageAccess="true">
         <uses-library android:name="android.test.runner" />
     </application>
 
diff --git a/tests/client/AndroidTest.xml b/tests/client/AndroidTest.xml
index 22ca7d3..8ebd0a5 100644
--- a/tests/client/AndroidTest.xml
+++ b/tests/client/AndroidTest.xml
@@ -18,6 +18,7 @@
         <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
new file mode 100644
index 0000000..aefca34
--- /dev/null
+++ b/tests/client/res/raw/orientation_90.jpg
Binary files differ
diff --git a/tests/client/src/com/android/providers/media/client/ClientPlaylistTest.java b/tests/client/src/com/android/providers/media/client/ClientPlaylistTest.java
index 79efb4c..9aaaae2 100644
--- a/tests/client/src/com/android/providers/media/client/ClientPlaylistTest.java
+++ b/tests/client/src/com/android/providers/media/client/ClientPlaylistTest.java
@@ -16,16 +16,19 @@
 
 package com.android.providers.media.client;
 
+import static android.provider.MediaStore.VOLUME_EXTERNAL;
 import static android.provider.MediaStore.VOLUME_EXTERNAL_PRIMARY;
 import static android.provider.MediaStore.VOLUME_INTERNAL;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
+import android.database.ContentObserver;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
@@ -52,6 +55,8 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Verify typical behaviors of {@link MediaStore.Audio.Playlists} from an
@@ -117,82 +122,127 @@
         mValues.put(MediaColumns.DISPLAY_NAME, "Playlist " + System.nanoTime());
         mValues.put(MediaColumns.MIME_TYPE, mMimeType);
 
-        final Uri playlist = mContentResolver.insert(mExternalPlaylists, mValues);
-        final Uri members = MediaStore.Audio.Playlists.Members
-                .getContentUri(VOLUME_EXTERNAL_PRIMARY, ContentUris.parseId(playlist));
+        final Uri playlistUri = mContentResolver.insert(mExternalPlaylists, mValues);
+        final Uri externalVolumePlaylistUri = getExternalVolumePlaylistUri(
+                ContentUris.parseId(playlistUri));
+
+        final TestContentObserverHelper obs = TestContentObserverHelper.create(
+                Arrays.asList(playlistUri, externalVolumePlaylistUri),
+                ContentResolver.NOTIFY_INSERT);
+        final Uri membersUri = MediaStore.Audio.Playlists.Members
+                .getContentUri(VOLUME_EXTERNAL_PRIMARY, ContentUris.parseId(playlistUri));
 
         // Inserting without ordering will always append
         mValues.clear();
         mValues.put(Playlists.Members.AUDIO_ID, mRed);
-        mContentResolver.insert(members, mValues);
+        Uri resultUri = mContentResolver.insert(membersUri, mValues);
+        obs.waitForChange();
+
         mValues.put(Playlists.Members.AUDIO_ID, mGreen);
-        mContentResolver.insert(members, mValues);
+        resultUri = mContentResolver.insert(membersUri, mValues);
+        obs.waitForChange();
         assertMembers(Arrays.asList(
                 Pair.create(mRed, 1),
-                Pair.create(mGreen, 2)), queryMembers(members));
+                Pair.create(mGreen, 2)), queryMembers(membersUri));
 
         // Inserting with ordering should be injected
         mValues.clear();
         mValues.put(Playlists.Members.AUDIO_ID, mBlue);
         mValues.put(Playlists.Members.PLAY_ORDER, 1);
-        mContentResolver.insert(members, mValues);
+        resultUri = mContentResolver.insert(membersUri, mValues);
+        obs.waitForChange();
         assertMembers(Arrays.asList(
                 Pair.create(mBlue, 1),
                 Pair.create(mRed, 2),
-                Pair.create(mGreen, 3)), queryMembers(members));
+                Pair.create(mGreen, 3)), queryMembers(membersUri));
+
+        obs.unregister();
     }
 
     @Test
     public void testMove() throws Exception {
         final long playlistId = createPlaylist(mRed, mGreen, mBlue);
-        final Uri members = Playlists.Members.getContentUri(VOLUME_EXTERNAL_PRIMARY, playlistId);
+        Uri playlistUri = ContentUris.withAppendedId(
+                MediaStore.Audio.Playlists.getContentUri(VOLUME_EXTERNAL), playlistId);
+        Uri externalVolumePlaylistUri = getExternalVolumePlaylistUri(playlistId);
+        final Uri membersUri = Playlists.Members.getContentUri(VOLUME_EXTERNAL_PRIMARY, playlistId);
+
+        TestContentObserverHelper obs = TestContentObserverHelper.create(
+                Arrays.asList(playlistUri, externalVolumePlaylistUri),
+                ContentResolver.NOTIFY_UPDATE);
+
 
         // Simple move forwards
-        Playlists.Members.moveItem(mContentResolver, playlistId, 0, 2);
+        boolean result = Playlists.Members.moveItem(mContentResolver, playlistId, 0, 2);
+        obs.waitForChange();
+        assertTrue(result);
         assertMembers(Arrays.asList(
                 Pair.create(mGreen, 1),
                 Pair.create(mBlue, 2),
-                Pair.create(mRed, 3)), queryMembers(members));
+                Pair.create(mRed, 3)), queryMembers(membersUri));
 
         // Simple move backwards
-        Playlists.Members.moveItem(mContentResolver, playlistId, 2, 0);
+        result = Playlists.Members.moveItem(mContentResolver, playlistId, 2, 0);
+        obs.waitForChange();
+        assertTrue(result);
         assertMembers(Arrays.asList(
                 Pair.create(mRed, 1),
                 Pair.create(mGreen, 2),
-                Pair.create(mBlue, 3)), queryMembers(members));
+                Pair.create(mBlue, 3)), queryMembers(membersUri));
 
         // Advanced moves using query args
         mValues.clear();
         mValues.put(Playlists.Members.PLAY_ORDER, 1);
-        mContentResolver.update(members, mValues, Playlists.Members.PLAY_ORDER + "=?",
-                new String[] { "2" });
+        int count = mContentResolver.update(membersUri, mValues,
+                Playlists.Members.PLAY_ORDER + "=?", new String[] { "2" });
+        obs.waitForChange();
+        assertEquals(1, count);
         assertMembers(Arrays.asList(
                 Pair.create(mGreen, 1),
                 Pair.create(mRed, 2),
-                Pair.create(mBlue, 3)), queryMembers(members));
+                Pair.create(mBlue, 3)), queryMembers(membersUri));
 
-        mContentResolver.update(members, mValues, Playlists.Members.PLAY_ORDER + "=2", null);
+
+        count = mContentResolver.update(membersUri, mValues,
+                Playlists.Members.PLAY_ORDER + "=2", null);
+        obs.waitForChange();
+        assertEquals(1, count);
         assertMembers(Arrays.asList(
                 Pair.create(mRed, 1),
                 Pair.create(mGreen, 2),
-                Pair.create(mBlue, 3)), queryMembers(members));
+                Pair.create(mBlue, 3)), queryMembers(membersUri));
+
+        obs.unregister();
     }
 
     @Test
     public void testRemove() throws Exception {
         final long playlistId = createPlaylist(mRed, mGreen, mBlue);
-        final Uri members = Playlists.Members.getContentUri(VOLUME_EXTERNAL_PRIMARY, playlistId);
+        final Uri membersUri = Playlists.Members.getContentUri(VOLUME_EXTERNAL_PRIMARY, playlistId);
+
+        final Uri playlistUri = ContentUris.withAppendedId(
+                MediaStore.Audio.Playlists.getContentUri(VOLUME_EXTERNAL_PRIMARY), playlistId);
+        final Uri externalVolumePlaylistUri = getExternalVolumePlaylistUri(playlistId);
+        final TestContentObserverHelper obs = TestContentObserverHelper.create(
+                Arrays.asList(playlistUri, externalVolumePlaylistUri),
+                ContentResolver.NOTIFY_DELETE);
 
         // Simple delete in middle, duplicates are okay
-        mContentResolver.delete(members, Playlists.Members.PLAY_ORDER + "=?",
+        int count = mContentResolver.delete(membersUri, Playlists.Members.PLAY_ORDER + "=?",
                 new String[] { "2" });
+        obs.waitForChange();
+        assertEquals(count, 1);
         assertMembers(Arrays.asList(
                 Pair.create(mRed, 1),
-                Pair.create(mBlue, 2)), queryMembers(members));
+                Pair.create(mBlue, 2)), queryMembers(membersUri));
 
-        mContentResolver.delete(members, Playlists.Members.PLAY_ORDER + "=2", null);
+        count = mContentResolver.delete(membersUri, Playlists.Members.PLAY_ORDER + "=2", null);
+        obs.waitForChange();
+        assertEquals(count, 1);
         assertMembers(Arrays.asList(
-                Pair.create(mRed, 1)), queryMembers(members));
+                Pair.create(mRed, 1)), queryMembers(membersUri));
+
+        obs.unregister();
     }
 
     /**
@@ -245,8 +295,15 @@
         mValues.clear();
         mValues.put(MediaColumns.DISPLAY_NAME, "Playlist " + System.nanoTime());
         mValues.put(MediaColumns.MIME_TYPE, mMimeType);
+        final Uri externalVolumePlaylistUri = MediaStore.Audio.Playlists
+                .getContentUri(VOLUME_EXTERNAL_PRIMARY);
+        final TestContentObserverHelper obs = TestContentObserverHelper.create(
+                Arrays.asList(mExternalPlaylists, externalVolumePlaylistUri),
+                ContentResolver.NOTIFY_INSERT);
 
         final Uri playlist = mContentResolver.insert(mExternalPlaylists, mValues);
+        obs.waitForChange();
+        obs.unregister();
         final Uri members = MediaStore.Audio.Playlists.Members
                 .getContentUri(VOLUME_EXTERNAL_PRIMARY, ContentUris.parseId(playlist));
 
@@ -263,6 +320,11 @@
         return ContentUris.parseId(playlist);
     }
 
+    private static Uri getExternalVolumePlaylistUri(long playlistId) {
+        return ContentUris.withAppendedId(
+            MediaStore.Audio.Playlists.getContentUri(VOLUME_EXTERNAL), playlistId);
+    }
+
     private void assertMembers(List<Pair<Long, Integer>> expected,
             List<Pair<Long, Integer>> actual) {
         assertEquals(expected.toString(), actual.toString());
@@ -283,4 +345,81 @@
         }
         return res;
     }
+
+    /**
+     * Observer that will wait for a specific change event to be delivered on all the given uris.
+     */
+    private static class TestContentObserverHelper {
+        private List<TestContentObserver> observers;
+
+        private TestContentObserverHelper(List<TestContentObserver> observers) {
+            this.observers = observers;
+        }
+
+        private static TestContentObserverHelper create(List<Uri> uris, int flags) {
+            List<TestContentObserver> observers = new ArrayList();
+            for (Uri uri : uris) {
+                final TestContentObserver observer = TestContentObserver.create(uri, flags);
+                observers.add(observer);
+            }
+            final TestContentObserverHelper obsWrapper = new TestContentObserverHelper(observers);
+            return obsWrapper;
+        }
+
+        private void waitForChange() {
+            for (TestContentObserver observer : observers) {
+                observer.waitForChange();
+            }
+        }
+
+        private void unregister() {
+            for (TestContentObserver observer : observers) {
+                observer.unregister();
+            }
+        }
+    }
+
+    /**
+     * Observer that will wait for a specific change event to be delivered.
+     */
+    public static class TestContentObserver extends ContentObserver {
+        private final int flags;
+
+        private CountDownLatch latch = new CountDownLatch(1);
+
+        private TestContentObserver(int flags) {
+            super(null);
+            this.flags = flags;
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri, int flags) {
+            Log.v(TAG, String.format("onChange(%b, %s, %d)", selfChange, uri.toString(), flags));
+
+            if (flags == this.flags) {
+                latch.countDown();
+            }
+        }
+
+        public static TestContentObserver create(Uri uri, int flags) {
+            final TestContentObserver obs = new TestContentObserver(flags);
+            InstrumentationRegistry.getContext().getContentResolver()
+                    .registerContentObserver(uri, true, obs);
+            return obs;
+        }
+
+        public void waitForChange() {
+            try {
+                assertTrue(latch.await(5, TimeUnit.SECONDS));
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+            latch = new CountDownLatch(1);
+        }
+
+        public void unregister() {
+            InstrumentationRegistry.getContext().getContentResolver()
+                .unregisterContentObserver(this);
+        }
+    }
 }
diff --git a/tests/client/src/com/android/providers/media/client/DownloadProviderTest.java b/tests/client/src/com/android/providers/media/client/DownloadProviderTest.java
new file mode 100644
index 0000000..b25e8df
--- /dev/null
+++ b/tests/client/src/com/android/providers/media/client/DownloadProviderTest.java
@@ -0,0 +1,157 @@
+/*
+ * 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.client;
+
+import static com.android.providers.media.client.PublicVolumeSetupHelper.createNewPublicVolume;
+import static com.android.providers.media.client.PublicVolumeSetupHelper.deletePublicVolumes;
+import static com.android.providers.media.client.PublicVolumeSetupHelper.executeShellCommand;
+import static com.android.providers.media.client.PublicVolumeSetupHelper.pollForCondition;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.storage.StorageManager;
+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.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Verify DownloadProvider's access to app's private files on primary and public volumes.
+ * DownloadProvider and Media Provider client tests use the same shared UID
+ * `android:sharedUserId="android.media"` which helps us test DownloadProvider's behavior here
+ */
+@RunWith(AndroidJUnit4.class)
+public class DownloadProviderTest {
+
+    private static final String TAG = "DownloadProviderTest";
+    private static final  String OTHER_PKG_NAME = "com.example.foo";
+
+    @BeforeClass
+    public static void setUp() throws Exception {
+        createNewPublicVolume();
+    }
+
+    @AfterClass
+    public static void tearDown() throws Exception {
+        deletePublicVolumes();
+    }
+
+
+    @Test
+    public void testCanReadWriteOtherAppPrivateFiles() throws Exception {
+        List<File> otherPackageDirs = createOtherPackageExternalFilesDir();
+        List<File> otherPackagePrivateFiles = createOtherPackagePrivateFile(otherPackageDirs);
+        for (File privateFile: otherPackagePrivateFiles) {
+            assertTrue(canOpenForWrite(privateFile));
+        }
+        deleteOtherPackageExternalFiles(otherPackageDirs);
+    }
+
+    @Test
+    public void testCanOpenOtherAppPrivateDir() throws Exception {
+        List<File> otherPackageDirs = createOtherPackageExternalFilesDir();
+        for (File privateDir: otherPackageDirs) {
+            String[] dirContents = privateDir.list();
+            assertThat(dirContents).asList().containsExactly("files");
+        }
+        deleteOtherPackageExternalFiles(otherPackageDirs);
+    }
+
+    private List<File> createOtherPackageExternalFilesDir() throws Exception {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        Set<String> volNames = MediaStore.getExternalVolumeNames(context);
+        List<File> otherPackageDirs = new ArrayList();
+
+        for (String volName : volNames) {
+            File volPath = getVolumePath(context, volName);
+            // List of private external package files for other package on the same volume
+            List<String> otherPackageDirsOnSameVolume = new ArrayList();
+            final String otherPackageDataDir = volPath.getAbsolutePath() + "/Android/data/"
+                    + OTHER_PKG_NAME;
+            otherPackageDirsOnSameVolume.add(otherPackageDataDir);
+            final String otherPackageObbDir = volPath.getAbsolutePath() + "/Android/obb/"
+                    + OTHER_PKG_NAME;
+            otherPackageDirsOnSameVolume.add(otherPackageObbDir);
+
+            for (String dir: otherPackageDirsOnSameVolume) {
+                otherPackageDirs.add(new File(dir));
+                final String otherPackageExternalFilesDir = dir + "/files";
+                executeShellCommand("mkdir -p " + otherPackageExternalFilesDir + " -m 2770");
+                // Need to wait for the directory to be created, as the rest of the test depends on
+                // the dir to be created. A race condition can cause the test to be flaky.
+                pollForDirectoryToBeCreated(new File(otherPackageExternalFilesDir));
+            }
+        }
+        return otherPackageDirs;
+    }
+
+    private List<File> createOtherPackagePrivateFile(List<File> otherPackageDirs) throws Exception {
+        List<File> otherPackagePrivateFiles = new ArrayList();
+        for (File otherPackageDir : otherPackageDirs) {
+            final String otherPackagePrivateFile = otherPackageDir + "/files/test.txt";
+            otherPackagePrivateFiles.add(new File(otherPackagePrivateFile));
+            executeShellCommand("touch " + otherPackagePrivateFile);
+        }
+        return otherPackagePrivateFiles;
+    }
+
+    private void deleteOtherPackageExternalFiles(List<File> otherPackageDirs) throws Exception {
+        for (File dir: otherPackageDirs) {
+            executeShellCommand("rm -r " + dir.getAbsolutePath());
+        }
+    }
+
+    /**
+     * Returns whether we can open the file.
+     */
+    private static boolean canOpenForWrite(File file) {
+        try (FileOutputStream fis = new FileOutputStream(file)) {
+            return true;
+        } catch (IOException expected) {
+            return false;
+        }
+    }
+
+    private static File getVolumePath(Context context, String volumeName) {
+        return context.getSystemService(StorageManager.class)
+            .getStorageVolume(MediaStore.Files.getContentUri(volumeName)).getDirectory();
+    }
+
+    /**
+     * Polls for directory to be created
+     */
+    private static void pollForDirectoryToBeCreated(File dir) throws Exception {
+        pollForCondition(
+            () -> dir.exists(),
+            "Timed out while waiting for dir " + dir + " to be created");
+    }
+}
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 1a94ef1..dde3911 100644
--- a/tests/client/src/com/android/providers/media/client/LegacyProviderMigrationTest.java
+++ b/tests/client/src/com/android/providers/media/client/LegacyProviderMigrationTest.java
@@ -18,7 +18,11 @@
 
 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;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -26,6 +30,7 @@
 import android.content.ContentProviderClient;
 import android.content.ContentProviderOperation;
 import android.content.ContentResolver;
+import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.pm.ProviderInfo;
@@ -33,6 +38,7 @@
 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;
@@ -41,6 +47,7 @@
 import android.provider.MediaStore.Audio.AudioColumns;
 import android.provider.MediaStore.DownloadColumns;
 import android.provider.MediaStore.Files.FileColumns;
+import android.provider.MediaStore.Images.ImageColumns;
 import android.provider.MediaStore.MediaColumns;
 import android.provider.MediaStore.Video.VideoColumns;
 import android.system.ErrnoException;
@@ -63,10 +70,12 @@
 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;
@@ -98,6 +107,7 @@
     private Uri mExternalVideo;
     private Uri mExternalImages;
     private Uri mExternalDownloads;
+    private Uri mExternalPlaylists;
 
     @Before
     public void setUp() throws Exception {
@@ -106,17 +116,31 @@
         mExternalVideo = MediaStore.Video.Media.getContentUri(mVolumeName);
         mExternalImages = MediaStore.Images.Media.getContentUri(mVolumeName);
         mExternalDownloads = MediaStore.Downloads.getContentUri(mVolumeName);
+
+        Uri playlists = MediaStore.Audio.Playlists.getContentUri(mVolumeName);
+        mExternalPlaylists = playlists.buildUpon()
+                .appendQueryParameter("silent", "true").build();
     }
 
-    private ContentValues generateValues(int mediaType, String mimeType, String dirName) {
+    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 {
         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);
-        final File file = new File(subDir, "legacy" + System.nanoTime() + "."
+        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());
@@ -129,6 +153,25 @@
         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,
@@ -177,6 +220,7 @@
         values.put(VideoColumns.BOOKMARK, String.valueOf(42));
         values.put(VideoColumns.TAGS, "My Tags");
         values.put(VideoColumns.CATEGORY, "My Category");
+        values.put(VideoColumns.IS_PRIVATE, String.valueOf(1));
         doLegacy(mExternalVideo, values);
     }
 
@@ -184,6 +228,7 @@
     public void testLegacy_Image() throws Exception {
         final ContentValues values = generateValues(FileColumns.MEDIA_TYPE_IMAGE,
                 "image/png", Environment.DIRECTORY_PICTURES);
+        values.put(ImageColumns.IS_PRIVATE, String.valueOf(1));
         doLegacy(mExternalImages, values);
     }
 
@@ -197,14 +242,154 @@
     }
 
     /**
-     * Verify that a legacy database with thousands of media entries can be
-     * successfully migrated.
+     * Test that migrating from legacy database with volume_name=NULL doesn't
+     * result in empty cursor when queried.
      */
     @Test
-    public void testLegacy_Extreme() throws Exception {
+    public void testMigrateNullVolumeName() throws Exception {
+        final ContentValues values = generateValues(FileColumns.MEDIA_TYPE_IMAGE,
+                "image/png", Environment.DIRECTORY_PICTURES);
+        values.remove(MediaColumns.VOLUME_NAME);
+        doLegacy(mExternalImages, values);
+    }
+
+    @Test
+    public void testLegacy_PlaylistMap() throws Exception {
         final Context context = InstrumentationRegistry.getTargetContext();
         final UiAutomation ui = InstrumentationRegistry.getInstrumentation().getUiAutomation();
 
+        final ContentValues audios[] = new ContentValues[] {
+                generateValues(FileColumns.MEDIA_TYPE_AUDIO, "audio/mpeg",
+                        Environment.DIRECTORY_MUSIC),
+                generateValues(FileColumns.MEDIA_TYPE_AUDIO, "audio/mpeg",
+                        Environment.DIRECTORY_MUSIC),
+        };
+
+        final String playlistMimeType = "audio/mpegurl";
+        final ContentValues playlist = generateValues(FileColumns.MEDIA_TYPE_PLAYLIST,
+                playlistMimeType, "Playlists");
+        final String playlistName = "LegacyPlaylistName_" + System.nanoTime();
+        playlist.put(MediaStore.Audio.PlaylistsColumns.NAME, playlistName);
+        File playlistFile = new File(playlist.getAsString(MediaColumns.DATA));
+
+        playlistFile.delete();
+
+        final ContentValues playlistMap = new ContentValues();
+        playlistMap.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, 1);
+
+        prepareProviders(context, ui);
+
+        try (ContentProviderClient legacy = context.getContentResolver()
+                .acquireContentProviderClient(MediaStore.AUTHORITY_LEGACY)) {
+
+            // Step 1: Insert the playlist entry into the playlists table.
+            final Uri playlistUri = rewriteToLegacy(legacy.insert(
+                    rewriteToLegacy(mExternalPlaylists), playlist));
+            long playlistId = ContentUris.parseId(playlistUri);
+            final Uri playlistMemberUri = MediaStore.rewriteToLegacy(
+                    MediaStore.Audio.Playlists.Members.getContentUri(mVolumeName, playlistId)
+                            .buildUpon()
+                            .appendQueryParameter("silent", "true").build());
+
+
+            for (ContentValues values : audios) {
+                // Step 2: Write the audio file to the legacy mediastore.
+                final Uri audioUri =
+                        rewriteToLegacy(legacy.insert(rewriteToLegacy(mExternalAudio), values));
+                // Remember our ID to check it later
+                values.put(MediaColumns._ID, audioUri.getLastPathSegment());
+
+
+                long audioId = ContentUris.parseId(audioUri);
+                playlistMap.put(MediaStore.Audio.Playlists.Members.PLAYLIST_ID, playlistId);
+                playlistMap.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, audioId);
+
+                // Step 3: Add a mapping to playlist members.
+                legacy.insert(playlistMemberUri, playlistMap);
+            }
+
+            // Insert a stale row, We only have 3 items in the database. #4 is a stale row
+            // and will be skipped from the playlist during the migration.
+            playlistMap.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, 4);
+            legacy.insert(playlistMemberUri, playlistMap);
+
+        }
+
+        // This will delete MediaProvider data and restarts MediaProvider, and mounts storage.
+        clearProviders(context, ui);
+
+        // Verify scan on DEMAND doesn't delete any virtual playlist files.
+        MediaStore.scanFile(context.getContentResolver(),
+                Environment.getExternalStorageDirectory());
+
+        // Playlist files are created from playlist NAME
+        final File musicDir = new File(context.getSystemService(StorageManager.class)
+                .getStorageVolume(MediaStore.Files.getContentUri(mVolumeName)).getDirectory(),
+                Environment.DIRECTORY_MUSIC);
+        playlistFile = new File(musicDir, playlistName + "."
+                + MimeTypeMap.getSingleton().getExtensionFromMimeType(playlistMimeType));
+        // Wait for scan on MEDIA_MOUNTED to create "real" playlist files.
+        pollForFile(playlistFile);
+
+        // Scan again to verify updated playlist metadata
+        MediaStore.scanFile(context.getContentResolver(), playlistFile);
+
+        try (ContentProviderClient modern = context.getContentResolver()
+                .acquireContentProviderClient(MediaStore.AUTHORITY)) {
+            long legacyPlaylistId =
+                    playlistMap.getAsLong(MediaStore.Audio.Playlists.Members.PLAYLIST_ID);
+            long legacyAudioId1 = audios[0].getAsLong(MediaColumns._ID);
+            long legacyAudioId2 = audios[1].getAsLong(MediaColumns._ID);
+
+            // Verify that playlist_id matches with legacy playlist_id
+            {
+                Uri playlists = MediaStore.Audio.Playlists.getContentUri(mVolumeName);
+                final String[] project = {FileColumns._ID, MediaStore.Audio.PlaylistsColumns.NAME};
+
+                try (Cursor cursor = modern.query(playlists, project, null, null, null)) {
+                    boolean found = false;
+                    while(cursor.moveToNext()) {
+                        if (cursor.getLong(0) == legacyPlaylistId) {
+                            found = true;
+                            assertEquals(playlistName, cursor.getString(1));
+                            break;
+                        }
+                    }
+                    assertTrue(found);
+                }
+            }
+
+            // Verify that playlist_members map matches legacy playlist_members map.
+            {
+                 Uri members = MediaStore.Audio.Playlists.Members.getContentUri(
+                        mVolumeName, legacyPlaylistId);
+                 final String[] project = { MediaStore.Audio.Playlists.Members.AUDIO_ID };
+
+                 try (Cursor cursor = modern.query(members, project, null, null,
+                         MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER)) {
+                     assertTrue(cursor.moveToNext());
+                     assertEquals(legacyAudioId1, cursor.getLong(0));
+                     assertTrue(cursor.moveToNext());
+                     assertEquals(legacyAudioId2, cursor.getLong(0));
+                     assertFalse(cursor.moveToNext());
+                 }
+            }
+
+            // Verify that migrated playlist audio_id refers to legacy audio file.
+            {
+                Uri modernAudioUri = ContentUris.withAppendedId(
+                        MediaStore.Audio.Media.getContentUri(mVolumeName), legacyAudioId1);
+                final String[] project = {FileColumns.DATA};
+
+                try (Cursor cursor = modern.query(modernAudioUri, project, null, null, null)) {
+                    assertTrue(cursor.moveToFirst());
+                    assertEquals(audios[0].getAsString(MediaColumns.DATA), cursor.getString(0));
+                }
+            }
+        }
+    }
+
+    private static void prepareProviders(Context context, UiAutomation ui) throws Exception {
         final ProviderInfo legacyProvider = context.getPackageManager()
                 .resolveContentProvider(MediaStore.AUTHORITY_LEGACY, 0);
         final ProviderInfo modernProvider = context.getPackageManager()
@@ -219,6 +404,30 @@
         executeShellCommand("sync", ui);
         executeShellCommand("pm clear " + legacyProvider.applicationInfo.packageName, ui);
         waitForMountedAndIdle(context.getContentResolver());
+    }
+
+    private static void clearProviders(Context context, UiAutomation ui) throws Exception {
+        final ProviderInfo modernProvider = context.getPackageManager()
+                .resolveContentProvider(MediaStore.AUTHORITY, 0);
+
+        // Clear data on the modern provider so that the initial scan recovers
+        // metadata from the legacy provider
+        waitForMountedAndIdle(context.getContentResolver());
+        executeShellCommand("sync", ui);
+        executeShellCommand("pm clear " + modernProvider.applicationInfo.packageName, ui);
+        waitForMountedAndIdle(context.getContentResolver());
+    }
+
+    /**
+     * Verify that a legacy database with thousands of media entries can be
+     * successfully migrated.
+     */
+    @Test
+    public void testLegacy_Extreme() throws Exception {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        final UiAutomation ui = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+
+        prepareProviders(context, ui);
 
         // Create thousands of items in the legacy provider
         try (ContentProviderClient legacy = context.getContentResolver()
@@ -243,12 +452,7 @@
             }
         }
 
-        // Clear data on the modern provider so that the initial scan recovers
-        // metadata from the legacy provider
-        waitForMountedAndIdle(context.getContentResolver());
-        executeShellCommand("sync", ui);
-        executeShellCommand("pm clear " + modernProvider.applicationInfo.packageName, ui);
-        waitForMountedAndIdle(context.getContentResolver());
+        clearProviders(context, ui);
 
         // Confirm that details from legacy provider have migrated
         try (ContentProviderClient modern = context.getContentResolver()
@@ -263,20 +467,7 @@
         final Context context = InstrumentationRegistry.getTargetContext();
         final UiAutomation ui = InstrumentationRegistry.getInstrumentation().getUiAutomation();
 
-        final ProviderInfo legacyProvider = context.getPackageManager()
-                .resolveContentProvider(MediaStore.AUTHORITY_LEGACY, 0);
-        final ProviderInfo modernProvider = context.getPackageManager()
-                .resolveContentProvider(MediaStore.AUTHORITY, 0);
-
-        // Only continue if we have both providers to test against
-        Assume.assumeNotNull(legacyProvider);
-        Assume.assumeNotNull(modernProvider);
-
-        // Clear data on the legacy provider so that we create a database
-        waitForMountedAndIdle(context.getContentResolver());
-        executeShellCommand("sync", ui);
-        executeShellCommand("pm clear " + legacyProvider.applicationInfo.packageName, ui);
-        waitForMountedAndIdle(context.getContentResolver());
+        prepareProviders(context, ui);
 
         // Create a well-known entry in legacy provider, and write data into
         // place to ensure the file is created on disk
@@ -298,17 +489,31 @@
             values.remove(FileColumns.DATA);
         }
 
-        // Clear data on the modern provider so that the initial scan recovers
-        // metadata from the legacy provider
-        waitForMountedAndIdle(context.getContentResolver());
-        executeShellCommand("sync", ui);
-        executeShellCommand("pm clear " + modernProvider.applicationInfo.packageName, ui);
-        waitForMountedAndIdle(context.getContentResolver());
+        // 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)) {
@@ -321,13 +526,14 @@
             extras.putInt(MediaStore.QUERY_ARG_MATCH_TRASHED, MediaStore.MATCH_INCLUDE);
             extras.putInt(MediaStore.QUERY_ARG_MATCH_FAVORITE, MediaStore.MATCH_INCLUDE);
 
-            try (Cursor cursor = modern.query(collectionUri, null, extras, null)) {
+            try (Cursor cursor = pollForCursor(modern, collectionUri, extras)) {
+                assertNotNull(cursor);
                 assertTrue(cursor.moveToFirst());
-                final ContentValues actualValues = new ContentValues();
                 for (String key : values.keySet()) {
-                    actualValues.put(key, cursor.getString(cursor.getColumnIndexOrThrow(key)));
+                    assertWithMessage("Checking key %s", key)
+                            .that(cursor.getString(cursor.getColumnIndexOrThrow(key)))
+                            .isEqualTo(values.get(key));
                 }
-                assertEquals(values, actualValues);
             }
         }
     }
@@ -343,6 +549,33 @@
         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;
+
+            Log.v(TAG, "Waiting for..." + file);
+            SystemClock.sleep(POLLING_SLEEP_MILLIS);
+        }
+        fail("Timed out while waiting for file " + file);
+    }
+
     private static void pollForExternalStorageState() {
         final File target = Environment.getExternalStorageDirectory();
         for (int i = 0; i < POLLING_TIMEOUT_MILLIS / POLLING_SLEEP_MILLIS; i++) {
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 eff8e67..0088514 100644
--- a/tests/client/src/com/android/providers/media/client/PerformanceTest.java
+++ b/tests/client/src/com/android/providers/media/client/PerformanceTest.java
@@ -16,6 +16,8 @@
 
 package com.android.providers.media.client;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
@@ -33,13 +35,20 @@
 import android.util.Log;
 
 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;
 import java.util.HashSet;
 import java.util.List;
@@ -50,8 +59,12 @@
 /**
  * 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";
 
@@ -75,19 +88,9 @@
             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;
-        assertTrue(timers.actionInsert.getAverageDurationMillis() < actionDeadline);
-        assertTrue(timers.actionUpdate.getAverageDurationMillis() < actionDeadline);
-        assertTrue(timers.actionDelete.getAverageDurationMillis() < actionDeadline);
-
-        // Verify that external notifications finished within 30ms deadline
-        final long notifyDeadline = 30;
-        assertTrue(timers.notifyInsert.getAverageDurationMillis() < notifyDeadline);
-        assertTrue(timers.notifyUpdate.getAverageDurationMillis() < notifyDeadline);
-        assertTrue(timers.notifyDelete.getAverageDurationMillis() < notifyDeadline);
     }
 
     private void doSingle(Uri collection, Timers timers) throws Exception {
@@ -154,19 +157,9 @@
             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;
-        assertTrue(timers.actionInsert.getAverageDurationMillis() < actionDeadline);
-        assertTrue(timers.actionUpdate.getAverageDurationMillis() < actionDeadline);
-        assertTrue(timers.actionDelete.getAverageDurationMillis() < actionDeadline);
-
-        // Verify that external notifications finished within 100ms deadline
-        final long notifyDeadline = 100;
-        assertTrue(timers.notifyInsert.getAverageDurationMillis() < notifyDeadline);
-        assertTrue(timers.notifyUpdate.getAverageDurationMillis() < notifyDeadline);
-        assertTrue(timers.notifyDelete.getAverageDurationMillis() < notifyDeadline);
     }
 
     private void doBulk(Uri collection, Timers timers) throws Exception {
@@ -238,32 +231,55 @@
 
     @Test
     public void testDirOperations_10() throws Exception {
-        Timer createTimer = new Timer("mkdir");
-        Timer readTimer = new Timer("readdir");
-        Timer deleteTimer = new Timer("rmdir");
-        for (int i = 0; i < COUNT_REPEAT; i++ ){
-            doDirOperations(10, createTimer, readTimer, deleteTimer);
-        }
-        createTimer.dumpResults();
-        readTimer.dumpResults();
-        deleteTimer.dumpResults();
+        testDirOperations_size(10);
     }
 
     @Test
     public void testDirOperations_100() throws Exception {
+        testDirOperations_size(100);
+    }
+
+    @Test
+    public void testDirOperations_500() throws Exception {
+        testDirOperations_size(500);
+    }
+
+    @Test
+    public void testDirOperations_1000() throws Exception {
+        testDirOperations_size(1000);
+    }
+
+    private void testDirOperations_size(int size) throws Exception {
         Timer createTimer = new Timer("mkdir");
-        Timer readTimer = new Timer("readdir");
+        Timer readdirTimer = new Timer("readdir");
+        Timer isFileTimer = new Timer("isFile");
+        // 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
+        // DB entries for all the files within it. (it takes ~10ms for a dir of 1000 files)
+        // 2. Renaming files is faster as well (for file managers), as we do not do DB operations
+        // on each rename.
+        Timer renameDirTimer = new Timer("renamedir");
+        Timer renameFilesTimer = new Timer("renamefiles");
         Timer deleteTimer = new Timer("rmdir");
-        for (int i = 0; i < COUNT_REPEAT; i++ ){
-            doDirOperations(100, createTimer, readTimer, deleteTimer);
+        for (int i = 0; i < COUNT_REPEAT; i++ ) {
+            doDirOperations(size, createTimer, readdirTimer, isFileTimer,
+                    renameDirTimer, renameFilesTimer, deleteTimer);
         }
+
+        // The numbers dumped by the timers are monitored using crystalball and regressions are
+        // reported from there.
         createTimer.dumpResults();
-        readTimer.dumpResults();
+        readdirTimer.dumpResults();
+        isFileTimer.dumpResults();
+        renameDirTimer.dumpResults();
+        renameFilesTimer.dumpResults();
         deleteTimer.dumpResults();
     }
 
-    private void doDirOperations(int size, Timer createTimer, Timer readTimer, Timer deleteTimer)
-            throws Exception {
+    private void doDirOperations(int size, Timer createTimer, Timer readdirTimer,
+            Timer isFileTimer, Timer renameDirTimer, Timer renameFilesTimer,
+            Timer deleteTimer) throws Exception {
         createTimer.start();
         File testDir = new File(new File(Environment.getExternalStorageDirectory(),
                 "Download"), "test_dir_" + System.nanoTime());
@@ -271,23 +287,54 @@
         List<File> files = new ArrayList<>();
         for (int i = 0; i < size; i++) {
             File file = new File(testDir, "file_" + System.nanoTime());
-            assertTrue(file.createNewFile());
+            assertThat(file.createNewFile()).isTrue();
             files.add(file);
         }
         createTimer.stop();
 
+        File renamedTestDir = new File(new File(Environment.getExternalStorageDirectory(),
+                "Download"), "renamed_test_dir_" + System.nanoTime());
         try {
-            readTimer.start();
+            readdirTimer.start();
             File[] result = testDir.listFiles();
-            readTimer.stop();
-            assertEquals(size, result.length);
+            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();
+
+            renameDirTimer.start();
+            assertThat(testDir.renameTo(renamedTestDir)).isTrue();
+            renameDirTimer.stop();
+            testDir = renamedTestDir;
+
+            // renameTo for files will fail as the old files are not valid files as the dir name
+            // is changed, update the files to be valid.
+            files = Arrays.asList(renamedTestDir.listFiles());
+
+            renameFilesTimer.start();
+            List<File> renamedFiles = new ArrayList<>();
+            for (File file : files) {
+                File newFile = new File(testDir, "file_" + System.nanoTime());
+                assertThat(file.renameTo(newFile)).isTrue();
+                renamedFiles.add(newFile);
+            }
+            renameFilesTimer.stop();
+            // This is essential for the finally block to delete valid files.
+            files = renamedFiles;
 
         } finally {
             deleteTimer.start();
             for (File file : files) {
-                assertTrue(file.delete());
+                assertThat(file.delete()).isTrue();
             }
-            assertTrue(testDir.delete());
+            assertThat(testDir.delete()).isTrue();
             deleteTimer.stop();
         }
     }
@@ -296,52 +343,6 @@
         return new HashSet<>(uris);
     }
 
-    /**
-     * Timer that can be started/stopped with nanosecond accuracy, and later
-     * averaged based on the number of times it was cycled.
-     */
-    private static class Timer {
-        private final String name;
-        private int count;
-        private long duration;
-        private long start;
-
-        public Timer(String name) {
-            this.name = name;
-        }
-
-        public void start() {
-            if (start != 0) {
-                throw new IllegalStateException();
-            } else {
-                start = SystemClock.elapsedRealtimeNanos();
-            }
-        }
-
-        public void stop() {
-            if (start == 0) {
-                throw new IllegalStateException();
-            } else {
-                duration += (SystemClock.elapsedRealtimeNanos() - start);
-                start = 0;
-                count++;
-            }
-        }
-
-        public long getAverageDurationMillis() {
-            return TimeUnit.MILLISECONDS.convert(duration / count, TimeUnit.NANOSECONDS);
-        }
-
-        public void dumpResults() {
-            final long duration = getAverageDurationMillis();
-            Log.v(TAG, name + ": " + duration + "ms");
-
-            final Bundle results = new Bundle();
-            results.putLong(name, duration);
-            InstrumentationRegistry.getInstrumentation().sendStatus(0, results);
-        }
-    }
-
     private static class Timers {
         public final Timer actionInsert = new Timer("action_insert");
         public final Timer actionUpdate = new Timer("action_update");
@@ -414,4 +415,32 @@
                     .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/PlaylistPerformanceTest.java b/tests/client/src/com/android/providers/media/client/PlaylistPerformanceTest.java
new file mode 100644
index 0000000..e761814
--- /dev/null
+++ b/tests/client/src/com/android/providers/media/client/PlaylistPerformanceTest.java
@@ -0,0 +1,163 @@
+/*
+ * 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.client;
+
+import static android.provider.MediaStore.VOLUME_EXTERNAL_PRIMARY;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.provider.MediaStore;
+import android.provider.MediaStore.Audio.Playlists;
+import android.provider.MediaStore.MediaColumns;
+
+import androidx.annotation.NonNull;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.providers.media.tests.utils.Timer;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+
+@RunWith(AndroidJUnit4.class)
+public class PlaylistPerformanceTest {
+    private static final Uri AUDIO_URI = MediaStore.Audio.Media
+            .getContentUri(VOLUME_EXTERNAL_PRIMARY);
+    private static final Uri PLAYLISTS_URI = Playlists
+            .getContentUri(VOLUME_EXTERNAL_PRIMARY);
+
+    private Context mContext;
+    private ContentResolver mContentResolver;
+
+    private String mRelativePath;
+    private File mTestDir;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mContentResolver = mContext.getContentResolver();
+
+        mRelativePath = Environment.DIRECTORY_MUSIC + "/test_" + System.nanoTime();
+        mTestDir = new File(Environment.getExternalStorageDirectory(), mRelativePath);
+        assertTrue(mTestDir.mkdirs());
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        FileUtils.deleteContentsAndDir(mTestDir);
+    }
+
+    @Test
+    public void testBulkInsertPlaylistMembers() throws Exception {
+        final int expected = 200;
+        final long[] memberIds = new long[expected];
+        for (int i = 0; i < expected; i++) {
+            memberIds[i] = (createAudio(i));
+        }
+
+        final Uri playlistUri = createPlaylist();
+        final Uri membersUri = Playlists.Members
+                .getContentUri(VOLUME_EXTERNAL_PRIMARY, ContentUris.parseId(playlistUri));
+
+        final ContentValues[] valuesArray = createValuesForMembers(memberIds);
+        final Timer addMembers = new Timer("bulk insert " + expected);
+        addMembers.start();
+        final int actual = mContentResolver.bulkInsert(membersUri, valuesArray);
+        addMembers.stop();
+        addMembers.dumpResults();
+        assertEquals(expected, actual);
+    }
+
+    @Test
+    public void testDeletePlaylistMembers() throws Exception {
+        final int expected = 200;
+        final long[] memberIds = new long[expected];
+        for (int i = 0; i < expected; i++) {
+            memberIds[i] = (createAudio(i));
+        }
+
+        final Uri playlistUri = createPlaylist();
+        final Uri membersUri = Playlists.Members
+                .getContentUri(VOLUME_EXTERNAL_PRIMARY, ContentUris.parseId(playlistUri));
+
+        final ContentValues[] valuesArray = createValuesForMembers(memberIds);
+        assertEquals(expected, mContentResolver.bulkInsert(membersUri, valuesArray));
+
+        final Timer deleteMembers = new Timer("delete member " + expected);
+        final String[] whereArgs = new String[] { "0" };
+        int actual = 0;
+        for (int i = 0; i < memberIds.length; i++) {
+            whereArgs[0] = "" + memberIds[i];
+            deleteMembers.start();
+            actual += mContentResolver.delete(membersUri, Playlists.Members.AUDIO_ID + "=?",
+                    whereArgs);
+            deleteMembers.stop();
+        }
+        deleteMembers.dumpResults();
+        assertEquals(expected, actual);
+    }
+
+    private @NonNull Uri createPlaylist() {
+        final ContentValues values = new ContentValues();
+        values.put(MediaColumns.DISPLAY_NAME, "Test_Playlist");
+        values.put(MediaColumns.RELATIVE_PATH, mRelativePath);
+        values.put(MediaColumns.MIME_TYPE, "audio/x-mpegurl");
+
+        final Uri playlistUri = mContentResolver.insert(PLAYLISTS_URI, values);
+        assertNotNull(playlistUri);
+        return playlistUri;
+    }
+
+    private long createAudio(int num) throws IOException {
+        final ContentValues values = new ContentValues();
+        values.put(MediaColumns.DISPLAY_NAME, "Test_Song_" + num);
+        values.put(MediaColumns.RELATIVE_PATH, mRelativePath);
+        values.put(MediaColumns.MIME_TYPE, "audio/mpeg");
+
+        final Uri uri = mContentResolver.insert(AUDIO_URI, values);
+        assertNotNull(uri);
+        try (OutputStream ignore = mContentResolver.openOutputStream(uri)) {
+        }
+        return ContentUris.parseId(uri);
+    }
+
+    private @NonNull ContentValues[] createValuesForMembers(@NonNull long[] memberIds) {
+        final ContentValues[] valuesArray = new ContentValues[memberIds.length];
+        for (int i = 0; i < memberIds.length; i++) {
+            final ContentValues values = new ContentValues();
+            values.put(Playlists.Members.AUDIO_ID, memberIds[i]);
+            valuesArray[i] = values;
+        }
+        return valuesArray;
+    }
+}
+
diff --git a/tests/client/src/com/android/providers/media/client/PublicVolumePlaylistTest.java b/tests/client/src/com/android/providers/media/client/PublicVolumePlaylistTest.java
new file mode 100644
index 0000000..f4eea1f
--- /dev/null
+++ b/tests/client/src/com/android/providers/media/client/PublicVolumePlaylistTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 android.provider.MediaStore.VOLUME_EXTERNAL;
+import static android.provider.MediaStore.VOLUME_EXTERNAL_PRIMARY;
+
+import static com.android.providers.media.client.PublicVolumeSetupHelper.createNewPublicVolume;
+import static com.android.providers.media.client.PublicVolumeSetupHelper.deletePublicVolumes;
+import static com.android.providers.media.client.PublicVolumeSetupHelper.mountPublicVolume;
+import static com.android.providers.media.client.PublicVolumeSetupHelper.unmountPublicVolume;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.MediaStore;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.OutputStream;
+
+@RunWith(AndroidJUnit4.class)
+public class PublicVolumePlaylistTest {
+    @BeforeClass
+    public static void setUp() throws Exception {
+        createNewPublicVolume();
+    }
+
+    @AfterClass
+    public static void tearDown() throws Exception {
+        deletePublicVolumes();
+    }
+
+    /**
+     * Test that playlist query doesn't return audio files of ejected volume.
+     */
+    @Test
+    // TODO(b/180910871) fix side effects
+    @Ignore
+    public void testEjectedVolume() throws Exception {
+        ContentValues values = new ContentValues();
+        values.clear();
+        values.put(MediaStore.MediaColumns.DISPLAY_NAME, "Playlist " + System.nanoTime());
+        values.put(MediaStore.MediaColumns.MIME_TYPE, "audio/x-mpegurl");
+
+        Context context = InstrumentationRegistry.getTargetContext();
+        ContentResolver contentResolver = context.getContentResolver();
+        MediaStore.waitForIdle(contentResolver);
+
+        final Uri externalPlaylists = MediaStore.Audio.Playlists
+                .getContentUri(VOLUME_EXTERNAL_PRIMARY);
+        final Uri playlist = contentResolver.insert(externalPlaylists, values);
+        assertNotNull(playlist);
+        // Use external uri for playlists to be able to add audio files from
+        // different volumes
+        final Uri members = MediaStore.Audio.Playlists.Members
+                .getContentUri(VOLUME_EXTERNAL, ContentUris.parseId(playlist));
+
+        mountPublicVolume();
+
+        // Create audio files in both volumes and add them to playlist.
+        for (String volumeName : MediaStore.getExternalVolumeNames(context)) {
+            values.clear();
+            values.put(MediaStore.MediaColumns.DISPLAY_NAME, "Song " + System.nanoTime());
+            values.put(MediaStore.MediaColumns.MIME_TYPE, "audio/mpeg");
+
+            final Uri audioUri = contentResolver.insert(
+                    MediaStore.Audio.Media.getContentUri(volumeName), values);
+            assertNotNull(audioUri);
+            try (OutputStream out = contentResolver.openOutputStream(audioUri)) {
+            }
+
+            values.clear();
+            values.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, ContentUris.parseId(audioUri));
+            assertThat(contentResolver.insert(members, values)).isNotNull();
+        }
+
+        final int volumeCount = MediaStore.getExternalVolumeNames(context).size();
+        // Verify that we can see audio files from both volumes in the playlist.
+        try (Cursor c = contentResolver.query(members, new String[] {
+                MediaStore.Audio.Playlists.Members.AUDIO_ID}, Bundle.EMPTY, null)) {
+            assertThat(c.getCount()).isEqualTo(volumeCount);
+        }
+
+        unmountPublicVolume();
+        // Verify that we don't see audio file from the ejected volume.
+        try (Cursor c = contentResolver.query(members, new String[] {
+                MediaStore.Audio.Playlists.Members.AUDIO_ID}, Bundle.EMPTY, null)) {
+            assertThat(c.getCount()).isEqualTo(volumeCount-1);
+        }
+    }
+}
+
diff --git a/tests/client/src/com/android/providers/media/client/PublicVolumeSetupHelper.java b/tests/client/src/com/android/providers/media/client/PublicVolumeSetupHelper.java
new file mode 100644
index 0000000..73bcf41
--- /dev/null
+++ b/tests/client/src/com/android/providers/media/client/PublicVolumeSetupHelper.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 android.app.UiAutomation;
+import android.os.Environment;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.google.common.io.ByteStreams;
+
+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;
+
+/**
+ * Helper methods for public volume setup.
+ */
+class PublicVolumeSetupHelper {
+    private static final long POLLING_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(2);
+    private static final long POLLING_SLEEP_MILLIS = 100;
+    private static final String TAG = "TestUtils";
+    private static boolean usingExistingPublicVolume = false;
+
+    /**
+     * Creates a new virtual public volume and returns the volume's name.
+     */
+    static void createNewPublicVolume() throws Exception {
+        // Skip public volume setup if we can use already available public volume on the device.
+        if (getCurrentPublicVolumeString() != null && isPublicVolumeMounted()) {
+            usingExistingPublicVolume = true;
+            return;
+        }
+        executeShellCommand("sm set-force-adoptable on");
+        executeShellCommand("sm set-virtual-disk true");
+        pollForCondition(() -> partitionDisk(), "Timed out while waiting for"
+                + " disk partitioning");
+        // Poll twice to avoid using previous mount status
+        pollForCondition(() -> isPublicVolumeMounted(), "Timed out while waiting for"
+                + " the public volume to mount");
+        pollForCondition(() -> isExternalStorageStateMounted(), "Timed out while"
+               + " waiting for ExternalStorageState to be MEDIA_MOUNTED");
+    }
+
+    private static boolean isExternalStorageStateMounted() {
+        final File target = Environment.getExternalStorageDirectory();
+        try {
+            return (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState(target))
+                    && Os.statvfs(target.getAbsolutePath()).f_blocks > 0);
+        } catch (ErrnoException ignored) {
+        }
+        return false;
+    }
+
+    private static 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;
+        }
+    }
+
+    /**
+     * Gets the name of the public volume string from list-volumes,
+     * waiting for a bit for it to be available.
+     */
+    private static String getPublicVolumeString() throws Exception {
+        final String[] volName = new String[1];
+        pollForCondition(() -> {
+            volName[0] = getCurrentPublicVolumeString();
+            return volName[0] != null;
+        }, "Timed out while waiting for public volume to be ready");
+
+        return volName[0];
+    }
+
+    /**
+     * @return the currently mounted public volume string, if any.
+     */
+    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[0];
+                if ("null".equals(res)) {
+                    continue;
+                }
+                return res;
+            }
+        }
+        return null;
+    }
+
+    static void mountPublicVolume() throws Exception {
+        executeShellCommand("sm mount " + getPublicVolumeString());
+    }
+
+    static void unmountPublicVolume() throws Exception {
+        executeShellCommand("sm unmount " + getPublicVolumeString());
+    }
+
+    static void deletePublicVolumes() throws Exception {
+        if (!usingExistingPublicVolume) {
+            executeShellCommand("sm set-virtual-disk false");
+            // Wait for the public volume to disappear.
+            for (int i = 0; i < POLLING_TIMEOUT_MILLIS / POLLING_SLEEP_MILLIS; i++) {
+                if (!isPublicVolumeMounted()) {
+                    return;
+                }
+                Thread.sleep(POLLING_SLEEP_MILLIS);
+            }
+        }
+    }
+
+    /**
+     * 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) {
+                // Hmm, we had trouble executing the shell command; the best we
+                // can do is try again a few more times
+                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));
+        }
+    }
+
+    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);
+    }
+}
diff --git a/tests/client/src/com/android/providers/media/client/PublicVolumeTest.java b/tests/client/src/com/android/providers/media/client/PublicVolumeTest.java
new file mode 100644
index 0000000..def72f7
--- /dev/null
+++ b/tests/client/src/com/android/providers/media/client/PublicVolumeTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/large_xmp.mp4 b/tests/res/raw/large_xmp.mp4
new file mode 100644
index 0000000..3dd4b2c
--- /dev/null
+++ b/tests/res/raw/large_xmp.mp4
Binary files differ
diff --git a/tests/res/raw/test_audio_empty_track_number.mp3 b/tests/res/raw/test_audio_empty_track_number.mp3
new file mode 100644
index 0000000..8cf8663
--- /dev/null
+++ b/tests/res/raw/test_audio_empty_track_number.mp3
Binary files differ
diff --git a/tests/res/raw/test_bmp.bmp b/tests/res/raw/test_bmp.bmp
new file mode 100644
index 0000000..e7cf515
--- /dev/null
+++ b/tests/res/raw/test_bmp.bmp
Binary files differ
diff --git a/tests/res/raw/test_broken_m3u.m3u b/tests/res/raw/test_broken_m3u.m3u
new file mode 100644
index 0000000..cd5d9db
--- /dev/null
+++ b/tests/res/raw/test_broken_m3u.m3u
Binary files differ
diff --git a/tests/src/com/android/providers/media/DatabaseHelperTest.java b/tests/src/com/android/providers/media/DatabaseHelperTest.java
index b2b7825..eef8bbd 100644
--- a/tests/src/com/android/providers/media/DatabaseHelperTest.java
+++ b/tests/src/com/android/providers/media/DatabaseHelperTest.java
@@ -18,20 +18,24 @@
 
 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.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.Files.FileColumns;
@@ -68,6 +72,8 @@
 
     @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();
@@ -75,7 +81,7 @@
 
     @Test
     public void testFilterVolumeNames() throws Exception {
-        try (DatabaseHelper helper = new DatabaseHelperR(sIsolatedContext, TEST_CLEAN_DB)) {
+        try (DatabaseHelper helper = new DatabaseHelperS(sIsolatedContext, TEST_CLEAN_DB)) {
             SQLiteDatabase db = helper.getWritableDatabaseForTest();
             {
                 final ContentValues values = new ContentValues();
@@ -234,18 +240,23 @@
     }
 
     @Test
-    public void testRtoO() throws Exception {
-        assertDowngrade(DatabaseHelperR.class, DatabaseHelperO.class);
+    public void testStoO() throws Exception {
+        assertDowngrade(DatabaseHelperS.class, DatabaseHelperO.class);
     }
 
     @Test
-    public void testRtoP() throws Exception {
-        assertDowngrade(DatabaseHelperR.class, DatabaseHelperP.class);
+    public void testStoP() throws Exception {
+        assertDowngrade(DatabaseHelperS.class, DatabaseHelperP.class);
     }
 
     @Test
-    public void testRtoQ() throws Exception {
-        assertDowngrade(DatabaseHelperR.class, DatabaseHelperQ.class);
+    public void testStoQ() throws Exception {
+        assertDowngrade(DatabaseHelperS.class, DatabaseHelperQ.class);
+    }
+
+    @Test
+    public void testStoR() throws Exception {
+        assertDowngrade(DatabaseHelperS.class, DatabaseHelperR.class);
     }
 
     private void assertDowngrade(Class<? extends DatabaseHelper> before,
@@ -279,20 +290,25 @@
     }
 
     @Test
-    public void testOtoR() throws Exception {
-        assertRecompute(DatabaseHelperO.class, DatabaseHelperR.class);
-        assertUpgrade(DatabaseHelperO.class, DatabaseHelperR.class);
+    public void testOtoS() throws Exception {
+        assertRecompute(DatabaseHelperO.class, DatabaseHelperS.class);
+        assertUpgrade(DatabaseHelperO.class, DatabaseHelperS.class);
     }
 
     @Test
-    public void testPtoR() throws Exception {
-        assertRecompute(DatabaseHelperP.class, DatabaseHelperR.class);
-        assertUpgrade(DatabaseHelperP.class, DatabaseHelperR.class);
+    public void testPtoS() throws Exception {
+        assertRecompute(DatabaseHelperP.class, DatabaseHelperS.class);
+        assertUpgrade(DatabaseHelperP.class, DatabaseHelperS.class);
     }
 
     @Test
-    public void testQtoR() throws Exception {
-        assertUpgrade(DatabaseHelperQ.class, DatabaseHelperR.class);
+    public void testQtoS() throws Exception {
+        assertUpgrade(DatabaseHelperQ.class, DatabaseHelperS.class);
+    }
+
+    @Test
+    public void testRtoS() throws Exception {
+        assertUpgrade(DatabaseHelperR.class, DatabaseHelperS.class);
     }
 
     private void assertRecompute(Class<? extends DatabaseHelper> before,
@@ -336,17 +352,6 @@
             {
                 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());
@@ -396,18 +401,6 @@
                         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)) {
@@ -467,6 +460,122 @@
         }
     }
 
+    /**
+     * Test that existing database rows will default to _modifier=MODIFIER_MEDIA_SCAN
+     * after database upgrade.
+     */
+    @Test
+    public void testUpgradeAndAddModifier() throws Exception {
+        Class<? extends DatabaseHelper> beforeModifier = DatabaseHelperR.class;
+        Class<? extends DatabaseHelper> afterModifier = DatabaseHelperS.class;
+
+        try (DatabaseHelper helper = beforeModifier.getConstructor(Context.class, String.class)
+                .newInstance(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 = afterModifier.getConstructor(Context.class, String.class)
+                .newInstance(sIsolatedContext, TEST_UPGRADE_DB)) {
+            SQLiteDatabase db = helper.getWritableDatabaseForTest();
+
+            try (Cursor cr = db.query("files", new String[]{FileColumns._MODIFIER}, null, null,
+                    null, null, null)) {
+                assertEquals(cr.getCount(), 1);
+                while (cr.moveToNext()) {
+                    // Verify that after db upgrade, for existing database rows, we set value of
+                    // _modifier=MODIFIER_MEDIA_SCAN
+                    assertThat(cr.getInt(0)).isEqualTo(FileColumns._MODIFIER_MEDIA_SCAN);
+                }
+            }
+        }
+    }
+
+    @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;
     }
@@ -525,6 +634,19 @@
                     false, false, false, Column.class, null, null,
                     MediaProvider.MIGRATION_LISTENER, null);
         }
+
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+            createRSchema(db, false);
+        }
+    }
+
+    private static class DatabaseHelperS extends DatabaseHelper {
+        public DatabaseHelperS(Context context, String name) {
+            super(context, name, DatabaseHelper.VERSION_S,
+                    false, false, false, Column.class, null, null,
+                    MediaProvider.MIGRATION_LISTENER, null);
+        }
     }
 
     /**
@@ -935,4 +1057,168 @@
                 + "instance_id,duration,description,orientation,height,is_drm,bucket_display_name,owner_package_name,volume_name,date_modified,date_expires,_display_name,datetaken,mime_type,referer_uri,_id,_data,_hash,_size,title,width,is_trashed,group_id,document_id,is_pending,date_added,download_uri,primary_directory,secondary_directory,original_document_id,bucket_id,relative_path"
                 + " FROM files WHERE is_download=1");
     }
+
+
+    /**
+     * Snapshot of {@link DatabaseHelper#createLatestSchema} as of
+     * {@link android.os.Build.VERSION_CODES#R}.
+     */
+    private static void createRSchema(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)");
+
+        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)");
+        }
+
+        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 " + "_id"
+                + ", MIN(artist) AS " + "artist"
+                + ", artist_key AS " + "artist_key"
+                + ", COUNT(DISTINCT album_id) AS " + "number_of_albums"
+                + ", COUNT(DISTINCT _id) AS " + "number_of_tracks"
+                + " FROM audio"
+                + " WHERE is_music=1"
+                + " GROUP BY artist_id");
+
+        db.execSQL("CREATE VIEW audio_albums AS SELECT "
+                + "  album_id AS " + "_id"
+                + ", album_id AS " + "album_id"
+                + ", MIN(album) AS " + "album"
+                + ", album_key AS " + "album_key"
+                + ", artist_id AS " + "artist_id"
+                + ", artist AS " + "artist"
+                + ", artist_key AS " + "artist_key"
+                + ", COUNT(DISTINCT _id) AS " + "numsongs"
+                + ", COUNT(DISTINCT _id) AS " + "numsongs_by_artist"
+                + ", MIN(year) AS " + "minyear"
+                + ", MAX(year) AS " + "maxyear"
+                + ", NULL AS " + "album_art"
+                + " FROM audio"
+                + " WHERE is_music=1"
+                + " GROUP BY album_id");
+
+        db.execSQL("CREATE VIEW audio_genres AS SELECT "
+                + "  genre_id AS " + "_id"
+                + ", MIN(genre) AS " + "name"
+                + " FROM audio"
+                + " 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 b0af8853..064a4f8 100644
--- a/tests/src/com/android/providers/media/IdleServiceTest.java
+++ b/tests/src/com/android/providers/media/IdleServiceTest.java
@@ -19,25 +19,35 @@
 import static android.os.Environment.DIRECTORY_MOVIES;
 import static android.os.Environment.DIRECTORY_PICTURES;
 import static android.os.Environment.buildPath;
+import static android.provider.MediaStore.MediaColumns.DATE_EXPIRES;
+
+import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import android.Manifest;
 import android.app.UiAutomation;
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.Context;
+import android.database.Cursor;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.provider.MediaStore;
+import android.text.format.DateUtils;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.providers.media.scan.MediaScannerTest;
+import com.android.providers.media.util.FileUtils;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -48,16 +58,43 @@
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.nio.charset.StandardCharsets;
+import java.util.Locale;
 
 @RunWith(AndroidJUnit4.class)
 public class IdleServiceTest {
     private static final String TAG = MediaProviderTest.TAG;
 
+    private File mDir;
+
+    @Before
+    public void setUp() {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                .adoptShellPermissionIdentity(Manifest.permission.LOG_COMPAT_CHANGE,
+                        Manifest.permission.READ_COMPAT_CHANGE_CONFIG);
+
+        mDir = new File(context.getExternalMediaDirs()[0], "test_" + System.nanoTime());
+        mDir.mkdirs();
+        FileUtils.deleteContents(mDir);
+    }
+
+    @After
+    public void tearDown() {
+        FileUtils.deleteContents(mDir);
+        InstrumentationRegistry.getInstrumentation()
+                .getUiAutomation().dropShellPermissionIdentity();
+    }
+
     @Test
     public void testPruneThumbnails() throws Exception {
         final Context context = InstrumentationRegistry.getTargetContext();
         final ContentResolver resolver = context.getContentResolver();
 
+        // Previous tests (like DatabaseHelperTest) may have left stale
+        // .database_uuid files, do an idle run first to clean them up.
+        runIdleMaintenance(resolver);
+        MediaStore.waitForIdle(resolver);
+
         final File dir = Environment.getExternalStorageDirectory();
         final File mediaDir = context.getExternalMediaDirs()[0];
 
@@ -98,6 +135,76 @@
         assertFalse(exists(d));
     }
 
+    @Test
+    public void testExtendTrashedItemExpiresOverOneWeek() throws Exception {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        final ContentResolver resolver = context.getContentResolver();
+
+        // Create the expired item and scan the file to add it into database
+        final long dateExpires = (System.currentTimeMillis() - 10 * DateUtils.DAY_IN_MILLIS) / 1000;
+        final String expiredFileName = String.format(Locale.US, ".%s-%d-%s",
+                FileUtils.PREFIX_TRASHED, dateExpires, System.nanoTime() + ".jpg");
+        final File file = MediaScannerTest.stage(R.raw.test_image,
+                new File(mDir, expiredFileName));
+        final Uri uri = MediaStore.scanFile(resolver, file);
+
+        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);
+        }
+
+        final long expectedExtendedTimestamp =
+                (System.currentTimeMillis() + FileUtils.DEFAULT_DURATION_EXTENDED) / 1000 - 1;
+        runIdleMaintenance(resolver);
+
+        final long dateExpiresAfter;
+        try (Cursor cursor = resolver.query(uri, projection, queryArgs,
+                null /* cancellationSignal */)) {
+            assertThat(cursor.getCount()).isEqualTo(1);
+            cursor.moveToFirst();
+            dateExpiresAfter = cursor.getLong(0);
+            assertThat(dateExpiresAfter).isGreaterThan(expectedExtendedTimestamp);
+        }
+    }
+
+    @Test
+    public void testDeleteExpiredTrashedItem() throws Exception {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        final ContentResolver resolver = context.getContentResolver();
+
+        // Create the expired item and scan the file to add it into database
+        final long dateExpires = (System.currentTimeMillis() - 3 * DateUtils.DAY_IN_MILLIS) / 1000;
+        final 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);
+
+        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);
+        }
+    }
+
     private static void runIdleMaintenance(ContentResolver resolver) {
         final UiAutomation ui = InstrumentationRegistry.getInstrumentation().getUiAutomation();
         ui.adoptShellPermissionIdentity(android.Manifest.permission.DUMP);
diff --git a/tests/src/com/android/providers/media/LocalCallingIdentityTest.java b/tests/src/com/android/providers/media/LocalCallingIdentityTest.java
index e30ed92..1abca94 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,
+        final LocalCallingIdentity ident = LocalCallingIdentity.fromExternal(context, null,
                 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 2db98b9..f9754e8 100644
--- a/tests/src/com/android/providers/media/MediaDocumentsProviderTest.java
+++ b/tests/src/com/android/providers/media/MediaDocumentsProviderTest.java
@@ -63,7 +63,8 @@
     public void setUp() {
         InstrumentationRegistry.getInstrumentation().getUiAutomation()
                 .adoptShellPermissionIdentity(Manifest.permission.LOG_COMPAT_CHANGE,
-                        Manifest.permission.READ_COMPAT_CHANGE_CONFIG);
+                        Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
+                        Manifest.permission.INTERACT_ACROSS_USERS);
     }
 
     @After
diff --git a/tests/src/com/android/providers/media/MediaProviderForFuseTest.java b/tests/src/com/android/providers/media/MediaProviderForFuseTest.java
index 277acf2..7de9834 100644
--- a/tests/src/com/android/providers/media/MediaProviderForFuseTest.java
+++ b/tests/src/com/android/providers/media/MediaProviderForFuseTest.java
@@ -58,7 +58,8 @@
         InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
                 Manifest.permission.LOG_COMPAT_CHANGE,
                 Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
-                Manifest.permission.UPDATE_APP_OPS_STATS);
+                Manifest.permission.UPDATE_APP_OPS_STATS,
+                Manifest.permission.INTERACT_ACROSS_USERS);
 
         final Context context = InstrumentationRegistry.getTargetContext();
         sIsolatedContext = new IsolatedContext(context, "modern", /*asFuseThread*/ true);
@@ -95,12 +96,11 @@
         file.createNewFile();
 
         // We can write our file
-        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]);
+        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]);
 
         // We can rename our file
         final File renamed = new File(sTestDir, "renamed" + System.nanoTime() + ".jpg");
@@ -138,13 +138,6 @@
     }
 
     @Test
-    public void test_scanFileForFuse() throws Exception {
-        final File file = new File(sTestDir, "test" + System.nanoTime() + ".jpg");
-        Truth.assertThat(file.createNewFile()).isTrue();
-        sMediaProvider.scanFileForFuse(file.getPath());
-    }
-
-    @Test
     public void test_isOpendirAllowedForFuse() throws Exception {
         Truth.assertThat(sMediaProvider.isOpendirAllowedForFuse(
                 sTestDir.getPath(), sTestUid, /* forWrite */ false)).isEqualTo(0);
diff --git a/tests/src/com/android/providers/media/MediaProviderTest.java b/tests/src/com/android/providers/media/MediaProviderTest.java
index 76faa16..aa0a697 100644
--- a/tests/src/com/android/providers/media/MediaProviderTest.java
+++ b/tests/src/com/android/providers/media/MediaProviderTest.java
@@ -17,10 +17,13 @@
 package com.android.providers.media;
 
 import static com.android.providers.media.scan.MediaScannerTest.stage;
+import static com.android.providers.media.util.FileUtils.extractDisplayName;
+import static com.android.providers.media.util.FileUtils.extractRelativePath;
 import static com.android.providers.media.util.FileUtils.extractRelativePathForDirectory;
 import static com.android.providers.media.util.FileUtils.isDownload;
 import static com.android.providers.media.util.FileUtils.isDownloadDir;
 
+import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -41,12 +44,14 @@
 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;
@@ -63,6 +68,7 @@
 import com.android.providers.media.MediaProvider.VolumeNotFoundException;
 import com.android.providers.media.scan.MediaScannerTest.IsolatedContext;
 import com.android.providers.media.util.FileUtils;
+import com.android.providers.media.util.FileUtilsTest;
 import com.android.providers.media.util.SQLiteQueryBuilder;
 
 import org.junit.AfterClass;
@@ -75,10 +81,12 @@
 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;
 
@@ -86,11 +94,8 @@
 public class MediaProviderTest {
     static final String TAG = "MediaProviderTest";
 
-    /**
-     * 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";
+    // The test app without permissions
+    static final String PERMISSIONLESS_APP = "com.android.providers.media.testapp.withoutperms";
 
     private static Context sIsolatedContext;
     private static ContentResolver sIsolatedResolver;
@@ -99,7 +104,9 @@
     public static void setUp() {
         InstrumentationRegistry.getInstrumentation().getUiAutomation()
                 .adoptShellPermissionIdentity(Manifest.permission.LOG_COMPAT_CHANGE,
-                        Manifest.permission.READ_COMPAT_CHANGE_CONFIG);
+                        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);
@@ -313,6 +320,59 @@
                 android.os.Process.myUid(), Intent.FLAG_GRANT_READ_URI_PERMISSION));
     }
 
+    @Test
+    public void testTrashLongFileNameItemHasTrimmedFileName() throws Exception {
+        testActionLongFileNameItemHasTrimmedFileName(MediaColumns.IS_TRASHED);
+    }
+
+    @Test
+    public void testPendingLongFileNameItemHasTrimmedFileName() throws Exception {
+        testActionLongFileNameItemHasTrimmedFileName(MediaColumns.IS_PENDING);
+    }
+
+    private void testActionLongFileNameItemHasTrimmedFileName(String columnKey) throws Exception {
+        // We might have old files lurking, so force a clean slate
+        final Context context = InstrumentationRegistry.getTargetContext();
+        sIsolatedContext = new IsolatedContext(context, "modern", /*asFuseThread*/ false);
+        sIsolatedResolver = sIsolatedContext.getContentResolver();
+        final String[] projection = new String[]{MediaColumns.DATA};
+        final File dir = Environment
+                .getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
+
+        // create extreme long file name
+        final String originalName = FileUtilsTest.createExtremeFileName("test" + System.nanoTime(),
+                ".jpg");
+
+        File file = stage(R.raw.lg_g4_iso_800_jpg, new File(dir, originalName));
+        final Uri uri = MediaStore.scanFile(sIsolatedResolver, file);
+        Log.v(TAG, "Scanned " + file + " as " + uri);
+
+        try (Cursor c = sIsolatedResolver.query(uri, projection, null, null)) {
+            assertNotNull(c);
+            assertEquals(1, c.getCount());
+            assertTrue(c.moveToFirst());
+            final String data = c.getString(0);
+            final String result = FileUtils.extractDisplayName(data);
+            assertEquals(originalName, result);
+        }
+
+        final Bundle extras = new Bundle();
+        extras.putBoolean(MediaStore.QUERY_ARG_ALLOW_MOVEMENT, true);
+        final ContentValues values = new ContentValues();
+        values.put(columnKey, 1);
+        sIsolatedResolver.update(uri, values, extras);
+
+        try (Cursor c = sIsolatedResolver.query(uri, projection, null, null)) {
+            assertNotNull(c);
+            assertEquals(1, c.getCount());
+            assertTrue(c.moveToFirst());
+            final String data = c.getString(0);
+            final String result = FileUtils.extractDisplayName(data);
+            assertThat(result.length()).isAtMost(FileUtilsTest.MAX_FILENAME_BYTES);
+            assertNotEquals(originalName, result);
+        }
+    }
+
     /**
      * We already have solid coverage of this logic in
      * {@code CtsProviderTestCases}, but the coverage system currently doesn't
@@ -434,8 +494,6 @@
                 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
@@ -458,6 +516,24 @@
     }
 
     @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",
@@ -548,6 +624,10 @@
             }
         };
 
+        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();
 
@@ -629,6 +709,18 @@
     }
 
     @Test
+    public void testBuildData_Pending_FromValues_differentLocale() throws Exception {
+        // See b/174120008 for context.
+        Locale defaultLocale = Locale.getDefault();
+        try {
+            Locale.setDefault(new Locale("ar", "SA"));
+            testBuildData_Pending_FromValues();
+        } finally {
+            Locale.setDefault(defaultLocale);
+        }
+    }
+
+    @Test
     public void testBuildData_Pending_FromData() throws Exception {
         final Uri uri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
         final ContentValues reverse = new ContentValues();
@@ -665,6 +757,18 @@
     }
 
     @Test
+    public void testBuildData_Trashed_FromValues_differentLocale() throws Exception {
+        // See b/174120008 for context.
+        Locale defaultLocale = Locale.getDefault();
+        try {
+            Locale.setDefault(new Locale("ar", "SA"));
+            testBuildData_Trashed_FromValues();
+        } finally {
+            Locale.setDefault(defaultLocale);
+        }
+    }
+
+    @Test
     public void testBuildData_Trashed_FromData() throws Exception {
         final Uri uri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
         final ContentValues reverse = new ContentValues();
@@ -805,6 +909,7 @@
         map.put("external", "internal");
         builder.setProjectionMap(map);
         builder.setStrict(true);
+        builder.setStrictColumns(true);
 
         assertArrayEquals(
                 new String[] { "internal" },
@@ -832,36 +937,19 @@
         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
@@ -922,7 +1010,6 @@
 
         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);
@@ -963,6 +1050,10 @@
                 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");
@@ -1115,6 +1206,67 @@
         assertEquals(albumIdThree, albumIdFour);
     }
 
+    @Test
+    public void testQueryAudioViewsNoTrashedItem() throws Exception {
+        testQueryAudioViewsNoItemWithColumn(MediaStore.Audio.Media.IS_TRASHED);
+    }
+
+    @Test
+    public void testQueryAudioViewsNoPendingItem() throws Exception {
+        testQueryAudioViewsNoItemWithColumn(MediaStore.Audio.Media.IS_PENDING);
+    }
+
+    private void testQueryAudioViewsNoItemWithColumn(String columnKey) throws Exception {
+        // We might have old files lurking, so force a clean slate
+        final Context context = InstrumentationRegistry.getTargetContext();
+        sIsolatedContext = new IsolatedContext(context, "modern", /*asFuseThread*/ false);
+        sIsolatedResolver = sIsolatedContext.getContentResolver();
+        final File dir = Environment
+                .getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
+
+        final File audio = new File(dir, "test" + System.nanoTime() + ".mp3");
+        final Uri audioUri =
+                MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
+        final String album = "TestAlbum" + System.nanoTime();
+        final String artist = "TestArtist" + System.nanoTime();
+        final String genre = "TestGenre" + System.nanoTime();
+        final String relativePath = extractRelativePath(audio.getAbsolutePath());
+        final String displayName = extractDisplayName(audio.getAbsolutePath());
+        ContentValues values = new ContentValues();
+
+        values.put(MediaStore.Audio.Media.ALBUM, album);
+        values.put(MediaStore.Audio.Media.ARTIST, artist);
+        values.put(MediaStore.Audio.Media.GENRE, genre);
+        values.put(MediaStore.Audio.Media.DISPLAY_NAME, displayName);
+        values.put(MediaStore.Audio.Media.RELATIVE_PATH, relativePath);
+        values.put(MediaStore.Audio.Media.IS_MUSIC, 1);
+        values.put(columnKey, 1);
+
+        Uri result = sIsolatedResolver.insert(audioUri, values);
+
+        // Check the audio file is inserted correctly
+        try (Cursor c = sIsolatedResolver.query(result,
+                new String[]{MediaColumns.DISPLAY_NAME, columnKey}, null, null)) {
+            assertNotNull(c);
+            assertEquals(1, c.getCount());
+            assertTrue(c.moveToFirst());
+            assertEquals(displayName, c.getString(0));
+            assertEquals(1, c.getInt(1));
+        }
+
+        final String volume = MediaStore.VOLUME_EXTERNAL_PRIMARY;
+        assertQueryResultNoItems(MediaStore.Audio.Albums.getContentUri(volume));
+        assertQueryResultNoItems(MediaStore.Audio.Artists.getContentUri(volume));
+        assertQueryResultNoItems(MediaStore.Audio.Genres.getContentUri(volume));
+    }
+
+    private static void assertQueryResultNoItems(Uri uri) throws Exception {
+        try (Cursor c = sIsolatedResolver.query(uri, null, null, null, null)) {
+            assertNotNull(c);
+            assertEquals(0, c.getCount());
+        }
+    }
+
     private static void assertRelativePathForDirectory(String directoryPath, String relativePath) {
         assertWithMessage("extractRelativePathForDirectory(" + directoryPath + ") :")
                 .that(extractRelativePathForDirectory(directoryPath))
@@ -1210,7 +1362,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),
         };
@@ -1219,4 +1371,102 @@
         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 7bbce62..6eb738e 100644
--- a/tests/src/com/android/providers/media/PermissionActivityTest.java
+++ b/tests/src/com/android/providers/media/PermissionActivityTest.java
@@ -16,6 +16,30 @@
 
 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;
@@ -24,16 +48,22 @@
 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},
@@ -42,6 +72,43 @@
  */
 @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();
@@ -52,6 +119,155 @@
         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();
 
@@ -67,4 +283,52 @@
         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/ResolvePlaylistTest.java b/tests/src/com/android/providers/media/ResolvePlaylistTest.java
new file mode 100644
index 0000000..0f6c50a
--- /dev/null
+++ b/tests/src/com/android/providers/media/ResolvePlaylistTest.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.scan.MediaScanner.REASON_UNKNOWN;
+import static com.android.providers.media.scan.MediaScannerTest.stage;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.Manifest;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.MediaStore;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.providers.media.scan.MediaScannerTest;
+import com.android.providers.media.scan.ModernMediaScanner;
+import com.android.providers.media.util.FileUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+@RunWith(AndroidJUnit4.class)
+public class ResolvePlaylistTest {
+    private File mDir;
+
+    private Context mIsolatedContext;
+    private ContentResolver mIsolatedResolver;
+
+    private ModernMediaScanner mModern;
+
+    @Before
+    public void setUp() {
+        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);
+
+        mDir = new File(context.getExternalMediaDirs()[0], "test_" + System.nanoTime());
+        mDir.mkdirs();
+        FileUtils.deleteContents(mDir);
+
+        mIsolatedContext = new MediaScannerTest.IsolatedContext(context, "modern",
+                /*asFuseThread*/ false);
+        mIsolatedResolver = mIsolatedContext.getContentResolver();
+
+        mModern = new ModernMediaScanner(mIsolatedContext);
+    }
+
+    @After
+    public void tearDown() {
+        FileUtils.deleteContents(mDir);
+    }
+
+    @Test
+    public void testPlaylistM3u() throws Exception {
+        doPlaylist(R.raw.test_m3u, "test.m3u");
+    }
+
+    @Test
+    public void testPlaylistPls() throws Exception {
+        doPlaylist(R.raw.test_pls, "test.pls");
+    }
+
+    @Test
+    public void testPlaylistWpl() throws Exception {
+        doPlaylist(R.raw.test_wpl, "test.wpl");
+    }
+
+    @Test
+    public void testPlaylistXspf() throws Exception {
+        doPlaylist(R.raw.test_xspf, "test.xspf");
+    }
+
+    private void doPlaylist(int res, String name) throws Exception {
+        final File music = new File(mDir, "Music");
+        music.mkdirs();
+        stage(R.raw.test_audio, new File(music, "001.mp3"));
+        stage(R.raw.test_audio, new File(music, "002.mp3"));
+        stage(R.raw.test_audio, new File(music, "003.mp3"));
+        stage(R.raw.test_audio, new File(music, "004.mp3"));
+        stage(R.raw.test_audio, new File(music, "005.mp3"));
+        stage(res, new File(music, name));
+
+        mModern.scanDirectory(mDir, REASON_UNKNOWN);
+
+        // We should see a new playlist with all three items as members
+        final long playlistId;
+        try (Cursor cursor = mIsolatedContext.getContentResolver().query(
+                MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL),
+                new String[] { MediaStore.Files.FileColumns._ID },
+                MediaStore.Files.FileColumns.MEDIA_TYPE + "="
+                        + MediaStore.Files.FileColumns.MEDIA_TYPE_PLAYLIST, null, null)) {
+            assertTrue(cursor.moveToFirst());
+            playlistId = cursor.getLong(0);
+        }
+
+        final Uri membersUri = MediaStore.Audio.Playlists.Members
+                .getContentUri(MediaStore.VOLUME_EXTERNAL, playlistId);
+        try (Cursor cursor = mIsolatedResolver.query(membersUri, new String[] {
+                MediaStore.MediaColumns.DISPLAY_NAME
+        }, null, null, MediaStore.Audio.Playlists.Members.PLAY_ORDER + " ASC")) {
+            assertEquals(5, cursor.getCount());
+            cursor.moveToNext();
+            assertEquals("001.mp3", cursor.getString(0));
+            cursor.moveToNext();
+            assertEquals("002.mp3", cursor.getString(0));
+            cursor.moveToNext();
+            assertEquals("003.mp3", cursor.getString(0));
+            cursor.moveToNext();
+            assertEquals("004.mp3", cursor.getString(0));
+            cursor.moveToNext();
+            assertEquals("005.mp3", cursor.getString(0));
+        }
+
+        // Delete one of the media files and rescan
+        new File(music, "002.mp3").delete();
+        new File(music, name).setLastModified(10L);
+        mModern.scanDirectory(mDir, REASON_UNKNOWN);
+
+        try (Cursor cursor = mIsolatedResolver.query(membersUri, new String[] {
+                MediaStore.MediaColumns.DISPLAY_NAME
+        }, null, null, MediaStore.Audio.Playlists.Members.PLAY_ORDER + " ASC")) {
+            assertEquals(4, cursor.getCount());
+            cursor.moveToNext();
+            assertEquals("001.mp3", cursor.getString(0));
+            cursor.moveToNext();
+            assertEquals("003.mp3", cursor.getString(0));
+        }
+
+        // Replace media file in a completely different location, which normally
+        // wouldn't match the exact playlist path, but we're willing to perform
+        // a relaxed search
+        final File soundtracks = new File(mDir, "Soundtracks");
+        soundtracks.mkdirs();
+        stage(R.raw.test_audio, new File(soundtracks, "002.mp3"));
+        stage(res, new File(music, name));
+
+        mModern.scanDirectory(mDir, REASON_UNKNOWN);
+
+        try (Cursor cursor = mIsolatedResolver.query(membersUri, new String[] {
+                MediaStore.MediaColumns.DISPLAY_NAME
+        }, null, null, MediaStore.Audio.Playlists.Members.PLAY_ORDER + " ASC")) {
+            assertEquals(5, cursor.getCount());
+            cursor.moveToNext();
+            assertEquals("001.mp3", cursor.getString(0));
+            cursor.moveToNext();
+            assertEquals("002.mp3", cursor.getString(0));
+            cursor.moveToNext();
+            assertEquals("003.mp3", cursor.getString(0));
+        }
+    }
+
+    @Test
+    public void testBrokenPlaylistM3u() throws Exception {
+        final File music = new File(mDir, "Music");
+        music.mkdirs();
+        stage(R.raw.test_audio, new File(music, "001.mp3"));
+        stage(R.raw.test_audio, new File(music, "002.mp3"));
+        stage(R.raw.test_audio, new File(music, "003.mp3"));
+        stage(R.raw.test_audio, new File(music, "004.mp3"));
+        stage(R.raw.test_audio, new File(music, "005.mp3"));
+        stage(R.raw.test_broken_m3u, new File(music, "test_broken.m3u"));
+
+        mModern.scanDirectory(mDir, REASON_UNKNOWN);
+
+        final long playlistId;
+        try (Cursor cursor = mIsolatedContext.getContentResolver().query(
+                MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL),
+                new String[] { MediaStore.Files.FileColumns._ID },
+                MediaStore.Files.FileColumns.MEDIA_TYPE + "="
+                        + MediaStore.Files.FileColumns.MEDIA_TYPE_PLAYLIST, null, null)) {
+            assertTrue(cursor.moveToFirst());
+            playlistId = cursor.getLong(0);
+        }
+
+        final Uri membersUri = MediaStore.Audio.Playlists.Members
+                .getContentUri(MediaStore.VOLUME_EXTERNAL, playlistId);
+        try (Cursor cursor = mIsolatedResolver.query(membersUri, new String[] {
+            MediaStore.MediaColumns.DISPLAY_NAME
+        }, null, null, null)) {
+            assertEquals(0, cursor.getCount());
+        }
+    }
+
+    @Test
+    public void testPlaylistDeletion() throws Exception {
+        final File music = new File(mDir, "Music");
+        music.mkdirs();
+        stage(R.raw.test_audio, new File(music, "001.mp3"));
+        stage(R.raw.test_audio, new File(music, "002.mp3"));
+        stage(R.raw.test_audio, new File(music, "003.mp3"));
+        stage(R.raw.test_audio, new File(music, "004.mp3"));
+        stage(R.raw.test_audio, new File(music, "005.mp3"));
+        stage(R.raw.test_m3u, new File(music, "test.m3u"));
+
+        mModern.scanDirectory(mDir, REASON_UNKNOWN);
+
+        final Uri playlistUri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
+        final long playlistId;
+        try (Cursor cursor = mIsolatedContext.getContentResolver().query(playlistUri,
+                new String[] { MediaStore.Files.FileColumns._ID }, null, null)) {
+            assertTrue(cursor.moveToFirst());
+            playlistId = cursor.getLong(0);
+        }
+
+        final int count = mIsolatedContext.getContentResolver().delete(
+                ContentUris.withAppendedId(playlistUri, playlistId), null);
+        assertEquals(1, count);
+
+        MediaStore.waitForIdle(mIsolatedResolver);
+
+        final Uri membersUri = MediaStore.Audio.Playlists.Members
+                .getContentUri(MediaStore.VOLUME_EXTERNAL, playlistId);
+        try (Cursor cursor = mIsolatedResolver.query(membersUri, null, null, null)) {
+            assertEquals(0, cursor.getCount());
+        }
+    }
+
+    @Test
+    public void testPlaylistMembersDeletion() throws Exception {
+        final File music = new File(mDir, "Music");
+        music.mkdirs();
+        stage(R.raw.test_audio, new File(music, "001.mp3"));
+        stage(R.raw.test_audio, new File(music, "002.mp3"));
+        stage(R.raw.test_audio, new File(music, "003.mp3"));
+        stage(R.raw.test_audio, new File(music, "004.mp3"));
+        stage(R.raw.test_audio, new File(music, "005.mp3"));
+        stage(R.raw.test_m3u, new File(music, "test.m3u"));
+
+        mModern.scanDirectory(mDir, REASON_UNKNOWN);
+
+        final Uri playlistUri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
+        final long playlistId;
+        try (Cursor cursor = mIsolatedContext.getContentResolver().query(playlistUri,
+                new String[] { MediaStore.Files.FileColumns._ID }, null, null)) {
+            assertTrue(cursor.moveToFirst());
+            playlistId = cursor.getLong(0);
+        }
+
+        final int count = mIsolatedContext.getContentResolver().delete(
+                MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null);
+        assertEquals(5, count);
+
+        MediaStore.waitForIdle(mIsolatedResolver);
+
+        final Uri membersUri = MediaStore.Audio.Playlists.Members
+                .getContentUri(MediaStore.VOLUME_EXTERNAL, playlistId);
+        try (Cursor cursor = mIsolatedResolver.query(membersUri, null, null, null)) {
+            assertEquals(0, cursor.getCount());
+        }
+    }
+
+}
diff --git a/tests/src/com/android/providers/media/TranscodeHelperTest.java b/tests/src/com/android/providers/media/TranscodeHelperTest.java
new file mode 100644
index 0000000..3aab7eb
--- /dev/null
+++ b/tests/src/com/android/providers/media/TranscodeHelperTest.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/metrics/StorageAccessMetricsTest.java b/tests/src/com/android/providers/media/metrics/StorageAccessMetricsTest.java
new file mode 100644
index 0000000..5be69ff
--- /dev/null
+++ b/tests/src/com/android/providers/media/metrics/StorageAccessMetricsTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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
new file mode 100644
index 0000000..8b635df
--- /dev/null
+++ b/tests/src/com/android/providers/media/metrics/TranscodeMetricsTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/playlist/PlaylistTest.java b/tests/src/com/android/providers/media/playlist/PlaylistTest.java
index fd453e0..d7b7f92 100644
--- a/tests/src/com/android/providers/media/playlist/PlaylistTest.java
+++ b/tests/src/com/android/providers/media/playlist/PlaylistTest.java
@@ -16,6 +16,8 @@
 
 package com.android.providers.media.playlist;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -128,7 +130,31 @@
         assertPlaylistEquals(GREEN);
     }
 
+    @Test
+    public void testRemoveMultiple() throws Exception {
+        playlist.add(0, RED);
+        playlist.add(1, GREEN);
+        playlist.add(2, BLUE);
+        assertPlaylistEquals(RED, GREEN, BLUE);
+
+        // Unlike #remove, #removeMultiple ignores out of bounds indexes
+        assertEquals(0, playlist.removeMultiple(100));
+        assertPlaylistEquals(RED, GREEN, BLUE);
+
+        assertEquals(2, playlist.removeMultiple(0, 2));
+        assertPlaylistEquals(GREEN);
+
+        assertEquals(1, playlist.removeMultiple(0));
+        assertPlaylistEmpty();
+    }
+
+
+
     private void assertPlaylistEquals(Path... items) {
         assertEquals(items, playlist.asList().toArray());
     }
+
+    private void assertPlaylistEmpty() {
+        assertThat(playlist.asList()).isEmpty();
+    }
 }
diff --git a/tests/src/com/android/providers/media/scan/LegacyMediaScannerTest.java b/tests/src/com/android/providers/media/scan/LegacyMediaScannerTest.java
index af13fb8..cf9cb39 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(MediaStore.VOLUME_EXTERNAL_PRIMARY);
+            scanner.onDetachVolume(null);
             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 3d28820..98cbdcc 100644
--- a/tests/src/com/android/providers/media/scan/MediaScannerTest.java
+++ b/tests/src/com/android/providers/media/scan/MediaScannerTest.java
@@ -22,6 +22,7 @@
 
 import static org.junit.Assert.assertEquals;
 
+import android.Manifest;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.ContextWrapper;
@@ -33,6 +34,7 @@
 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;
@@ -85,6 +87,26 @@
                 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);
@@ -122,6 +144,8 @@
     @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 f7bb434..ca53f65 100644
--- a/tests/src/com/android/providers/media/scan/ModernMediaScannerTest.java
+++ b/tests/src/com/android/providers/media/scan/ModernMediaScannerTest.java
@@ -18,7 +18,7 @@
 
 import static com.android.providers.media.scan.MediaScanner.REASON_UNKNOWN;
 import static com.android.providers.media.scan.MediaScannerTest.stage;
-import static com.android.providers.media.scan.ModernMediaScanner.shouldScanPathAndIsPathHidden;
+import static com.android.providers.media.scan.ModernMediaScanner.MAX_EXCLUDE_DIRS;
 import static com.android.providers.media.scan.ModernMediaScanner.isFileAlbumArt;
 import static com.android.providers.media.scan.ModernMediaScanner.parseOptional;
 import static com.android.providers.media.scan.ModernMediaScanner.parseOptionalDate;
@@ -33,9 +33,12 @@
 import static com.android.providers.media.scan.ModernMediaScanner.parseOptionalVideoResolution;
 import static com.android.providers.media.scan.ModernMediaScanner.parseOptionalYear;
 import static com.android.providers.media.scan.ModernMediaScanner.shouldScanDirectory;
+import static com.android.providers.media.scan.ModernMediaScanner.shouldScanPathAndIsPathHidden;
 import static com.android.providers.media.util.FileUtils.isDirectoryHidden;
 import static com.android.providers.media.util.FileUtils.isFileHidden;
 
+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.assertNotNull;
@@ -55,19 +58,23 @@
 import android.media.ExifInterface;
 import android.media.MediaMetadataRetriever;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.provider.MediaStore;
-import android.provider.MediaStore.Files.FileColumns;
+import android.provider.MediaStore.Audio.AudioColumns;
 import android.provider.MediaStore.MediaColumns;
+import android.text.format.DateUtils;
 import android.util.Log;
 import android.util.Pair;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SdkSuppress;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.providers.media.R;
 import com.android.providers.media.scan.MediaScannerTest.IsolatedContext;
+import com.android.providers.media.tests.utils.Timer;
 import com.android.providers.media.util.FileUtils;
 
 import com.google.common.io.ByteStreams;
@@ -83,6 +90,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InterruptedIOException;
+import java.util.Locale;
 import java.util.Optional;
 
 @RunWith(AndroidJUnit4.class)
@@ -90,6 +98,11 @@
     // TODO: scan directory-vs-files and confirm identical results
 
     private static final String TAG = "ModernMediaScannerTest";
+    /**
+     * Number of times we should repeat an operation to get an average/max.
+     */
+    private static final int COUNT_REPEAT = 5;
+
     private File mDir;
 
     private Context mIsolatedContext;
@@ -102,7 +115,8 @@
         final Context context = InstrumentationRegistry.getTargetContext();
         InstrumentationRegistry.getInstrumentation().getUiAutomation()
                 .adoptShellPermissionIdentity(Manifest.permission.LOG_COMPAT_CHANGE,
-                        Manifest.permission.READ_COMPAT_CHANGE_CONFIG);
+                        Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
+                        Manifest.permission.INTERACT_ACROSS_USERS);
 
         mDir = new File(context.getExternalMediaDirs()[0], "test_" + System.nanoTime());
         mDir.mkdirs();
@@ -136,21 +150,10 @@
         assertTrue(parseOptionalMimeType("image/png", "image/x-shiny").isPresent());
         assertEquals("image/x-shiny",
                 parseOptionalMimeType("image/png", "image/x-shiny").get());
-    }
 
-    @Test
-    public void testOverrideMimeType_148316354() throws Exception {
         // Radical file type shifting isn't allowed
         assertEquals(Optional.empty(),
                 parseOptionalMimeType("video/mp4", "audio/mpeg"));
-
-        // One specific narrow type of shift (mp4 -> m4a) is allowed
-        assertEquals(Optional.of("audio/mp4"),
-                parseOptionalMimeType("video/mp4", "audio/mp4"));
-
-        // The other direction isn't allowed
-        assertEquals(Optional.empty(),
-                parseOptionalMimeType("audio/mp4", "video/mp4"));
     }
 
     @Test
@@ -228,7 +231,12 @@
 
     @Test
     public void testParseOptionalDate() throws Exception {
-        assertEquals(1577836800000L, (long) parseOptionalDate("20200101T000000").get());
+        assertThat(parseOptionalDate("20200101T000000")).isEqualTo(Optional.of(1577836800000L));
+        assertThat(parseOptionalDate("20200101T211205")).isEqualTo(Optional.of(1577913125000L));
+        assertThat(parseOptionalDate("20200101T211205.000Z"))
+                .isEqualTo(Optional.of(1577913125000L));
+        assertThat(parseOptionalDate("20200101T211205.123Z"))
+                .isEqualTo(Optional.of(1577913125123L));
     }
 
     @Test
@@ -383,15 +391,11 @@
     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"));
 
@@ -407,6 +411,9 @@
                     new File(prefix + "/Movies/.thumbnails/meow"));
             assertShouldScanPathAndIsPathHidden(false, false,
                     new File(prefix + "/Music/.thumbnails/meow"));
+
+            assertShouldScanPathAndIsPathHidden(false, false,
+                    new File(prefix + "/.transforms/transcode"));
         }
     }
 
@@ -457,26 +464,24 @@
     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"));
         }
     }
 
@@ -492,9 +497,7 @@
     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"));
@@ -545,104 +548,6 @@
     }
 
     @Test
-    public void testPlaylistM3u() throws Exception {
-        doPlaylist(R.raw.test_m3u, "test.m3u");
-    }
-
-    @Test
-    public void testPlaylistPls() throws Exception {
-        doPlaylist(R.raw.test_pls, "test.pls");
-    }
-
-    @Test
-    public void testPlaylistWpl() throws Exception {
-        doPlaylist(R.raw.test_wpl, "test.wpl");
-    }
-
-    @Test
-    public void testPlaylistXspf() throws Exception {
-        doPlaylist(R.raw.test_xspf, "test.xspf");
-    }
-
-    private void doPlaylist(int res, String name) throws Exception {
-        final File music = new File(mDir, "Music");
-        music.mkdirs();
-        stage(R.raw.test_audio, new File(music, "001.mp3"));
-        stage(R.raw.test_audio, new File(music, "002.mp3"));
-        stage(R.raw.test_audio, new File(music, "003.mp3"));
-        stage(R.raw.test_audio, new File(music, "004.mp3"));
-        stage(R.raw.test_audio, new File(music, "005.mp3"));
-        stage(res, new File(music, name));
-
-        mModern.scanDirectory(mDir, REASON_UNKNOWN);
-
-        // We should see a new playlist with all three items as members
-        final long playlistId;
-        try (Cursor cursor = mIsolatedContext.getContentResolver().query(
-                MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL),
-                new String[] { FileColumns._ID },
-                FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_PLAYLIST, null, null)) {
-            assertTrue(cursor.moveToFirst());
-            playlistId = cursor.getLong(0);
-        }
-
-        final Uri membersUri = MediaStore.Audio.Playlists.Members
-                .getContentUri(MediaStore.VOLUME_EXTERNAL, playlistId);
-        try (Cursor cursor = mIsolatedResolver.query(membersUri, new String[] {
-                MediaColumns.DISPLAY_NAME
-        }, null, null, MediaStore.Audio.Playlists.Members.PLAY_ORDER + " ASC")) {
-            assertEquals(5, cursor.getCount());
-            cursor.moveToNext();
-            assertEquals("001.mp3", cursor.getString(0));
-            cursor.moveToNext();
-            assertEquals("002.mp3", cursor.getString(0));
-            cursor.moveToNext();
-            assertEquals("003.mp3", cursor.getString(0));
-            cursor.moveToNext();
-            assertEquals("004.mp3", cursor.getString(0));
-            cursor.moveToNext();
-            assertEquals("005.mp3", cursor.getString(0));
-        }
-
-        // Delete one of the media files and rescan
-        new File(music, "002.mp3").delete();
-        new File(music, name).setLastModified(10L);
-        mModern.scanDirectory(mDir, REASON_UNKNOWN);
-
-        try (Cursor cursor = mIsolatedResolver.query(membersUri, new String[] {
-                MediaColumns.DISPLAY_NAME
-        }, null, null, MediaStore.Audio.Playlists.Members.PLAY_ORDER + " ASC")) {
-            assertEquals(4, cursor.getCount());
-            cursor.moveToNext();
-            assertEquals("001.mp3", cursor.getString(0));
-            cursor.moveToNext();
-            assertEquals("003.mp3", cursor.getString(0));
-        }
-
-        // Replace media file in a completely different location, which normally
-        // wouldn't match the exact playlist path, but we're willing to perform
-        // a relaxed search
-        final File soundtracks = new File(mDir, "Soundtracks");
-        soundtracks.mkdirs();
-        stage(R.raw.test_audio, new File(soundtracks, "002.mp3"));
-        stage(res, new File(music, name));
-
-        mModern.scanDirectory(mDir, REASON_UNKNOWN);
-
-        try (Cursor cursor = mIsolatedResolver.query(membersUri, new String[] {
-                MediaColumns.DISPLAY_NAME
-        }, null, null, MediaStore.Audio.Playlists.Members.PLAY_ORDER + " ASC")) {
-            assertEquals(5, cursor.getCount());
-            cursor.moveToNext();
-            assertEquals("001.mp3", cursor.getString(0));
-            cursor.moveToNext();
-            assertEquals("002.mp3", cursor.getString(0));
-            cursor.moveToNext();
-            assertEquals("003.mp3", cursor.getString(0));
-        }
-    }
-
-    @Test
     public void testFilter() throws Exception {
         final File music = new File(mDir, "Music");
         music.mkdirs();
@@ -735,13 +640,24 @@
     }
 
     @Test
+    public void testScan_missingDir() throws Exception {
+        File newDir = new File(mDir, "new-dir");
+        // Below shouldn't crash
+        mModern.scanDirectory(newDir, REASON_UNKNOWN);
+
+        newDir = new File(Environment.getStorageDirectory(), "new-dir");
+        // Below shouldn't crash
+        mModern.scanDirectory(newDir, REASON_UNKNOWN);
+    }
+
+    @Test
     public void testScan_Nomedia_Dir() throws Exception {
-        final File red = new File(mDir, "red");
-        final File blue = new File(mDir, "blue");
-        red.mkdirs();
-        blue.mkdirs();
-        stage(R.raw.test_image, new File(red, "red.jpg"));
-        stage(R.raw.test_image, new File(blue, "blue.jpg"));
+        final File redDir = new File(mDir, "red");
+        final File blueDir = new File(mDir, "blue");
+        redDir.mkdirs();
+        blueDir.mkdirs();
+        stage(R.raw.test_image, new File(redDir, "red.jpg"));
+        stage(R.raw.test_image, new File(blueDir, "blue.jpg"));
 
         mModern.scanDirectory(mDir, REASON_UNKNOWN);
 
@@ -749,7 +665,7 @@
         assertQueryCount(2, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
 
         // Hide one directory, rescan, and confirm hidden
-        final File redNomedia = new File(red, ".nomedia");
+        final File redNomedia = new File(redDir, ".nomedia");
         redNomedia.createNewFile();
         mModern.scanDirectory(mDir, REASON_UNKNOWN);
         assertQueryCount(1, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
@@ -761,6 +677,35 @@
     }
 
     @Test
+    public void testScan_MaxExcludeNomediaDirs_DoesNotThrowException() throws Exception {
+        // Create MAX_EXCLUDE_DIRS + 50 nomedia dirs in mDir
+        // (Need to add 50 as MAX_EXCLUDE_DIRS is a safe limit;
+        // 499 would have been too close to the exception limit)
+        // Mark them as non-dirty so that they are excluded from scans
+        for (int i = 0 ; i < (MAX_EXCLUDE_DIRS + 50) ; i++) {
+            createCleanNomediaDir(mDir);
+        }
+
+        final File redDir = new File(mDir, "red");
+        redDir.mkdirs();
+        stage(R.raw.test_image, new File(redDir, "red.jpg"));
+
+        assertQueryCount(0, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
+        mModern.scanDirectory(mDir, REASON_UNKNOWN);
+        assertQueryCount(1, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
+    }
+
+    private void createCleanNomediaDir(File dir) throws Exception {
+        final File nomediaDir = new File(dir, "test_" + System.nanoTime());
+        nomediaDir.mkdirs();
+        final File nomedia = new File(nomediaDir, ".nomedia");
+        nomedia.createNewFile();
+
+        FileUtils.setDirectoryDirty(nomediaDir, false);
+        assertThat(FileUtils.isDirectoryDirty(nomediaDir)).isFalse();
+    }
+
+    @Test
     public void testScan_Nomedia_File() throws Exception {
         final File image = new File(mDir, "image.jpg");
         final File nomedia = new File(mDir, ".nomedia");
@@ -783,6 +728,15 @@
     }
 
     @Test
+    public void testScan_missingFile() throws Exception {
+        File image = new File(mDir, "image.jpg");
+        assertThat(mModern.scanFile(image, REASON_UNKNOWN)).isNull();
+
+        image = new File(Environment.getStorageDirectory(), "image.jpg");
+        assertThat(mModern.scanFile(image, REASON_UNKNOWN)).isNull();
+    }
+
+    @Test
     public void testScanFileAndUpdateOwnerPackageName() throws Exception {
         final File image = new File(mDir, "image.jpg");
         final String thisPackageName = InstrumentationRegistry.getContext().getPackageName();
@@ -847,6 +801,26 @@
         }
     }
 
+    @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.
@@ -861,8 +835,34 @@
                 .query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, null)) {
             assertEquals(1, cursor.getCount());
             cursor.moveToFirst();
-            assertEquals("audio/mp4",
-                    cursor.getString(cursor.getColumnIndex(MediaColumns.MIME_TYPE)));
+            assertThat(cursor.getString(cursor.getColumnIndex(MediaColumns.MIME_TYPE)))
+                    .isEqualTo("audio/mp4");
+            assertThat(cursor.getString(cursor.getColumnIndex(AudioColumns.IS_MUSIC)))
+                    .isEqualTo("1");
+
+        }
+    }
+
+    @Test
+    public void testScan_audioMp4_notRescanIfUnchanged() throws Exception {
+        final File file = new File(mDir, "176522651.m4a");
+        stage(R.raw.test_m4a, file);
+
+        // We trigger a scan twice, but we expect the second scan to be skipped since there were
+        // no changes.
+        mModern.scanFile(file, REASON_UNKNOWN);
+        mModern.scanFile(file, REASON_UNKNOWN);
+
+        try (Cursor cursor =
+                     mIsolatedResolver.query(
+                             MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+                             null, null, null, null)) {
+            assertThat(cursor.getCount()).isEqualTo(1);
+            cursor.moveToFirst();
+            String added = cursor.getString(cursor.getColumnIndex(MediaColumns.GENERATION_ADDED));
+            String modified =
+                    cursor.getString(cursor.getColumnIndex(MediaColumns.GENERATION_MODIFIED));
+            assertThat(modified).isEqualTo(added);
         }
     }
 
@@ -925,6 +925,227 @@
         }
     }
 
+    @Test
+    public void testScan_BitmapFile() throws Exception {
+        final File bmp = new File(mDir, "image.bmp");
+        stage(R.raw.test_bmp, bmp);
+
+        final Uri uri = mModern.scanFile(bmp, REASON_UNKNOWN);
+        try (Cursor cursor = mIsolatedResolver
+                .query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null)) {
+            assertEquals(1, cursor.getCount());
+            cursor.moveToFirst();
+            assertEquals(1280,
+                    cursor.getInt(cursor.getColumnIndex(MediaColumns.WIDTH)));
+            assertEquals(720,
+                    cursor.getInt(cursor.getColumnIndex(MediaColumns.HEIGHT)));
+        }
+    }
+
+    @Test
+    public void testScan_deleteStaleRowWithExpiredPendingFile() throws Exception {
+        final String displayName = "audio.mp3";
+        final long dateExpires = (System.currentTimeMillis() - 5 * DateUtils.DAY_IN_MILLIS) / 1000;
+        final String expiredName = String.format(
+                Locale.US, ".%s-%d-%s", FileUtils.PREFIX_PENDING, dateExpires, displayName);
+        final File audio = new File(mDir, expiredName);
+        stage(R.raw.test_audio, audio);
+
+        // files should exist
+        assertThat(audio.exists()).isTrue();
+
+        // scan file, row is added
+        mModern.scanFile(audio, REASON_UNKNOWN);
+        final Bundle queryArgs = new Bundle();
+        queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_PENDING, MediaStore.MATCH_INCLUDE);
+
+        try (Cursor cursor = mIsolatedResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+                null, queryArgs, null)) {
+            assertThat(cursor.getCount()).isEqualTo(1);
+        }
+
+        // Delete the pending file to make the row is stale
+        executeShellCommand("rm " + audio.getAbsolutePath());
+        assertThat(audio.exists()).isFalse();
+
+        // the row still exists
+        try (Cursor cursor = mIsolatedResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+                null, queryArgs, null)) {
+            assertThat(cursor.getCount()).isEqualTo(1);
+        }
+
+        mModern.scanFile(audio, REASON_UNKNOWN);
+
+        // ScanFile above deleted stale expired pending row, hence we shouldn't see
+        // the pending row in query result
+        try (Cursor cursor = mIsolatedResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+                null, queryArgs, null)) {
+            assertThat(cursor.getCount()).isEqualTo(0);
+        }
+    }
+
+    @Test
+    public void testScan_keepStaleRowWithNonExpiredPendingFile() throws Exception {
+        final String displayName = "audio.mp3";
+        final long dateExpires = (System.currentTimeMillis() + 2 * DateUtils.DAY_IN_MILLIS) / 1000;
+        final String expiredName = String.format(
+                Locale.US, ".%s-%d-%s", FileUtils.PREFIX_PENDING, dateExpires, displayName);
+        final File audio = new File(mDir, expiredName);
+        stage(R.raw.test_audio, audio);
+
+        // file should exist
+        assertThat(audio.exists()).isTrue();
+
+        // scan file, row is added
+        mModern.scanFile(audio, REASON_UNKNOWN);
+        final Bundle queryArgs = new Bundle();
+        queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_PENDING, MediaStore.MATCH_INCLUDE);
+
+        try (Cursor cursor = mIsolatedResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+                null, queryArgs, null)) {
+            assertThat(cursor.getCount()).isEqualTo(1);
+        }
+
+        // Delete the pending file to make the row is stale
+        executeShellCommand("rm " + audio.getAbsolutePath());
+        assertThat(audio.exists()).isFalse();
+
+        // the row still exists
+        try (Cursor cursor = mIsolatedResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+                null, queryArgs, null)) {
+            assertThat(cursor.getCount()).isEqualTo(1);
+        }
+
+        mModern.scanFile(audio, REASON_UNKNOWN);
+
+        // ScanFile above didn't delete stale pending row which is not expired, hence
+        // we still see the pending row in query result
+        try (Cursor cursor = mIsolatedResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+                null, queryArgs, null)) {
+            assertThat(cursor.getCount()).isEqualTo(1);
+        }
+    }
+
+    @Test
+    public void testScan_deleteStaleRowWithExpiredTrashedFile() throws Exception {
+        final String displayName = "audio.mp3";
+        final long dateExpires = (System.currentTimeMillis() - 5 * DateUtils.DAY_IN_MILLIS) / 1000;
+        final String expiredName = String.format(
+                Locale.US, ".%s-%d-%s", FileUtils.PREFIX_TRASHED, dateExpires, displayName);
+        final File audio = new File(mDir, expiredName);
+        stage(R.raw.test_audio, audio);
+
+        // file should exist
+        assertThat(audio.exists()).isTrue();
+
+        // scan file, row is added
+        mModern.scanFile(audio, REASON_UNKNOWN);
+        final Bundle queryArgs = new Bundle();
+        queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_TRASHED, MediaStore.MATCH_INCLUDE);
+
+        try (Cursor cursor = mIsolatedResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+                null, queryArgs, null)) {
+            assertThat(cursor.getCount()).isEqualTo(1);
+        }
+
+        // Delete the trashed file to make the row is stale
+        executeShellCommand("rm " + audio.getAbsolutePath());
+        assertThat(audio.exists()).isFalse();
+
+        // the row still exists
+        try (Cursor cursor = mIsolatedResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+                null, queryArgs, null)) {
+            assertThat(cursor.getCount()).isEqualTo(1);
+        }
+
+        mModern.scanFile(audio, REASON_UNKNOWN);
+
+        // ScanFile above deleted stale expired trashed row, hence we shouldn't see
+        // the trashed row in query result
+        try (Cursor cursor = mIsolatedResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+                null, queryArgs, null)) {
+            assertThat(cursor.getCount()).isEqualTo(0);
+        }
+    }
+
+    @Test
+    public void testScan_deleteStaleRowWithNonExpiredTrashedFile() throws Exception {
+        final String displayName = "audio.mp3";
+        final long dateExpires = (System.currentTimeMillis() + 2 * DateUtils.DAY_IN_MILLIS) / 1000;
+        final String expiredName = String.format(
+                Locale.US, ".%s-%d-%s", FileUtils.PREFIX_TRASHED, dateExpires, displayName);
+        final File audio = new File(mDir, expiredName);
+        stage(R.raw.test_audio, audio);
+
+        // file should exist
+        assertThat(audio.exists()).isTrue();
+
+        // scan file, row is added
+        mModern.scanFile(audio, REASON_UNKNOWN);
+        final Bundle queryArgs = new Bundle();
+        queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_TRASHED, MediaStore.MATCH_INCLUDE);
+
+        try (Cursor cursor = mIsolatedResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+                null, queryArgs, null)) {
+            assertThat(cursor.getCount()).isEqualTo(1);
+        }
+
+        // Delete the trashed file to make the row is stale
+        executeShellCommand("rm " + audio.getAbsolutePath());
+        assertThat(audio.exists()).isFalse();
+
+        // the row still exists
+        try (Cursor cursor = mIsolatedResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+                null, queryArgs, null)) {
+            assertThat(cursor.getCount()).isEqualTo(1);
+        }
+
+        mModern.scanFile(audio, REASON_UNKNOWN);
+
+        // ScanFile above deleted stale trashed row that is not expired, hence we
+        // shouldn't see the trashed row in query result
+        try (Cursor cursor = mIsolatedResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+                null, queryArgs, null)) {
+            assertThat(cursor.getCount()).isEqualTo(0);
+        }
+    }
+
+    @Test
+    public void testScan_deleteStaleRow() throws Exception {
+        final String displayName = "audio.mp3";
+        final File audio = new File(mDir, displayName);
+        stage(R.raw.test_audio, audio);
+
+        // file should exist
+        assertThat(audio.exists()).isTrue();
+
+        // scan file, row is added
+        mModern.scanFile(audio, REASON_UNKNOWN);
+
+        try (Cursor cursor = mIsolatedResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+                null, null, null)) {
+            assertThat(cursor.getCount()).isEqualTo(1);
+        }
+
+        // Delete the file to make the row is stale
+        executeShellCommand("rm " + audio.getAbsolutePath());
+        assertThat(audio.exists()).isFalse();
+
+        // the row still exists
+        try (Cursor cursor = mIsolatedResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+                null, null, null)) {
+            assertThat(cursor.getCount()).isEqualTo(1);
+        }
+
+        mModern.scanFile(audio, REASON_UNKNOWN);
+
+        // ScanFile above deleted stale row, hence we shouldn't see the row in query result
+        try (Cursor cursor = mIsolatedResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+                null, null, null)) {
+            assertThat(cursor.getCount()).isEqualTo(0);
+        }
+    }
+
     /**
      * Executes a shell command.
      */
@@ -949,4 +1170,92 @@
             return new String(ByteStreams.toByteArray(output));
         }
     }
+
+    @Test
+    public void testScan_largeXmpData() throws Exception {
+        final File image = new File(mDir, "large_xmp.mp4");
+        stage(R.raw.large_xmp, image);
+        assertTrue(image.exists());
+
+        mModern.scanDirectory(mDir, REASON_UNKNOWN);
+
+        try (Cursor cursor = mIsolatedResolver.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
+                new String[] { MediaColumns.XMP }, null, null, null)) {
+            assertEquals(1, cursor.getCount());
+            cursor.moveToFirst();
+            assertEquals(0, cursor.getBlob(0).length);
+        }
+    }
+
+    @Test
+    public void testNoOpScan_NoMediaDirs() throws Exception {
+        File nomedia = new File(mDir, ".nomedia");
+        assertThat(nomedia.createNewFile()).isTrue();
+        for (int i = 0; i < 100; i++) {
+            File file = new File(mDir, "file_" + System.nanoTime());
+            assertThat(file.createNewFile()).isTrue();
+        }
+        Timer firstDirScan = new Timer("firstDirScan");
+        firstDirScan.start();
+        // Time taken : preVisitDirectory + 100 visitFiles
+        mModern.scanDirectory(mDir, REASON_UNKNOWN);
+        firstDirScan.stop();
+        firstDirScan.dumpResults();
+
+        // Time taken : preVisitDirectory
+        Timer noOpDirScan = new Timer("noOpDirScan1");
+        for (int i = 0; i < COUNT_REPEAT; i++) {
+            noOpDirScan.start();
+            mModern.scanDirectory(mDir, REASON_UNKNOWN);
+            noOpDirScan.stop();
+        }
+        noOpDirScan.dumpResults();
+        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();
+
+        // The dir should not be dirty and subsequest scans should not scan the entire directory.
+        // 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(
+                firstDirScan.getMaxDurationMillis());
+    }
+
+    @Test
+    public void testScan_TrackNumber() throws Exception {
+        final File music = new File(mDir, "Music");
+        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(2, cursor.getInt(cursor.getColumnIndex(AudioColumns.TRACK)));
+        }
+
+        stage(R.raw.test_audio_empty_track_number, 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();
+            assertThat(cursor.getString(cursor.getColumnIndex(AudioColumns.TRACK))).isNull();
+        }
+    }
 }
diff --git a/tests/src/com/android/providers/media/scan/NullMediaScannerTest.java b/tests/src/com/android/providers/media/scan/NullMediaScannerTest.java
index 44528ba..063f1b7 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(MediaStore.VOLUME_EXTERNAL_PRIMARY);
+        scanner.onDetachVolume(null);
     }
 }
diff --git a/tests/src/com/android/providers/media/util/ExifUtilsTest.java b/tests/src/com/android/providers/media/util/ExifUtilsTest.java
index b712d51..6af0eac 100644
--- a/tests/src/com/android/providers/media/util/ExifUtilsTest.java
+++ b/tests/src/com/android/providers/media/util/ExifUtilsTest.java
@@ -103,5 +103,17 @@
         }
         return true;
     }
+
+    @Test
+    public void testSubSeconds() throws Exception {
+        assertEquals(0L, ExifUtils.parseSubSeconds("0"));
+        assertEquals(100L, ExifUtils.parseSubSeconds("1"));
+        assertEquals(10L, ExifUtils.parseSubSeconds("01"));
+        assertEquals(120L, ExifUtils.parseSubSeconds("12"));
+        assertEquals(123L, ExifUtils.parseSubSeconds("123"));
+        assertEquals(123L, ExifUtils.parseSubSeconds("1234"));
+        assertEquals(12L, ExifUtils.parseSubSeconds("01234"));
+    }
+
 }
 
diff --git a/tests/src/com/android/providers/media/util/FileUtilsTest.java b/tests/src/com/android/providers/media/util/FileUtilsTest.java
index edec1a2..c31a6db 100644
--- a/tests/src/com/android/providers/media/util/FileUtilsTest.java
+++ b/tests/src/com/android/providers/media/util/FileUtilsTest.java
@@ -44,6 +44,7 @@
 import static com.android.providers.media.util.FileUtils.extractTopLevelDir;
 import static com.android.providers.media.util.FileUtils.extractVolumeName;
 import static com.android.providers.media.util.FileUtils.extractVolumePath;
+import static com.android.providers.media.util.FileUtils.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;
@@ -57,8 +58,11 @@
 import static org.junit.Assert.fail;
 
 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;
@@ -67,6 +71,7 @@
 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;
@@ -74,15 +79,32 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.RandomAccessFile;
 import java.util.Arrays;
 import java.util.HashSet;
+import java.util.Locale;
 import java.util.Optional;
 
 @RunWith(AndroidJUnit4.class)
 public class FileUtilsTest {
+    // Exposing here since it is also used by MediaProviderTest.java
+    public static final int MAX_FILENAME_BYTES = FileUtils.MAX_FILENAME_BYTES;
+
+    /**
+     * 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 TEST_DIRECTORY_NAME = "FileUtilsTestDirectory" + NONCE;
+    private static final String TEST_FILE_NAME = "FileUtilsTestFile" + NONCE;
+
     private File mTarget;
     private File mDcimTarget;
     private File mDeleteTarget;
+    private File mDownloadTarget;
+    private File mTestDownloadDir;
 
     @Before
     public void setUp() throws Exception {
@@ -93,11 +115,17 @@
         mDcimTarget.mkdirs();
 
         mDeleteTarget = mDcimTarget;
+
+        mDownloadTarget = new File(Environment.getExternalStorageDirectory(),
+                Environment.DIRECTORY_DOWNLOADS);
+        mTestDownloadDir = new File(mDownloadTarget, TEST_DIRECTORY_NAME);
+        mTestDownloadDir.mkdirs();
     }
 
     @After
     public void tearDown() throws Exception {
         FileUtils.deleteContents(mTarget);
+        FileUtils.deleteContents(mTestDownloadDir);
     }
 
     private void touch(String name, long age) throws Exception {
@@ -121,6 +149,17 @@
         // Verify empty writing deletes file
         FileUtils.writeString(file, Optional.empty());
         assertFalse(FileUtils.readString(file).isPresent());
+
+        // Verify reading from a file with more than 4096 chars
+        try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) {
+            raf.setLength(4097);
+        }
+        assertEquals(Optional.empty(), FileUtils.readString(file));
+
+        // Verify reading from non existing file.
+        file.delete();
+        assertEquals(Optional.empty(), FileUtils.readString(file));
+
     }
 
     @Test
@@ -407,7 +446,25 @@
     }
 
     /**
-     * Verify that we generate unique filenames that look sane compared to other
+     * Verify that we generate unique filenames that meet the JEITA DCF
+     * specification when writing into directories like {@code DCIM}.
+     *
+     * See b/174120008 for context.
+     */
+    @Test
+    public void testBuildUniqueFile_DCF_strict_differentLocale() throws Exception {
+        Locale defaultLocale = Locale.getDefault();
+        try {
+            Locale.setDefault(new Locale("ar", "SA"));
+            testBuildUniqueFile_DCF_strict();
+        }
+        finally {
+            Locale.setDefault(defaultLocale);
+        }
+    }
+
+    /**
+     * Verify that we generate unique filenames that look valid compared to other
      * {@code DCIM} filenames. These technically aren't part of the official
      * JEITA DCF specification.
      */
@@ -424,6 +481,31 @@
                 buildUniqueFile(mDcimTarget, "IMG_20190102_030405~2.jpg"));
     }
 
+    /**
+     * Verify that we generate unique filenames that look valid compared to other
+     * {@code DCIM} filenames. These technically aren't part of the official
+     * JEITA DCF specification.
+     *
+     * See b/174120008 for context.
+     */
+    @Test
+    public void testBuildUniqueFile_DCF_relaxed_differentLocale() throws Exception {
+        Locale defaultLocale = Locale.getDefault();
+        try {
+            Locale.setDefault(new Locale("ar", "SA"));
+            testBuildUniqueFile_DCF_relaxed();
+        } finally {
+            Locale.setDefault(defaultLocale);
+        }
+    }
+
+    @Test
+    public void testGetAbsoluteExtendedPath() throws Exception {
+        assertEquals("/storage/emulated/0/DCIM/.trashed-1888888888-test.jpg",
+                FileUtils.getAbsoluteExtendedPath(
+                        "/storage/emulated/0/DCIM/.trashed-1621147340-test.jpg", 1888888888));
+    }
+
     @Test
     public void testExtractVolumePath() throws Exception {
         assertEquals("/storage/emulated/0/",
@@ -471,6 +553,62 @@
     }
 
     @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",
@@ -643,6 +781,81 @@
         assertNull(values.get(MediaColumns.DATE_EXPIRES));
     }
 
+    @Test
+    public void testComputeDataFromValues_Trashed_trimFileName() throws Exception {
+        testComputeDataFromValues_withAction_trimFileName(MediaColumns.IS_TRASHED);
+    }
+
+    @Test
+    public void testComputeDataFromValues_Pending_trimFileName() throws Exception {
+        testComputeDataFromValues_withAction_trimFileName(MediaColumns.IS_PENDING);
+    }
+
+    @Test
+    public void testGetTopLevelNoMedia_CurrentDir() throws Exception {
+        File dirInDownload = getNewDirInDownload("testGetTopLevelNoMedia_CurrentDir");
+        File nomedia = new File(dirInDownload, ".nomedia");
+        assertTrue(nomedia.createNewFile());
+
+        assertEquals(dirInDownload, FileUtils.getTopLevelNoMedia(new File(dirInDownload, "foo")));
+    }
+
+    @Test
+    public void testGetTopLevelNoMedia_TopDir() throws Exception {
+        File topDirInDownload = getNewDirInDownload("testGetTopLevelNoMedia_TopDir");
+        File topNomedia = new File(topDirInDownload, ".nomedia");
+        assertTrue(topNomedia.createNewFile());
+
+        File dirInTopDirInDownload = new File(topDirInDownload, "foo");
+        assertTrue(dirInTopDirInDownload.mkdirs());
+        File nomedia = new File(dirInTopDirInDownload, ".nomedia");
+        assertTrue(nomedia.createNewFile());
+
+        assertEquals(topDirInDownload,
+                FileUtils.getTopLevelNoMedia(new File(dirInTopDirInDownload, "foo")));
+    }
+
+    @Test
+    public void testGetTopLevelNoMedia_NoDir() throws Exception {
+        File topDirInDownload = getNewDirInDownload("testGetTopLevelNoMedia_NoDir");
+        File dirInTopDirInDownload = new File(topDirInDownload, "foo");
+        assertTrue(dirInTopDirInDownload.mkdirs());
+
+        assertEquals(null,
+                FileUtils.getTopLevelNoMedia(new File(dirInTopDirInDownload, "foo")));
+    }
+
+    @Test
+    public void testDirectoryDirty() throws Exception {
+        File dirInDownload = getNewDirInDownload("testDirectoryDirty");
+
+        // All directories are considered dirty, unless hidden
+        assertTrue(FileUtils.isDirectoryDirty(dirInDownload));
+
+        // Marking a directory as clean has no effect without a .nomedia file
+        FileUtils.setDirectoryDirty(dirInDownload, false);
+        assertTrue(FileUtils.isDirectoryDirty(dirInDownload));
+
+        // Creating an empty .nomedia file still keeps a directory dirty
+        File nomedia = new File(dirInDownload, ".nomedia");
+        assertTrue(nomedia.createNewFile());
+        assertTrue(FileUtils.isDirectoryDirty(dirInDownload));
+
+        // Marking as clean with a .nomedia file works
+        FileUtils.setDirectoryDirty(dirInDownload, false);
+        assertFalse(FileUtils.isDirectoryDirty(dirInDownload));
+
+        // Marking as dirty with a .nomedia file works
+        FileUtils.setDirectoryDirty(dirInDownload, true);
+        assertTrue(FileUtils.isDirectoryDirty(dirInDownload));
+    }
+
+    private File getNewDirInDownload(String name) {
+        File file = new File(mTestDownloadDir, name);
+        assertTrue(file.mkdir());
+        return file;
+    }
+
     private static File touch(File dir, String name) throws IOException {
         final File res = new File(dir, name);
         res.createNewFile();
@@ -665,4 +878,45 @@
             assertTrue("Unexpected actual file " + actualFile, expectedSet.contains(actualFile));
         }
     }
+
+    public static String createExtremeFileName(String prefix, String extension) {
+        // create extreme long file name
+        final int prefixLength = prefix.length();
+        final int extensionLength = extension.length();
+        StringBuilder str = new StringBuilder(prefix);
+        for (int i = 0; i < (MAX_FILENAME_BYTES - prefixLength - extensionLength); i++) {
+            str.append(i % 10);
+        }
+        return str.append(extension).toString();
+    }
+
+    private void testComputeDataFromValues_withAction_trimFileName(String columnKey) {
+        final String originalName = createExtremeFileName("test", ".jpg");
+        final String volumePath = "/storage/emulated/0/";
+        final ContentValues values = new ContentValues();
+        values.put(columnKey, 1);
+        values.put(MediaColumns.RELATIVE_PATH, "DCIM/My Vacation/");
+        values.put(MediaColumns.DATE_EXPIRES, 1577836800L);
+        values.put(MediaColumns.DISPLAY_NAME, originalName);
+
+        FileUtils.computeDataFromValues(values, new File(volumePath), false /* isForFuse */);
+
+        final String data = values.getAsString(MediaColumns.DATA);
+        final String result = FileUtils.extractDisplayName(data);
+        // after adding the prefix .pending-timestamp or .trashed-timestamp,
+        // the largest length of the file name is MAX_FILENAME_BYTES 255
+        Truth.assertThat(result.length()).isAtMost(MAX_FILENAME_BYTES);
+        Truth.assertThat(result).isNotEqualTo(originalName);
+    }
+
+    @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"));
+        }
+    }
 }
diff --git a/tests/src/com/android/providers/media/util/MetricsTest.java b/tests/src/com/android/providers/media/util/MetricsTest.java
index 41e25b5..264cb1b 100644
--- a/tests/src/com/android/providers/media/util/MetricsTest.java
+++ b/tests/src/com/android/providers/media/util/MetricsTest.java
@@ -41,10 +41,11 @@
         final String packageName = "com.example";
 
         Metrics.logScan(volumeName, MediaScanner.REASON_UNKNOWN, 42, 42, 42, 42, 42);
-        Metrics.logDeletion(volumeName, 42, packageName, 42);
+        Metrics.logDeletionPersistent(volumeName, "scanReason", new int[] { 42 });
+        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);
+        Metrics.logSchemaChange(volumeName, 42, 42, 42, 42, "UUID");
         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 1eba379..c434bd2 100644
--- a/tests/src/com/android/providers/media/util/PermissionUtilsTest.java
+++ b/tests/src/com/android/providers/media/util/PermissionUtilsTest.java
@@ -16,8 +16,29 @@
 
 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;
@@ -29,20 +50,49 @@
 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 org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertEquals;
+
+import android.app.AppOpsManager;
 import android.content.Context;
+import android.content.Intent;
 
-import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SdkSuppress;
 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();
@@ -53,26 +103,30 @@
      * we expect to be holding.
      */
     @Test
-    public void testSelfPermissions() throws Exception {
-        final Context context = InstrumentationRegistry.getContext();
+    public void testSelfPermissions() {
+        final Context context = getContext();
         final int pid = android.os.Process.myPid();
         final int uid = android.os.Process.myUid();
         final String packageName = context.getPackageName();
 
-        assertTrue(checkPermissionSelf(context, pid, uid));
-        assertFalse(checkPermissionShell(context, pid, uid));
-        assertFalse(checkPermissionManager(context, pid, uid, packageName, null));
-        assertFalse(checkPermissionDelegator(context, pid, uid));
+        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(checkPermissionReadStorage(context, pid, uid, packageName, null));
-        assertTrue(checkPermissionWriteStorage(context, pid, uid, packageName, null));
+        assertThat(checkPermissionReadStorage(context, pid, uid, packageName, null)).isTrue();
+        assertThat(checkPermissionWriteStorage(context, pid, uid, packageName, null)).isTrue();
 
-        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));
+        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();
     }
 
     /**
@@ -80,9 +134,372 @@
      */
     @Test
     public void testNoIsolatedStorageIsByDefaultDenied() throws Exception {
-        final Context context = InstrumentationRegistry.getContext();
+        final Context context = getContext();
         final int uid = android.os.Process.myUid();
         final String packageName = context.getPackageName();
-        assertFalse(checkNoIsolatedStorageGranted(context, uid, packageName, null));
+        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));
     }
 }
diff --git a/tests/src/com/android/providers/media/util/SQLiteQueryBuilderTest.java b/tests/src/com/android/providers/media/util/SQLiteQueryBuilderTest.java
index c8fe3bb..2a01d61 100644
--- a/tests/src/com/android/providers/media/util/SQLiteQueryBuilderTest.java
+++ b/tests/src/com/android/providers/media/util/SQLiteQueryBuilderTest.java
@@ -16,7 +16,10 @@
 
 package com.android.providers.media.util;
 
+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.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -31,6 +34,7 @@
 import android.os.Build;
 import android.os.CancellationSignal;
 import android.os.OperationCanceledException;
+import android.provider.MediaStore;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
@@ -91,9 +95,9 @@
         sqliteQueryBuilder.appendWhere("age=20");
         String sql = sqliteQueryBuilder.buildQuery(new String[] { "age", "address" },
                 null, null, null, null, null);
-        assertEquals(TEST_TABLE_NAME, sqliteQueryBuilder.getTables());
+        assertThat(sqliteQueryBuilder.getTables()).isEqualTo(TEST_TABLE_NAME);
         expected = "SELECT age, address FROM " + TEST_TABLE_NAME + " WHERE (age=20)";
-        assertEquals(expected, sql);
+        assertThat(sql).isEqualTo(expected);
 
         sqliteQueryBuilder = new SQLiteQueryBuilder();
         sqliteQueryBuilder.setTables(EMPLOYEE_TABLE_NAME);
@@ -101,9 +105,9 @@
         sqliteQueryBuilder.appendWhere("age>32");
         sql = sqliteQueryBuilder.buildQuery(new String[] { "age", "address" },
                 null, null, null, null, null);
-        assertEquals(EMPLOYEE_TABLE_NAME, sqliteQueryBuilder.getTables());
+        assertThat(sqliteQueryBuilder.getTables()).isEqualTo(EMPLOYEE_TABLE_NAME);
         expected = "SELECT DISTINCT age, address FROM " + EMPLOYEE_TABLE_NAME + " WHERE (age>32)";
-        assertEquals(expected, sql);
+        assertThat(sql).isEqualTo(expected);
 
         sqliteQueryBuilder = new SQLiteQueryBuilder();
         sqliteQueryBuilder.setTables(EMPLOYEE_TABLE_NAME);
@@ -111,15 +115,14 @@
         sqliteQueryBuilder.appendWhereEscapeString("age>32");
         sql = sqliteQueryBuilder.buildQuery(new String[] { "age", "address" },
                 null, null, null, null, null);
-        assertEquals(EMPLOYEE_TABLE_NAME, sqliteQueryBuilder.getTables());
+        assertThat(sqliteQueryBuilder.getTables()).isEqualTo(EMPLOYEE_TABLE_NAME);
         expected = "SELECT DISTINCT age, address FROM " + EMPLOYEE_TABLE_NAME
                 + " WHERE ('age>32')";
-        assertEquals(expected, sql);
+        assertThat(sql).isEqualTo(expected);
     }
 
     @Test
     public void testSetProjectionMap() {
-        String expected;
         Map<String, String> projectMap = new HashMap<String, String>();
         projectMap.put("EmployeeName", "name");
         projectMap.put("EmployeeAge", "age");
@@ -130,24 +133,54 @@
         sqliteQueryBuilder.setProjectionMap(projectMap);
         String sql = sqliteQueryBuilder.buildQuery(new String[] { "EmployeeName", "EmployeeAge" },
                 null, null, null, null, null);
-        expected = "SELECT name, age FROM " + TEST_TABLE_NAME;
-        assertEquals(expected, sql);
+        assertThat(sql).isEqualTo("SELECT name, age FROM " + TEST_TABLE_NAME);
 
         sql = sqliteQueryBuilder.buildQuery(null, // projectionIn is null
                 null, null, null, null, null);
-        assertTrue(sql.matches("SELECT (age|name|address), (age|name|address), (age|name|address) "
-                + "FROM " + TEST_TABLE_NAME));
-        assertTrue(sql.contains("age"));
-        assertTrue(sql.contains("name"));
-        assertTrue(sql.contains("address"));
+        assertThat(sql).matches(
+                "SELECT (age|name|address), (age|name|address), (age|name|address) "
+                    + "FROM " + TEST_TABLE_NAME);
+        assertThat(sql).contains("age");
+        assertThat(sql).contains("name");
+        assertThat(sql).contains("address");
 
         sqliteQueryBuilder.setProjectionMap(null);
         sql = sqliteQueryBuilder.buildQuery(new String[] { "name", "address" },
                 null, null, null, null, null);
-        assertTrue(sql.matches("SELECT (name|address), (name|address) "
-                + "FROM " + TEST_TABLE_NAME));
-        assertTrue(sql.contains("name"));
-        assertTrue(sql.contains("address"));
+        assertThat(sql).matches("SELECT (name|address), (name|address) "
+                + "FROM " + TEST_TABLE_NAME);
+        assertThat(sql).contains("name");
+        assertThat(sql).contains("address");
+
+    }
+
+    @Test
+    public void testAllowRowid() {
+        Map<String, String> projectMap = new HashMap<String, String>();
+        projectMap.put("EmployeeName", "name");
+        projectMap.put("EmployeeAge", "age");
+        projectMap.put("EmployeeAddress", "address");
+        SQLiteQueryBuilder sqliteQueryBuilder = new SQLiteQueryBuilder();
+        sqliteQueryBuilder.setTables(TEST_TABLE_NAME);
+        sqliteQueryBuilder.setDistinct(false);
+        sqliteQueryBuilder.setProjectionMap(projectMap);
+
+        sqliteQueryBuilder.allowColumn("rowid");
+
+        String sql = sqliteQueryBuilder.buildQuery(new String[] { SQLiteQueryBuilder.ROWID_COLUMN },
+                null, null, null, null, null);
+        assertThat(sql).isEqualTo("SELECT rowid FROM " + TEST_TABLE_NAME);
+
+        sql = sqliteQueryBuilder.buildQuery(null, // projectionIn is null
+                null, null, null, null, null);
+        assertThat(sql).matches(
+                "SELECT (age|name|address|rowid), (age|name|address|rowid), "
+                    + "(age|name|address|rowid), (age|name|address|rowid) "
+                    + "FROM " + TEST_TABLE_NAME);
+        assertThat(sql).contains("age");
+        assertThat(sql).contains("name");
+        assertThat(sql).contains("address");
+        assertThat(sql).contains("rowid");
     }
 
     private static class MockCursor extends SQLiteCursor {
@@ -173,7 +206,7 @@
                 "HAVING " + DEFAULT_HAVING + " " +
                 "ORDER BY name " +
                 "LIMIT 100";
-        assertEquals(expected, sql);
+        assertThat(sql).isEqualTo(expected);
     }
 
     @Test
@@ -190,7 +223,7 @@
         String expected = "SELECT name, sum(salary) FROM " + TEST_TABLE_NAME
                 + " WHERE (" + DEFAULT_TEST_WHERE + ") " +
                 "GROUP BY name HAVING " + DEFAULT_HAVING + " ORDER BY name LIMIT 2";
-        assertEquals(expected, sql);
+        assertThat(sql).isEqualTo(expected);
     }
 
     @Test
@@ -198,9 +231,9 @@
         StringBuilder sb = new StringBuilder();
         String[] columns = new String[] { "name", "age" };
 
-        assertEquals("", sb.toString());
+        assertThat(sb.toString()).isEmpty();
         SQLiteQueryBuilder.appendColumns(sb, columns);
-        assertEquals("name, age ", sb.toString());
+        assertThat(sb.toString()).isEqualTo("name, age ");
     }
 
     @Test
@@ -212,7 +245,7 @@
         qb.appendWhereStandalone("C");
 
         final String query = qb.buildQuery(null, null, null, null, null, null);
-        assertTrue(query.contains("(A) AND (B) AND (C)"));
+        assertThat(query).contains("(A) AND (B) AND (C)");
     }
 
     @Test
@@ -770,6 +803,66 @@
         builder.enforceStrictGrammar(null, null, null, sortOrder, null);
     }
 
+    @Test
+    public void testShouldAppendRowId_hasIdInValues_notAppendId() {
+        final SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
+        final ContentValues values = new ContentValues();
+        values.put(MediaStore.MediaColumns._ID, "1");
+        values.put(MediaStore.MediaColumns.DATA, "/storage/emulated/0/");
+
+        assertFalse(builder.shouldAppendRowId(values));
+    }
+
+    @Test
+    public void testShouldAppendRowId_noDataInValues_notAppendId() {
+        final SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
+        final ContentValues values = new ContentValues();
+
+        assertFalse(builder.shouldAppendRowId(values));
+    }
+
+    @Test
+    public void testShouldAppendRowId_noIdInProjectionMap_notAppendId() {
+        final SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
+        final ContentValues values = new ContentValues();
+        values.put(MediaStore.MediaColumns.DATA, "/storage/emulated/0/");
+
+        final HashMap<String, String> map = new HashMap<>();
+        map.put("_data", "_data");
+        map.put("date_added", "date_added");
+        map.put("date_modified", "date_modified");
+        map.put("media_type", "media_type");
+        builder.setProjectionMap(map);
+
+        assertFalse(builder.shouldAppendRowId(values));
+    }
+
+    @Test
+    public void testShouldAppendRowId_noProjectionMap_notAppendId() {
+        final SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
+        final ContentValues values = new ContentValues();
+        values.put(MediaStore.MediaColumns.DATA, "/storage/emulated/0/");
+
+        assertFalse(builder.shouldAppendRowId(values));
+    }
+
+    @Test
+    public void testShouldAppendRowId_hasIdInProjectionMap_shouldAppendId() {
+        final SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
+        final ContentValues values = new ContentValues();
+        values.put(MediaStore.MediaColumns.DATA, "/storage/emulated/0/");
+
+        final HashMap<String, String> map = new HashMap<>();
+        map.put(MediaStore.MediaColumns._ID, MediaStore.MediaColumns._ID);
+        map.put("_data", "_data");
+        map.put("date_added", "date_added");
+        map.put("date_modified", "date_modified");
+        map.put("media_type", "media_type");
+        builder.setProjectionMap(map);
+
+        assertTrue(builder.shouldAppendRowId(values));
+    }
+
     private void assertStrictInsertValid(ContentValues values) {
         mStrictBuilder.insert(mDatabase, values);
     }
diff --git a/tests/src/com/android/providers/media/util/TestUtils.java b/tests/src/com/android/providers/media/util/TestUtils.java
new file mode 100644
index 0000000..f2d3696
--- /dev/null
+++ b/tests/src/com/android/providers/media/util/TestUtils.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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
new file mode 100644
index 0000000..4fe9761
--- /dev/null
+++ b/tests/test_app/LegacyTestApp.xml
@@ -0,0 +1,38 @@
+<?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
new file mode 100644
index 0000000..cd1cc82
--- /dev/null
+++ b/tests/test_app/TestAppForPermissionActivity.xml
@@ -0,0 +1,41 @@
+<?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
new file mode 100644
index 0000000..5910280
--- /dev/null
+++ b/tests/test_app/TestAppWithStoragePerms.xml
@@ -0,0 +1,41 @@
+<?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
new file mode 100644
index 0000000..9708129
--- /dev/null
+++ b/tests/test_app/TestAppWithoutPerms.xml
@@ -0,0 +1,35 @@
+<?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
new file mode 100644
index 0000000..8c95cce
--- /dev/null
+++ b/tests/test_app/src/com/android/providers/media/util/TestAppActivity.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package 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
new file mode 100644
index 0000000..a77fd55
--- /dev/null
+++ b/tests/transcode/src/com/android/providers/media/transcode/TranscodeTest.java
@@ -0,0 +1,898 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.media.transcode;
+
+import static androidx.test.InstrumentationRegistry.getContext;
+
+import static com.android.providers.media.transcode.TranscodeTestUtils.assertFileContent;
+import static com.android.providers.media.transcode.TranscodeTestUtils.assertTranscode;
+import static com.android.providers.media.transcode.TranscodeTestUtils.installAppWithStoragePermissions;
+import static com.android.providers.media.transcode.TranscodeTestUtils.open;
+import static com.android.providers.media.transcode.TranscodeTestUtils.openFileAs;
+import static com.android.providers.media.transcode.TranscodeTestUtils.uninstallApp;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.Manifest;
+import android.media.ApplicationMediaCapabilities;
+import android.media.MediaFormat;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemProperties;
+import android.provider.MediaStore;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.cts.install.lib.TestApp;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class TranscodeTest {
+    private static final File EXTERNAL_STORAGE_DIRECTORY
+            = Environment.getExternalStorageDirectory();
+    private static final File DIR_CAMERA
+            = new File(EXTERNAL_STORAGE_DIRECTORY, Environment.DIRECTORY_DCIM + "/Camera");
+    // TODO(b/169546642): Test other directories like /sdcard and /sdcard/foo
+    // These are the only transcode unsupported directories we can stage files in given our
+    // test app permissions
+    private static final File[] DIRS_NO_TRANSCODE = {
+        new File(EXTERNAL_STORAGE_DIRECTORY, Environment.DIRECTORY_PICTURES),
+        new File(EXTERNAL_STORAGE_DIRECTORY, Environment.DIRECTORY_MOVIES),
+        new File(EXTERNAL_STORAGE_DIRECTORY, Environment.DIRECTORY_DOWNLOADS),
+        new File(EXTERNAL_STORAGE_DIRECTORY, Environment.DIRECTORY_DCIM),
+        new File(EXTERNAL_STORAGE_DIRECTORY, Environment.DIRECTORY_DOCUMENTS),
+    };
+
+    static final String NONCE = String.valueOf(System.nanoTime());
+    private static final String HEVC_FILE_NAME = "TranscodeTestHEVC_" + NONCE + ".mp4";
+    private static final String SMALL_HEVC_FILE_NAME = "TranscodeTestHevcSmall_" + NONCE + ".mp4";
+    private static final String LEGACY_FILE_NAME = "TranscodeTestLegacy_" + NONCE + ".mp4";
+
+    private static final TestApp TEST_APP_HEVC = new TestApp("TestAppHevc",
+            "com.android.providers.media.transcode.testapp", 1, false,
+            "TranscodeTestAppSupportsHevc.apk");
+
+    private static final TestApp TEST_APP_SLOW_MOTION = new TestApp("TestAppSlowMotion",
+            "com.android.providers.media.transcode.testapp", 1, false,
+            "TranscodeTestAppSupportsSlowMotion.apk");
+
+    @Before
+    public void setUp() throws Exception {
+        Assume.assumeTrue(SystemProperties.getBoolean("sys.fuse.transcode_enabled", false));
+
+        TranscodeTestUtils.pollForExternalStorageState();
+        TranscodeTestUtils.grantPermission(getContext().getPackageName(),
+                Manifest.permission.READ_EXTERNAL_STORAGE);
+        TranscodeTestUtils.pollForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, true);
+        TranscodeTestUtils.enableSeamlessTranscoding();
+        TranscodeTestUtils.disableTranscodingForAllPackages();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        TranscodeTestUtils.disableSeamlessTranscoding();
+    }
+
+    /**
+     * Tests that we return FD of transcoded file for legacy apps
+     * @throws Exception
+     */
+    @Test
+    public void testTranscoded_FilePath() throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        try {
+            TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+
+            ParcelFileDescriptor pfdOriginal = open(modernFile, false);
+
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+            ParcelFileDescriptor pfdTranscoded = open(modernFile, false);
+
+            assertFileContent(modernFile, modernFile, pfdOriginal, pfdTranscoded, false);
+        } finally {
+            modernFile.delete();
+        }
+    }
+
+    /**
+     * Tests that we don't transcode files outside DCIM/Camera
+     * @throws Exception
+     */
+    @Test
+    public void testNoTranscodeOutsideCamera_FilePath() throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        List<File> noTranscodeFiles = new ArrayList<>();
+        for (File file : DIRS_NO_TRANSCODE) {
+            noTranscodeFiles.add(new File(file, HEVC_FILE_NAME));
+        }
+        noTranscodeFiles.add(new File(getContext().getExternalFilesDir(null), HEVC_FILE_NAME));
+
+        try {
+            TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+            for (File file : noTranscodeFiles) {
+                TranscodeTestUtils.stageHEVCVideoFile(file);
+            }
+            ParcelFileDescriptor pfdOriginal1 = open(modernFile, false);
+
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+
+            for (File file : noTranscodeFiles) {
+                pfdOriginal1.seekTo(0);
+                ParcelFileDescriptor pfdOriginal2 = open(file, false);
+                assertFileContent(modernFile, file, pfdOriginal1, pfdOriginal2, true);
+            }
+        } finally {
+            modernFile.delete();
+            for (File file : noTranscodeFiles) {
+                file.delete();
+            }
+        }
+    }
+
+    /**
+     * Tests that same transcoded file is used for multiple open() from same app
+     * @throws Exception
+     */
+    @Test
+    public void testSameTranscoded_FilePath() throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        try {
+            TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+            ParcelFileDescriptor pfdTranscoded1 = open(modernFile, false);
+            ParcelFileDescriptor pfdTranscoded2 = open(modernFile, false);
+
+            assertFileContent(modernFile, modernFile, pfdTranscoded1, pfdTranscoded2, true);
+        } finally {
+            modernFile.delete();
+        }
+    }
+
+    /**
+     * Tests that we return FD of transcoded file for legacy apps
+     * @throws Exception
+     */
+    @Test
+    public void testTranscoded_ContentResolver() throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        try {
+            Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+
+            ParcelFileDescriptor pfdOriginal = open(uri, false, null /* bundle */);
+
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+
+            ParcelFileDescriptor pfdTranscoded = open(uri, false, null /* bundle */);
+
+            assertFileContent(modernFile, modernFile, pfdOriginal, pfdTranscoded, false);
+        } finally {
+            modernFile.delete();
+        }
+    }
+
+    /**
+     * Tests that we don't transcode files outside DCIM/Camera
+     * @throws Exception
+     */
+    @Test
+    public void testNoTranscodeOutsideCamera_ConentResolver() throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        List<File> noTranscodeFiles = new ArrayList<>();
+        for (File file : DIRS_NO_TRANSCODE) {
+            noTranscodeFiles.add(new File(file, HEVC_FILE_NAME));
+        }
+
+        try {
+            Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+            ArrayList<Uri> noTranscodeUris = new ArrayList<>();
+            for (File file : noTranscodeFiles) {
+                noTranscodeUris.add(TranscodeTestUtils.stageHEVCVideoFile(file));
+            }
+
+            ParcelFileDescriptor pfdOriginal1 = open(uri, false, null /* bundle */);
+
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+
+            for (int i = 0; i < noTranscodeUris.size(); i++) {
+                pfdOriginal1.seekTo(0);
+                ParcelFileDescriptor pfdOriginal2 =
+                        open(noTranscodeUris.get(i), false, null /* bundle */);
+                assertFileContent(modernFile, noTranscodeFiles.get(1), pfdOriginal1, pfdOriginal2,
+                        true);
+            }
+        } finally {
+            modernFile.delete();
+            for (File file : noTranscodeFiles) {
+                file.delete();
+            }
+        }
+    }
+
+    /**
+     * Tests that same transcoded file is used for multiple open() from same app
+     * @throws Exception
+     */
+    @Test
+    public void testSameTranscodedFile_ContentResolver() throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        try {
+            Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+
+            ParcelFileDescriptor pfdTranscoded1 = open(uri, false, null /* bundle */);
+            ParcelFileDescriptor pfdTranscoded2 = open(uri, false, null /* bundle */);
+
+            assertFileContent(modernFile, modernFile, pfdTranscoded1, pfdTranscoded2, true);
+        } finally {
+            modernFile.delete();
+        }
+    }
+
+    /**
+     * Tests that deletes are visible across legacy and modern apps
+     * @throws Exception
+     */
+    @Test
+    public void testDeleteTranscodedFile_FilePath() throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        try {
+            TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+
+            assertTrue(modernFile.delete());
+            assertFalse(modernFile.exists());
+
+            TranscodeTestUtils.disableTranscodingForAllPackages();
+
+            assertFalse(modernFile.exists());
+        } finally {
+            modernFile.delete();
+        }
+    }
+
+    /**
+     * Tests that renames are visible across legacy and modern apps
+     * @throws Exception
+     */
+    @Test
+    public void testRenameTranscodedFile_FilePath() throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        File destFile = new File(DIR_CAMERA, "renamed_" + HEVC_FILE_NAME);
+        try {
+            TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+
+            assertTrue(modernFile.renameTo(destFile));
+            assertTrue(destFile.exists());
+            assertFalse(modernFile.exists());
+
+            TranscodeTestUtils.disableTranscodingForAllPackages();
+
+            assertTrue(destFile.exists());
+            assertFalse(modernFile.exists());
+        } finally {
+            modernFile.delete();
+            destFile.delete();
+        }
+    }
+
+    /**
+     * Tests that transcode doesn't start until read(2)
+     * @throws Exception
+     */
+    @Test
+    public void testLazyTranscodedFile_FilePath() throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        try {
+            TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+
+            assertTranscode(modernFile, false);
+
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+
+            assertTranscode(modernFile, true);
+        } finally {
+            modernFile.delete();
+        }
+    }
+
+    /**
+     * Tests that transcode cache is reused after file path transcode
+     * @throws Exception
+     */
+    @Test
+    public void testTranscodedCacheReuse_FilePath() throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        try {
+            TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+
+            assertTranscode(modernFile, true);
+            assertTranscode(modernFile, false);
+        } finally {
+            modernFile.delete();
+        }
+    }
+
+    /**
+     * Tests that transcode cache is reused after ContentResolver transcode
+     * @throws Exception
+     */
+    @Test
+    public void testTranscodedCacheReuse_ContentResolver() throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        try {
+            Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+
+            assertTranscode(uri, true);
+            assertTranscode(uri, false);
+        } finally {
+            modernFile.delete();
+        }
+    }
+
+    /**
+     * Tests that transcode cache is reused after ContentResolver transcode
+     * and file path opens
+     * @throws Exception
+     */
+    @Test
+    public void testTranscodedCacheReuse_ContentResolverFilePath() throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        try {
+            Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+
+            assertTranscode(uri, true);
+            assertTranscode(modernFile, false);
+        } finally {
+            modernFile.delete();
+        }
+    }
+
+    /**
+     * Tests that transcode cache is reused after file path transcode
+     * and ContentResolver opens
+     * @throws Exception
+     */
+    @Test
+    public void testTranscodedCacheReuse_FilePathContentResolver() throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        try {
+            Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+
+            assertTranscode(modernFile, true);
+            assertTranscode(uri, false);
+        } finally {
+            modernFile.delete();
+        }
+    }
+
+    /**
+     * Tests that transcode cache is reused after rename
+     * @throws Exception
+     */
+    @Test
+    public void testTranscodedCacheReuseAfterRename_FilePath() throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        File destFile = new File(DIR_CAMERA, "renamed_" + HEVC_FILE_NAME);
+        try {
+            TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+
+            assertTranscode(modernFile, true);
+
+            assertTrue(modernFile.renameTo(destFile));
+
+            assertTranscode(destFile, false);
+        } finally {
+            modernFile.delete();
+            destFile.delete();
+        }
+    }
+
+    @Test
+    public void testExtraAcceptOriginalFormatTrue_ContentResolver() throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        try {
+            Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+
+            ParcelFileDescriptor pfdOriginal1 = open(uri, false, null /* bundle */);
+
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+
+            Bundle bundle = new Bundle();
+            bundle.putBoolean(MediaStore.EXTRA_ACCEPT_ORIGINAL_MEDIA_FORMAT, true);
+            ParcelFileDescriptor pfdOriginal2 = open(uri, false, bundle);
+
+            assertFileContent(modernFile, modernFile, pfdOriginal1, pfdOriginal2, true);
+        } finally {
+            modernFile.delete();
+        }
+    }
+
+    @Test
+    public void testExtraAcceptOriginalFormatFalse_ContentResolver() throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        try {
+            Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+
+            ParcelFileDescriptor pfdOriginal = open(uri, false, null /* bundle */);
+
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+
+            Bundle bundle = new Bundle();
+            bundle.putBoolean(MediaStore.EXTRA_ACCEPT_ORIGINAL_MEDIA_FORMAT, false);
+            ParcelFileDescriptor pfdTranscoded = open(uri, false, bundle);
+
+            assertFileContent(modernFile, modernFile, pfdOriginal, pfdTranscoded, false);
+        } finally {
+            modernFile.delete();
+        }
+    }
+
+    @Test
+    public void testExtraMediaCapabilitiesHevcSupportedTrue_ContentResolver() throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        try {
+            Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+
+            ParcelFileDescriptor pfdOriginal1 = open(uri, false, null /* bundle */);
+
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+
+            Bundle bundle = new Bundle();
+            ApplicationMediaCapabilities capabilities =
+                    new ApplicationMediaCapabilities.Builder()
+                    .addSupportedVideoMimeType(MediaFormat.MIMETYPE_VIDEO_HEVC).build();
+            bundle.putParcelable(MediaStore.EXTRA_MEDIA_CAPABILITIES, capabilities);
+            ParcelFileDescriptor pfdOriginal2 = open(uri, false, bundle);
+
+            assertFileContent(modernFile, modernFile, pfdOriginal1, pfdOriginal2, true);
+        } finally {
+            modernFile.delete();
+        }
+    }
+
+    @Test
+    public void testExtraMediaCapabilitiesHevcUnsupportedFalse_ContentResolver() throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        try {
+            Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+
+            ParcelFileDescriptor pfdOriginal1 = open(uri, false, null /* bundle */);
+
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+
+            Bundle bundle = new Bundle();
+            ApplicationMediaCapabilities capabilities =
+                    new ApplicationMediaCapabilities.Builder()
+                            .addUnsupportedVideoMimeType(MediaFormat.MIMETYPE_VIDEO_HEVC).build();
+            bundle.putParcelable(MediaStore.EXTRA_MEDIA_CAPABILITIES, capabilities);
+            ParcelFileDescriptor pfdOriginal2 = open(uri, false, bundle);
+
+            assertFileContent(modernFile, modernFile, pfdOriginal1, pfdOriginal2, false);
+        } finally {
+            modernFile.delete();
+        }
+    }
+
+    @Test
+    public void testExtraMediaCapabilitiesHevcUnspecifiedFalse_ContentResolver() throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        try {
+            Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+
+            ParcelFileDescriptor pfdOriginal1 = open(uri, false, null /* bundle */);
+
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+
+            Bundle bundle = new Bundle();
+            ApplicationMediaCapabilities capabilities =
+                    new ApplicationMediaCapabilities.Builder().build();
+            bundle.putParcelable(MediaStore.EXTRA_MEDIA_CAPABILITIES, capabilities);
+            ParcelFileDescriptor pfdTranscoded = open(uri, false, bundle);
+
+            assertFileContent(modernFile, modernFile, pfdOriginal1, pfdTranscoded, false);
+        } finally {
+            modernFile.delete();
+        }
+    }
+
+    @Test
+    public void testExtraAcceptOriginalTrueAndMediaCapabilitiesHevcFalse_ContentResolver()
+            throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        try {
+            Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+
+            ParcelFileDescriptor pfdOriginal1 = open(uri, false, null /* bundle */);
+
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+
+            Bundle bundle = new Bundle();
+            ApplicationMediaCapabilities capabilities =
+                    new ApplicationMediaCapabilities.Builder().build();
+            bundle.putParcelable(MediaStore.EXTRA_MEDIA_CAPABILITIES, capabilities);
+            bundle.putBoolean(MediaStore.EXTRA_ACCEPT_ORIGINAL_MEDIA_FORMAT, true);
+            ParcelFileDescriptor pfdOriginal2 = open(uri, false, bundle);
+
+            assertFileContent(modernFile, modernFile, pfdOriginal1, pfdOriginal2, true);
+        } finally {
+            modernFile.delete();
+        }
+    }
+
+    @Test
+    public void testMediaCapabilitiesManifestHevc()
+            throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        ParcelFileDescriptor pfdOriginal2 = null;
+        try {
+            installAppWithStoragePermissions(TEST_APP_HEVC);
+
+            Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+
+            ParcelFileDescriptor pfdOriginal1 = open(modernFile, false);
+
+            TranscodeTestUtils.enableTranscodingForPackage(TEST_APP_HEVC.getPackageName());
+
+            pfdOriginal2 = openFileAs(TEST_APP_HEVC, modernFile);
+
+            assertFileContent(modernFile, modernFile, pfdOriginal1, pfdOriginal2, true);
+        } finally {
+            // Explicitly close PFD otherwise instrumention might crash when test_app is uninstalled
+            if (pfdOriginal2 != null) {
+                pfdOriginal2.close();
+            }
+            modernFile.delete();
+            uninstallApp(TEST_APP_HEVC);
+        }
+    }
+
+    @Test
+    public void testMediaCapabilitiesManifestSlowMotion()
+            throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        ParcelFileDescriptor pfdOriginal2 = null;
+        try {
+            installAppWithStoragePermissions(TEST_APP_SLOW_MOTION);
+
+            Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+
+            ParcelFileDescriptor pfdOriginal1 = open(modernFile, false);
+
+            TranscodeTestUtils.enableTranscodingForPackage(TEST_APP_SLOW_MOTION.getPackageName());
+
+            pfdOriginal2 = openFileAs(TEST_APP_SLOW_MOTION, modernFile);
+
+            assertFileContent(modernFile, modernFile, pfdOriginal1, pfdOriginal2, false);
+        } finally {
+            // Explicitly close PFD otherwise instrumention might crash when test_app is uninstalled
+            if (pfdOriginal2 != null) {
+                pfdOriginal2.close();
+            }
+            modernFile.delete();
+            uninstallApp(TEST_APP_HEVC);
+        }
+    }
+
+    @Test
+    public void testAppCompatNoTranscodeHevc() throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        String packageName = TEST_APP_SLOW_MOTION.getPackageName();
+        ParcelFileDescriptor pfdOriginal2 = null;
+        try {
+            installAppWithStoragePermissions(TEST_APP_SLOW_MOTION);
+
+            Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+
+            ParcelFileDescriptor pfdOriginal1 = open(modernFile, false);
+
+            TranscodeTestUtils.enableTranscodingForPackage(packageName);
+            // App compat takes precedence
+            TranscodeTestUtils.forceEnableAppCompatHevc(packageName);
+
+            Thread.sleep(2000);
+
+            pfdOriginal2 = openFileAs(TEST_APP_SLOW_MOTION, modernFile);
+
+            assertFileContent(modernFile, modernFile, pfdOriginal1, pfdOriginal2, true);
+        } finally {
+            // Explicitly close PFD otherwise instrumention might crash when test_app is uninstalled
+            if (pfdOriginal2 != null) {
+                pfdOriginal2.close();
+            }
+            modernFile.delete();
+            TranscodeTestUtils.resetAppCompat(packageName);
+            uninstallApp(TEST_APP_HEVC);
+        }
+    }
+
+    @Test
+    public void testAppCompatTranscodeHevc() throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        String packageName = TEST_APP_SLOW_MOTION.getPackageName();
+        ParcelFileDescriptor pfdOriginal2 = null;
+        try {
+            installAppWithStoragePermissions(TEST_APP_SLOW_MOTION);
+
+            Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+
+            ParcelFileDescriptor pfdOriginal1 = open(modernFile, false);
+
+            // Transcoding is disabled but app compat enables it (disables hevc support)
+            TranscodeTestUtils.forceDisableAppCompatHevc(packageName);
+
+            pfdOriginal2 = openFileAs(TEST_APP_SLOW_MOTION, modernFile);
+
+            assertFileContent(modernFile, modernFile, pfdOriginal1, pfdOriginal2, false);
+        } finally {
+            // Explicitly close PFD otherwise instrumention might crash when test_app is uninstalled
+            if (pfdOriginal2 != null) {
+                pfdOriginal2.close();
+            }
+            modernFile.delete();
+            TranscodeTestUtils.resetAppCompat(packageName);
+            uninstallApp(TEST_APP_HEVC);
+        }
+    }
+
+    /**
+     * Tests that we never initiate tanscoding for legacy formats.
+     * This test compares the bytes read before and after enabling transcoding for the test app.
+     * @throws Exception
+     */
+    @Test
+    public void testTranscodedNotInitiatedForLegacy_UsingBytesRead() throws Exception {
+        File legacyFile = new File(DIR_CAMERA, LEGACY_FILE_NAME);
+        try {
+            TranscodeTestUtils.stageLegacyVideoFile(legacyFile);
+
+            ParcelFileDescriptor pfdOriginal = open(legacyFile, false);
+
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+            ParcelFileDescriptor pfdTranscoded = open(legacyFile, false);
+
+            assertFileContent(legacyFile, legacyFile, pfdOriginal, pfdTranscoded, true);
+        } finally {
+            legacyFile.delete();
+        }
+    }
+
+    /**
+     * Tests that we never initiate tanscoding for legacy formats.
+     * This test asserts using the time it took to read after enabling transcoding for the test app.
+     * The reason for keeping this check separately (than
+     * {@link TranscodeTest#testTranscodedNotInitiatedForLegacy_UsingTiming()}) is that this
+     * provides a higher level of suret that the timing wasn't favorable because of any caching
+     * after open().
+     * @throws Exception
+     */
+    @Test
+    public void testTranscodedNotInitiatedForLegacy_UsingTiming() throws Exception {
+        File legacyFile = new File(DIR_CAMERA, LEGACY_FILE_NAME);
+        try {
+            TranscodeTestUtils.stageLegacyVideoFile(legacyFile);
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+
+            assertTranscode(legacyFile, false);
+        } finally {
+            legacyFile.delete();
+        }
+    }
+
+    /**
+     * Tests that we don't timeout while transcoding small HEVC videos.
+     * For instance, due to some calculation errors we might incorrectly make timeout to be 0.
+     * We test this by making sure that a small HEVC video (< 1 sec long and < 1Mb size) gets
+     * transcoded.
+     * @throws Exception
+     */
+    @Test
+    public void testNoTranscodeTimeoutForSmallHevcVideos() throws Exception {
+        File modernFile = new File(DIR_CAMERA, SMALL_HEVC_FILE_NAME);
+        try {
+            TranscodeTestUtils.stageSmallHevcVideoFile(modernFile);
+            ParcelFileDescriptor pfdOriginal = open(modernFile, false);
+
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+            ParcelFileDescriptor pfdTranscoded = open(modernFile, false);
+
+            assertFileContent(modernFile, modernFile, pfdOriginal, pfdTranscoded, false);
+        } finally {
+            modernFile.delete();
+        }
+    }
+
+    /**
+     * Tests that we transcode an HEVC file when a modern app passes the mediaCapabilitiesUid of a
+     * legacy app that cannot handle an HEVC file.
+     */
+    @Test
+    public void testOriginalCallingUid_modernAppPassLegacyAppUid()
+            throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        ParcelFileDescriptor pfdModernApp = null;
+        ParcelFileDescriptor pfdModernAppPassingLegacyUid = null;
+        try {
+            installAppWithStoragePermissions(TEST_APP_SLOW_MOTION);
+            Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+
+            // pfdModernApp is for original content (without transcoding) since this is a modern
+            // app.
+            pfdModernApp = open(modernFile, false);
+
+            // pfdModernAppPassingLegacyUid is for transcoded content since this modern app is
+            // passing the UID of a legacy app capable of handling HEVC files.
+            Bundle bundle = new Bundle();
+            bundle.putInt(MediaStore.EXTRA_MEDIA_CAPABILITIES_UID,
+                    getContext().getPackageManager().getPackageUid(
+                            TEST_APP_SLOW_MOTION.getPackageName(), 0));
+            pfdModernAppPassingLegacyUid = open(uri, false, bundle);
+
+            assertTranscode(pfdModernApp, false);
+            assertTranscode(pfdModernAppPassingLegacyUid, true);
+
+            // pfdModernApp and pfdModernAppPassingLegacyUid should be different.
+            assertFileContent(modernFile, modernFile, pfdModernApp, pfdModernAppPassingLegacyUid,
+                    false);
+        } finally {
+            if (pfdModernApp != null) {
+                pfdModernApp.close();
+            }
+
+            if (pfdModernAppPassingLegacyUid != null) {
+                pfdModernAppPassingLegacyUid.close();
+            }
+            modernFile.delete();
+            uninstallApp(TEST_APP_SLOW_MOTION);
+        }
+    }
+
+    /**
+     * Tests that we don't transcode an HEVC file when a legacy app passes the mediaCapabilitiesUid
+     * of a modern app that can handle an HEVC file.
+     */
+    @Test
+    public void testOriginalCallingUid_legacyAppPassModernAppUid()
+            throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        ParcelFileDescriptor pfdLegacyApp = null;
+        ParcelFileDescriptor pfdLegacyAppPassingModernUid = null;
+        try {
+            installAppWithStoragePermissions(TEST_APP_HEVC);
+            Uri uri = TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+
+            // pfdLegacyApp is for transcoded content since this is a legacy app.
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+            pfdLegacyApp = open(modernFile, false);
+
+            // pfdLegacyAppPassingModernUid is for original content (without transcoding) since this
+            // legacy app is passing the UID of a modern app capable of handling HEVC files.
+            Bundle bundle = new Bundle();
+            bundle.putInt(MediaStore.EXTRA_MEDIA_CAPABILITIES_UID,
+                    getContext().getPackageManager().getPackageUid(TEST_APP_HEVC.getPackageName(),
+                            0));
+            pfdLegacyAppPassingModernUid = open(uri, false, bundle);
+
+            assertTranscode(pfdLegacyApp, true);
+            assertTranscode(pfdLegacyAppPassingModernUid, false);
+
+            // pfdLegacyApp and pfdLegacyAppPassingModernUid should be different.
+            assertFileContent(modernFile, modernFile, pfdLegacyApp, pfdLegacyAppPassingModernUid,
+                    false);
+        } finally {
+            if (pfdLegacyApp != null) {
+                pfdLegacyApp.close();
+            }
+
+            if (pfdLegacyAppPassingModernUid != null) {
+                pfdLegacyAppPassingModernUid.close();
+            }
+            modernFile.delete();
+            uninstallApp(TEST_APP_HEVC);
+        }
+    }
+
+    /**
+     * Tests that we return FD of original file from
+     * MediaStore#getOriginalMediaFormatFileDescriptor.
+     * @throws Exception
+     */
+    @Test
+    public void testGetOriginalMediaFormatFileDescriptor_returnsOriginalFileDescriptor()
+            throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        try {
+            TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+
+            ParcelFileDescriptor pfdOriginal = open(modernFile, false);
+
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+            ParcelFileDescriptor pfdTranscoded = open(modernFile, false);
+
+            ParcelFileDescriptor pfdOriginalMediaFormat =
+                    MediaStore.getOriginalMediaFormatFileDescriptor(getContext(), pfdTranscoded);
+
+            assertFileContent(modernFile, modernFile, pfdOriginal, pfdOriginalMediaFormat, true);
+            assertFileContent(modernFile, modernFile, pfdTranscoded, pfdOriginalMediaFormat, false);
+        } finally {
+            modernFile.delete();
+        }
+    }
+
+    /**
+     * Tests that we can successfully write to a transcoded file.
+     * We check this by writing something to tanscoded content and then read it back.
+     */
+    @Test
+    public void testWriteSuccessfulToTranscodedContent() throws Exception {
+        File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+        ParcelFileDescriptor pfdTranscodedContent = null;
+        try {
+            TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+            TranscodeTestUtils.enableTranscodingForPackage(getContext().getPackageName());
+            pfdTranscodedContent = open(modernFile, false);
+
+            // read some bytes from some random offset
+            Random random = new Random(System.currentTimeMillis());
+            int byteCount = 512;
+            int fileOffset = random.nextInt((int) pfdTranscodedContent.getStatSize() - byteCount);
+            byte[] readBytes = TranscodeTestUtils.read(pfdTranscodedContent, byteCount, fileOffset);
+
+            // write the bytes at the same offset after some modification
+            pfdTranscodedContent = open(modernFile, true);
+            byte[] writeBytes = new byte[byteCount];
+            for (int i = 0; i < byteCount; ++i) {
+                writeBytes[i] = (byte) ~readBytes[i];
+            }
+            TranscodeTestUtils.write(pfdTranscodedContent, writeBytes, byteCount, fileOffset);
+
+            // read back the same number of bytes from the same offset
+            readBytes = TranscodeTestUtils.read(pfdTranscodedContent, byteCount, fileOffset);
+
+            // assert that read is same as written
+            assertTrue(Arrays.equals(readBytes, writeBytes));
+        } finally {
+            if (pfdTranscodedContent != null) {
+                pfdTranscodedContent.close();
+            }
+            modernFile.delete();
+        }
+    }
+}
diff --git a/tests/utils/src/com/android/providers/media/tests/utils/Timer.java b/tests/utils/src/com/android/providers/media/tests/utils/Timer.java
new file mode 100644
index 0000000..6d8caca
--- /dev/null
+++ b/tests/utils/src/com/android/providers/media/tests/utils/Timer.java
@@ -0,0 +1,82 @@
+/**
+ * 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.tests.utils;
+
+import android.os.Bundle;
+import android.os.SystemClock;
+
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * General Timer function for MediaProvider tests.
+ */
+public class Timer {
+    private static final String TAG = "Timer";
+
+    private final String name;
+    private int count;
+    private long duration;
+    private long start;
+    private long maxRunDuration = 0;
+
+    public Timer(String name) {
+        this.name = name;
+    }
+
+    public void start() {
+        if (start != 0) {
+            throw new IllegalStateException();
+        } else {
+            start = SystemClock.elapsedRealtimeNanos();
+        }
+    }
+
+    public void stop() {
+        long currentRunDuration = 0;
+        if (start == 0) {
+            throw new IllegalStateException();
+        } else {
+            currentRunDuration = (SystemClock.elapsedRealtimeNanos() - start);
+            maxRunDuration = (currentRunDuration > maxRunDuration) ? currentRunDuration :
+                maxRunDuration;
+            duration += currentRunDuration;
+            start = 0;
+            count++;
+        }
+    }
+
+    public long getAverageDurationMillis() {
+        return TimeUnit.MILLISECONDS.convert(duration / count, TimeUnit.NANOSECONDS);
+    }
+
+    public long getMaxDurationMillis() {
+        return TimeUnit.MILLISECONDS.convert(maxRunDuration, TimeUnit.NANOSECONDS);
+    }
+
+    public void dumpResults() {
+        final long duration = getAverageDurationMillis();
+        Log.v(TAG, name + ": " + duration + "ms");
+
+        final Bundle results = new Bundle();
+        results.putLong(name, duration);
+        InstrumentationRegistry.getInstrumentation().sendStatus(0, results);
+    }
+}
diff --git a/tools/dialogs/AndroidManifest.xml b/tools/dialogs/AndroidManifest.xml
index 947f1bd..960cb13 100644
--- a/tools/dialogs/AndroidManifest.xml
+++ b/tools/dialogs/AndroidManifest.xml
@@ -1,14 +1,18 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.providers.media.tools.dialogs">
+<?xml version="1.0" encoding="utf-8"?>
 
-    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+     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"/>
 
     <application android:label="DialogsTool">
-        <activity android:name=".DialogsActivity">
+        <activity android:name=".DialogsActivity"
+             android:exported="true">
             <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 4ff1731..867bfaf 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,6 +99,14 @@
         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
new file mode 100644
index 0000000..c223eb5
--- /dev/null
+++ b/transcode.sh
@@ -0,0 +1,18 @@
+# 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
+}