Merge "Don't use std::stoi()" into mainline-prod am: 671e241bf2
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/providers/MediaProvider/+/13007436
Change-Id: Icda0e6bacec1b3f51fc632959d8e6e1c1ba39f97
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 305027b..e937ef3 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -52,7 +52,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 +62,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 +96,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/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
deleted file mode 100644
index e69de29..0000000
--- a/MODULE_LICENSE_APACHE2
+++ /dev/null
diff --git a/OWNERS b/OWNERS
index 4e37503..a1580ff 100644
--- a/OWNERS
+++ b/OWNERS
@@ -4,4 +4,4 @@
nandana@google.com
zezeozue@google.com
corinac@google.com
-sahanas@google.com
\ No newline at end of file
+sahanas@google.com
diff --git a/apex/apex_manifest.json b/apex/apex_manifest.json
index 6d8da53..ffef8fb 100644
--- a/apex/apex_manifest.json
+++ b/apex/apex_manifest.json
@@ -1,4 +1,4 @@
{
"name": "com.android.mediaprovider",
- "version": 309999900
+ "version": 300000000
}
diff --git a/apex/framework/api/current.txt b/apex/framework/api/current.txt
index 52439fb..646e1ac 100644
--- a/apex/framework/api/current.txt
+++ b/apex/framework/api/current.txt
@@ -26,6 +26,7 @@
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";
@@ -114,7 +115,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);
}
diff --git a/apex/framework/java/android/provider/MediaStore.java b/apex/framework/java/android/provider/MediaStore.java
index 73f56ef..7ecabdb 100644
--- a/apex/framework/java/android/provider/MediaStore.java
+++ b/apex/framework/java/android/provider/MediaStore.java
@@ -48,6 +48,7 @@
import android.media.MediaFormat;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Environment;
@@ -585,6 +586,26 @@
*/
public final static String EXTRA_OUTPUT = "output";
+ // TODO(b/158465539): Add API to explicitly specify media capabilities via Bundle
+ /**
+ * Specify that the caller wants to receive the original media format without transcoding.
+ *
+ * This is a very dangerous flag to use because apps can suddenly fail to play media after
+ * an OS upgrade. Clients should instead specify their supported media capabilities explicitly
+ * in their manifest.
+ *
+ * This is useful for apps that usually receive transcoded media, but want to have more granular
+ * control.
+ *
+ * <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_ACCEPT_ORIGINAL_MEDIA_FORMAT =
+ "android.provider.extra.ACCEPT_ORIGINAL_MEDIA_FORMAT";
+
/**
* 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
@@ -1596,7 +1617,7 @@
* The MTP storage ID of the file
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Deprecated
// @Column(Cursor.FIELD_TYPE_INTEGER)
public static final String STORAGE_ID = "storage_id";
@@ -1684,6 +1705,41 @@
* Constant for the {@link #MEDIA_TYPE} column indicating that file is a document file.
*/
public static final int MEDIA_TYPE_DOCUMENT = 6;
+
+ /**
+ * 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";
}
}
@@ -3073,7 +3129,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)
diff --git a/errorprone/Android.bp b/errorprone/Android.bp
index 317c081..7d1a7cd 100644
--- a/errorprone/Android.bp
+++ b/errorprone/Android.bp
@@ -14,11 +14,14 @@
static_libs: [
"//external/error_prone:error_prone_core",
- "//external/dagger2:dagger2-auto-service",
+ ],
+
+ libs: [
+ "//external/auto:auto_service_annotations",
],
plugins: [
- "//external/dagger2:dagger2-auto-service",
+ "//external/auto:auto_service_plugin",
],
javacflags: ["-verbose"],
diff --git a/jni/FuseDaemon.cpp b/jni/FuseDaemon.cpp
index b6deb60..d0785bd 100644
--- a/jni/FuseDaemon.cpp
+++ b/jni/FuseDaemon.cpp
@@ -110,8 +110,8 @@
// 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);
/*
* In order to avoid double caching with fuse, call fadvise on the file handles
@@ -388,14 +388,12 @@
}
}
-static double get_timeout(struct fuse* fuse, const string& path, bool should_inval) {
- string media_path = fuse->GetEffectiveRootPath() + "/Android/media";
- if (fuse->disable_dentry_cache || should_inval || path.find(media_path, 0) == 0 || is_package_owned_path(path, fuse->path)) {
+static double get_attr_timeout(const string& path, bool should_inval, node* node,
+ struct fuse* fuse) {
+ if (fuse->disable_dentry_cache || should_inval || is_package_owned_path(path, fuse->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. Case-insensitive lookups need to invalidate other case-insensitive dentry matches
// 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.
@@ -404,6 +402,23 @@
return std::numeric_limits<double>::max();
}
+static double get_entry_timeout(const string& path, bool should_inval, node* node,
+ struct fuse* fuse) {
+ string media_path = fuse->GetEffectiveRootPath() + "/Android/media";
+ if (path.find(media_path, 0) == 0) {
+ // 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 get_attr_timeout(path, should_inval, node, fuse);
+}
+
+static std::string get_path(node* node) {
+ const string& io_path = node->GetIoPath();
+ return io_path.empty() ? node->BuildPath() : io_path;
+}
+
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* fuse = get_fuse(req);
@@ -417,11 +432,49 @@
}
bool should_inval = false;
- node = parent->LookupChildByName(name, true /* acquire */);
+ bool transforms_complete = true;
+ int transforms = 0;
+ string io_path;
+
+ if (S_ISREG(e->attr.st_mode)) {
+ // Handle potential file transforms
+ transforms = fuse->mp->GetTransforms(path, req->ctx.uid);
+
+ if (transforms < 0) {
+ // Fail lookup if we can't fetch supported transforms for path
+ LOG(WARNING) << "Failed to fetch transforms for " << name;
+ *error_code = ENOENT;
+ return NULL;
+ }
+
+ // TODO(b/169412244): Improve JNI interaction
+ // Invalidate if there are any transforms so that we always get a lookup into userspace
+ should_inval = should_inval || transforms;
+ if (transforms) {
+ // If there are any transforms, fetch IO path
+ io_path = fuse->mp->GetIoPath(path, req->ctx.uid);
+ if (io_path.empty()) {
+ *error_code = EFAULT;
+ return NULL;
+ }
+
+ if (io_path != path) {
+ // Update size with io_path size
+ if (lstat(io_path.c_str(), &e->attr) < 0) {
+ *error_code = errno;
+ return NULL;
+ }
+ transforms_complete = false;
+ }
+ }
+ }
+
+ node = parent->LookupChildByName(name, true /* acquire */, transforms);
if (!node) {
- node = ::node::Create(parent, name, &fuse->lock, &fuse->tracker);
+ node = ::node::Create(parent, name, io_path, transforms_complete, transforms, &fuse->lock,
+ &fuse->tracker);
} else if (!mediaprovider::fuse::containsMount(path, std::to_string(getuid() / PER_USER_RANGE))) {
- should_inval = node->HasCaseInsensitiveMatch();
+ should_inval = should_inval || node->HasCaseInsensitiveMatch();
// Only invalidate a path if it does not contain mount.
// Invalidate both names to ensure there's no dentry left in the kernel after the following
// operations:
@@ -455,11 +508,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, should_inval, node, fuse);
+ e->attr_timeout = get_attr_timeout(path, should_inval, node, fuse);
return node;
}
@@ -619,7 +669,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;
@@ -631,8 +681,10 @@
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,
+ get_attr_timeout(path, node->GetTransforms() || node->HasCaseInsensitiveMatch(),
+ node, fuse));
}
}
@@ -648,7 +700,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;
@@ -727,15 +779,16 @@
}
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,
+ get_attr_timeout(path, node->GetTransforms() || node->HasCaseInsensitiveMatch(),
+ node, fuse));
}
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
@@ -852,12 +905,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);
}
@@ -938,10 +987,7 @@
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;
// TODO(b/147408834): Check ENOTEMPTY & EEXIST error conditions before JNI call.
@@ -949,11 +995,9 @@
// 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;
}
@@ -971,8 +1015,8 @@
}
*/
-static handle* create_handle_for_node(struct fuse* fuse, const string& path, int fd, node* node,
- const RedactionInfo* ri, int* keep_cache) {
+static handle* create_handle_for_node(struct fuse* fuse, const string& path, int fd, uid_t 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
@@ -1002,7 +1046,7 @@
}
bool direct_io = (is_cached_file_open && is_redaction_change) || is_file_locked(fd, path);
- handle* h = new handle(fd, ri, !direct_io);
+ handle* h = new handle(fd, ri, !direct_io, uid);
node->AddHandle(h);
return h;
}
@@ -1016,7 +1060,8 @@
return;
}
const struct fuse_ctx* ctx = fuse_req_ctx(req);
- const string path = node->BuildPath();
+ const string& path = get_path(node);
+ const string& build_path = node->BuildPath();
if (!is_app_accessible_path(fuse->mp, path, ctx->uid)) {
fuse_reply_err(req, ENOENT);
return;
@@ -1029,7 +1074,10 @@
fi->direct_io = true;
}
- int status = fuse->mp->IsOpenAllowed(path, ctx->uid, is_requesting_write(fi->flags));
+ // TODO: If transform, disallow write
+ // Force permission check with the build path because the MediaProvider database might not be
+ // aware of the io_path
+ int status = fuse->mp->IsOpenAllowed(build_path, ctx->uid, is_requesting_write(fi->flags));
if (status) {
fuse_reply_err(req, status);
return;
@@ -1058,6 +1106,8 @@
if (is_requesting_write(fi->flags)) {
ri = std::make_unique<RedactionInfo>();
} else {
+ // TODO(b/171953356): Pass build_path for use during query, otherwise, MediaProvider would
+ // find the transcoded path
ri = fuse->mp->GetRedactionInfo(path, req->ctx.uid, req->ctx.pid);
}
@@ -1068,7 +1118,8 @@
}
int keep_cache = 1;
- handle* h = create_handle_for_node(fuse, path, fd, node, ri.release(), &keep_cache);
+ handle* h =
+ create_handle_for_node(fuse, path, fd, req->ctx.uid, node, ri.release(), &keep_cache);
fi->fh = ptr_to_id(h);
fi->keep_cache = keep_cache;
fi->direct_io = !h->cached;
@@ -1153,6 +1204,17 @@
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(),
+ h->uid)) {
+ fuse_reply_err(req, EFAULT);
+ return;
+ }
+ node->SetTransformsComplete();
+ }
+
fuse->fadviser.Record(h->fd, size);
if (h->ri->isRedactionNeeded()) {
@@ -1224,14 +1286,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,
@@ -1602,7 +1656,8 @@
// to the file before all the EXIF content is written. We could special case reads before the
// first close after a file has just been created.
int keep_cache = 1;
- handle* h = create_handle_for_node(fuse, child_path, fd, node, new RedactionInfo(), &keep_cache);
+ handle* h = create_handle_for_node(fuse, child_path, fd, req->ctx.uid, node,
+ new RedactionInfo(), &keep_cache);
fi->fh = ptr_to_id(h);
fi->keep_cache = keep_cache;
fi->direct_io = !h->cached;
@@ -1669,7 +1724,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,
diff --git a/jni/MediaProviderWrapper.cpp b/jni/MediaProviderWrapper.cpp
index b0db1ca..167fb6f 100644
--- a/jni/MediaProviderWrapper.cpp
+++ b/jni/MediaProviderWrapper.cpp
@@ -285,6 +285,12 @@
/*is_static*/ false);
mid_is_app_clone_user_ = CacheMethod(env, "isAppCloneUser", "(I)Z",
/*is_static*/ false);
+ mid_get_io_path_ = CacheMethod(env, "getIoPath", "(Ljava/lang/String;I)Ljava/lang/String;",
+ /*is_static*/ false);
+ mid_get_transforms_ = CacheMethod(env, "getTransforms", "(Ljava/lang/String;I)I",
+ /*is_static*/ false);
+ mid_transform_ = CacheMethod(env, "transform", "(Ljava/lang/String;Ljava/lang/String;II)Z",
+ /*is_static*/ false);
}
MediaProviderWrapper::~MediaProviderWrapper() {
@@ -451,6 +457,50 @@
return res;
}
+std::string MediaProviderWrapper::GetIoPath(const std::string& path, uid_t uid) {
+ JNIEnv* env = MaybeAttachCurrentThread();
+
+ ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
+ ScopedLocalRef<jstring> j_res_path(
+ env, static_cast<jstring>(env->CallObjectMethod(media_provider_object_,
+ mid_get_io_path_, j_path.get(), uid)));
+ ScopedUtfChars j_res_utf(env, j_res_path.get());
+ if (CheckForJniException(env)) {
+ return "";
+ }
+
+ return string(j_res_utf.c_str());
+}
+
+int MediaProviderWrapper::GetTransforms(const std::string& path, uid_t uid) {
+ JNIEnv* env = MaybeAttachCurrentThread();
+
+ ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
+ int res = env->CallIntMethod(media_provider_object_, mid_get_transforms_, j_path.get(), uid);
+
+ if (CheckForJniException(env)) {
+ return -1;
+ }
+
+ return res;
+}
+
+bool MediaProviderWrapper::Transform(const std::string& src, const std::string& dst, int transforms,
+ uid_t 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, uid);
+
+ if (CheckForJniException(env)) {
+ return false;
+ }
+
+ return res;
+}
+
/*****************************************************************************************/
/******************************** Private member functions *******************************/
/*****************************************************************************************/
diff --git a/jni/MediaProviderWrapper.h b/jni/MediaProviderWrapper.h
index 4e931e8..b5060a3 100644
--- a/jni/MediaProviderWrapper.h
+++ b/jni/MediaProviderWrapper.h
@@ -165,6 +165,15 @@
*/
void OnFileCreated(const std::string& path);
+ /** Get path for actual I/O */
+ std::string GetIoPath(const std::string& path, uid_t uid);
+
+ /** Get supported transformations for path and transform actions for uid on path. */
+ int GetTransforms(const std::string& path, uid_t uid);
+
+ /** Transforms from src to dst file */
+ bool Transform(const std::string& src, const std::string& dst, int transforms, uid_t uid);
+
/**
* Determines if to allow FUSE_LOOKUP for uid. Might allow uids that don't belong to the
* MediaProvider user, depending on OEM configuration.
@@ -206,6 +215,9 @@
jmethodID mid_on_file_created_;
jmethodID mid_should_allow_lookup_;
jmethodID mid_is_app_clone_user_;
+ jmethodID mid_get_io_path_;
+ jmethodID mid_get_transforms_;
+ jmethodID mid_transform_;
/**
* Auxiliary for caching MediaProvider methods.
diff --git a/jni/node-inl.h b/jni/node-inl.h
index dfd8141..b635a1c 100644
--- a/jni/node-inl.h
+++ b/jni/node-inl.h
@@ -19,6 +19,7 @@
#include <android-base/logging.h>
+#include <atomic>
#include <cstdint>
#include <limits>
#include <list>
@@ -40,13 +41,15 @@
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, uid_t uid)
+ : fd(fd), ri(ri), cached(cached), uid(uid) {
CHECK(ri != nullptr);
}
const int fd;
const std::unique_ptr<const RedactionInfo> ri;
const bool cached;
+ const uid_t uid;
~handle() { close(fd); }
};
@@ -114,13 +117,14 @@
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 transforms_complete, const int transforms, std::recursive_mutex* lock,
NodeTracker* tracker) {
// Place the entire constructor under a critical section to make sure
// node creation, tracking (if enabled) and the addition to a parent are
// atomic.
std::lock_guard<std::recursive_mutex> guard(*lock);
- return new node(parent, name, lock, tracker);
+ return new node(parent, name, io_path, transforms_complete, transforms, lock, tracker);
}
// Creates a new root node. Root nodes have no parents by definition
@@ -128,7 +132,7 @@
static node* CreateRoot(const std::string& path, std::recursive_mutex* lock,
NodeTracker* tracker) {
std::lock_guard<std::recursive_mutex> guard(*lock);
- node* root = new node(nullptr, path, lock, tracker);
+ node* root = new node(nullptr, path, path, true, 0, lock, tracker);
// The root always has one extra reference to avoid it being
// accidentally collected.
@@ -176,36 +180,40 @@
// Looks up a direct descendant of this node by 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_) {
+ 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 +260,16 @@
return name_;
}
+ const std::string& GetIoPath() const { return io_path_; }
+
+ int GetTransforms() const { return transforms_; }
+
+ bool IsTransformsComplete() const {
+ return transforms_complete_.load(std::memory_order_acquire);
+ }
+
+ void SetTransformsComplete() { transforms_complete_.store(true, std::memory_order_release); }
+
node* GetParent() const {
std::lock_guard<std::recursive_mutex> guard(*lock_);
return parent_;
@@ -325,8 +343,12 @@
static const node* LookupAbsolutePath(const node* root, const std::string& absolute_path);
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, bool transforms_complete,
+ const int transforms, std::recursive_mutex* lock, NodeTracker* tracker)
: name_(name),
+ io_path_(io_path),
+ transforms_complete_(transforms_complete),
+ transforms_(transforms),
refcount_(0),
parent_(nullptr),
has_redacted_cache_(false),
@@ -381,6 +403,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.
//
@@ -427,6 +475,15 @@
// 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 determine the 'supported' and 'required' transforms to perform on node
+ // before IO. These flags should not be interpreted in native but should be passed as part
+ // of a transform function and if successful, |transforms_complete_| should be set to true
+ const int transforms_;
// 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
diff --git a/jni/node_test.cpp b/jni/node_test.cpp
index 357cea8..e502dbe 100644
--- a/jni/node_test.cpp
+++ b/jni/node_test.cpp
@@ -31,8 +31,14 @@
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, transforms, &lock_, &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 +67,7 @@
}
TEST_F(NodeTest, TestRelease) {
- node* node = node::Create(nullptr, "/path", &lock_, &tracker_);
+ node* node = node::Create(nullptr, "/path", "", true, 0, &lock_, &tracker_);
acquire(node);
acquire(node);
ASSERT_EQ(3, GetRefCount(node));
@@ -77,7 +83,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 +100,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 +119,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 +139,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 +257,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", "", true, 0, &lock_, &tracker_);
+ node::Create(child, "s1", "", true, 0, &lock_, &tracker_);
+ node* subchild2 = node::Create(child, "s2", "", true, 0, &lock_, &tracker_);
+ node::Create(subchild2, "sc2", "", true, 0, &lock_, &tracker_);
ASSERT_EQ(child, parent->LookupChildByName("subdir", false /* acquire */));
node::DeleteTree(child);
@@ -178,6 +295,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 +348,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 */, 0 /* uid */);
node->AddHandle(h);
ASSERT_TRUE(node->HasCachedHandle());
@@ -229,7 +361,7 @@
EXPECT_DEATH(node->DestroyHandle(h), "");
EXPECT_DEATH(node->DestroyHandle(nullptr), "");
std::unique_ptr<handle> h2(
- new handle(-1, new mediaprovider::fuse::RedactionInfo, true /* cached */));
+ new handle(-1, new mediaprovider::fuse::RedactionInfo, true /* cached */, 0 /* uid */));
EXPECT_DEATH(node->DestroyHandle(h2.get()), "");
}
@@ -318,3 +450,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/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/values-af/strings.xml b/res/values-af/strings.xml
index 09a1296..1d7c7db 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">Plus <xliff:g id="COUNT_1">^1</xliff:g> bykomende items</item>
<item quantity="one">Plus <xliff:g id="COUNT_0">^1</xliff:g> bykomende item</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Vee tydelike programlêers uit"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Vee tydelike programlêers uit?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> wil \'n paar tydelike lêers uitvee. Dit kan verhoogde batterygebruik of sellulêre data tot gevolg hê."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Vee tans tydelike programlêers uit …"</string>
- <string name="clear" msgid="5524638938415865915">"Vee uit"</string>
<string name="allow" msgid="8885707816848569619">"Laat toe"</string>
<string name="deny" msgid="6040983710442068936">"Weier"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 1a62bf5..ec43b10 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -37,10 +37,8 @@
<item quantity="one">እንዲሁም <xliff:g id="COUNT_1">^1</xliff:g> ተጨማሪ ንጥሎች</item>
<item quantity="other">እንዲሁም <xliff:g id="COUNT_1">^1</xliff:g> ተጨማሪ ንጥሎች</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"ጊዜያዊ የመተግበሪያ ፋይሎች ይጸዱ?"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"ጊዜያዊ የመተግበሪያ ፋይሎች ይጽዱ?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> አንዳንድ ጊዜያዊ ፋይሎችን ማጽዳት ይፈልጋል። ይህ የበለጠ የባትሪ ኃይል ወይም የተንቀሳቃሽ ስልክ ውሂብ ፍጆታን ሊጨምር ይችላል።"</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"ጊዜያዊ የመተግበሪያ ፋይሎችን በማጽዳት ላይ…"</string>
- <string name="clear" msgid="5524638938415865915">"አጽዳ"</string>
<string name="allow" msgid="8885707816848569619">"ፍቀድ"</string>
<string name="deny" msgid="6040983710442068936">"ከልክል"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index c5d7cea..74d70be 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -23,7 +23,7 @@
<string name="unknown" msgid="2059049215682829375">"غير معروف"</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_audio" msgid="3505830755201326018">"الصوت"</string>
<string name="root_documents" msgid="3829103301363849237">"المستندات"</string>
<string name="permission_required" msgid="1460820436132943754">"مطلوب الحصول على إذن لتعديل هذا العنصر أو حذفه."</string>
<string name="permission_required_action" msgid="706370952366113539">"متابعة"</string>
@@ -45,10 +45,8 @@
<item quantity="other">و<xliff:g id="COUNT_1">^1</xliff:g> عنصر إضافي</item>
<item quantity="one">وعنصر إضافي واحد (<xliff:g id="COUNT_0">^1</xliff:g>)</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"محو ملفات التطبيق المؤقتة"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"هل تريد محو ملفات التطبيق المؤقتة؟"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"يريد تطبيق <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> محو بعض الملفات المؤقتة. قد يؤدي هذا إلى زيادة استهلاك شحن البطارية أو بيانات شبكة الجوّال."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"جارٍ محو ملفات التطبيق المؤقتة…"</string>
- <string name="clear" msgid="5524638938415865915">"محو"</string>
<string name="allow" msgid="8885707816848569619">"سماح"</string>
<string name="deny" msgid="6040983710442068936">"رفض"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index be897a1..8bbc109 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -37,10 +37,8 @@
<item quantity="one">আৰু <xliff:g id="COUNT_1">^1</xliff:g> টা অতিৰিক্ত বস্তু</item>
<item quantity="other">আৰু <xliff:g id="COUNT_1">^1</xliff:g> টা অতিৰিক্ত বস্তু</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"অস্থায়ী এপ্ ফাইলসমূহ মচক"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"অস্থায়ী এপ্ ফাইলসমূহ মচিবনে?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>এ কিছুমান অস্থায়ী ফাইল মচিব বিচাৰিছে। ইয়াৰ ফলত বেটাৰী অথবা চেলুলাৰ ডেটাৰ ব্যৱহাৰ বৃদ্ধি হ’ব পাৰে।"</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"অস্থায়ী এপ্ ফাইলসমূহ মচি থকা হৈছে…"</string>
- <string name="clear" msgid="5524638938415865915">"মচক"</string>
<string name="allow" msgid="8885707816848569619">"অনুমতি দিয়ক"</string>
<string name="deny" msgid="6040983710442068936">"অস্বীকাৰ কৰক"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index a8d2ab3..988aeff 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">Üstəgəl <xliff:g id="COUNT_1">^1</xliff:g> əlavə element</item>
<item quantity="one">Üstəgəl <xliff:g id="COUNT_0">^1</xliff:g> əlavə element</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Müvəqqəti tətbiq fayllarını silin"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Müvəqqəti tətbiq faylları silinsin?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> bəzi müvəqqəti faylları silmək istəyir. Bu, batareya və ya mobil data istifadəsinin artmasına səbəb ola bilər."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Müvəqqəti tətbiq faylları silinir…"</string>
- <string name="clear" msgid="5524638938415865915">"Silin"</string>
<string name="allow" msgid="8885707816848569619">"İcazə verin"</string>
<string name="deny" msgid="6040983710442068936">"Rədd edin"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 9756151..feb033d 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -39,10 +39,8 @@
<item quantity="few">I još <xliff:g id="COUNT_1">^1</xliff:g> stavke</item>
<item quantity="other">I još <xliff:g id="COUNT_1">^1</xliff:g> stavki</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Obrišite privremene datoteke aplikacija"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Želite li da obrišete privremene datoteke aplikacija?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> želi da obriše neke privremene datoteke. Ovo može da dovede do povećane potrošnje baterije ili mobilnih podataka."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Brišu se privremene datoteke aplikacija…"</string>
- <string name="clear" msgid="5524638938415865915">"Obriši"</string>
<string name="allow" msgid="8885707816848569619">"Dozvoli"</string>
<string name="deny" msgid="6040983710442068936">"Odbij"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 58bffee..b73ff00 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -41,10 +41,8 @@
<item quantity="many">Плюс <xliff:g id="COUNT_1">^1</xliff:g> дадатковых элементаў</item>
<item quantity="other">Плюс <xliff:g id="COUNT_1">^1</xliff:g> дадатковага элемента</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Ачысціць часовыя файлы праграм"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Ачысціць часовыя файлы праграм?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> запытвае дазвол на выдаленне некаторых часовых файлаў. У сувязі з гэтым можа павялічыцца ўзровень выкарыстання акумулятара ці памер сотавай перадачы даных."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Выдаляюцца часовыя файлы праграм…"</string>
- <string name="clear" msgid="5524638938415865915">"Ачысціць"</string>
<string name="allow" msgid="8885707816848569619">"Дазволіць"</string>
<string name="deny" msgid="6040983710442068936">"Адмовіць"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index b576228..d160870 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">И още <xliff:g id="COUNT_1">^1</xliff:g> допълнителни елемента</item>
<item quantity="one">И още <xliff:g id="COUNT_0">^1</xliff:g> допълнителен елемент</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Изчистване на временните файлове на приложенията"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Да се изчистят ли временните файлове на приложенията?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> иска да изчисти някои временни файлове. Това може да доведе до увеличено използване на батерията или мобилните данни."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Временните файлове на приложенията се изчистват…"</string>
- <string name="clear" msgid="5524638938415865915">"Изчистване"</string>
<string name="allow" msgid="8885707816848569619">"Разрешаване"</string>
<string name="deny" msgid="6040983710442068936">"Отказ"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 552556e..8de6cb5 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -37,10 +37,8 @@
<item quantity="one">এছাড়াও <xliff:g id="COUNT_1">^1</xliff:g>টি অতিরিক্ত আইটেম</item>
<item quantity="other">এছাড়াও <xliff:g id="COUNT_1">^1</xliff:g>টি অতিরিক্ত আইটেম</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"অস্থায়ী অ্যাপ ফাইলগুলি খালি করুন"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"অস্থায়ী অ্যাপ ফাইল মুছে দিতে চান?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> কিছু অস্থায়ী ফাইলকে মুছে দিতে চায়। এর ফলে, ব্যাটারি বা মোবাইল ডেটা বেশি খরচ হতে পারে।"</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"অস্থায়ী অ্যাপ ফাইল মুছে ফেলা হচ্ছে…"</string>
- <string name="clear" msgid="5524638938415865915">"সরান"</string>
<string name="allow" msgid="8885707816848569619">"অনুমতি দিন"</string>
<string name="deny" msgid="6040983710442068936">"বাতিল করুন"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 9f89481..9aa39e6 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -39,10 +39,8 @@
<item quantity="few">Još <xliff:g id="COUNT_1">^1</xliff:g> dodatne stavke</item>
<item quantity="other">Još <xliff:g id="COUNT_1">^1</xliff:g> dodatnih stavki</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Brisanje privremenih fajlova aplikacija"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Obrisati privremene fajlove aplikacija?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"Aplikacija <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> želi obrisati neke privremene fajlove. Ovo može dovesti do povećanog korištenja baterije ili prijenosa podataka na mobilnoj mreži."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Brisanje privremenih fajlova aplikacije…"</string>
- <string name="clear" msgid="5524638938415865915">"Obriši"</string>
<string name="allow" msgid="8885707816848569619">"Dozvoli"</string>
<string name="deny" msgid="6040983710442068936">"Odbij"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 184a9b9..b23455f 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other"><xliff:g id="COUNT_1">^1</xliff:g> elements addicionals més</item>
<item quantity="one"><xliff:g id="COUNT_0">^1</xliff:g> element addicional més</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Esborra fitxers temporals d\'aplicacions"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Vols esborrar els fitxers temporals d\'aplicacions?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vol esborrar alguns fitxers temporals. Això pot suposar un augment de l\'ús de la bateria o de les dades mòbils."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"S\'estan esborrant els fitxers temporals d\'aplicacions…"</string>
- <string name="clear" msgid="5524638938415865915">"Esborra"</string>
<string name="allow" msgid="8885707816848569619">"Permet"</string>
<string name="deny" msgid="6040983710442068936">"Denega"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 3fa3e40..bc92886 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -41,10 +41,8 @@
<item quantity="other">Plus <xliff:g id="COUNT_1">^1</xliff:g> dalších položek</item>
<item quantity="one">Plus <xliff:g id="COUNT_0">^1</xliff:g> další položka</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Vymazání dočasných souborů aplikací"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Vymazat dočasné soubory aplikací?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"Aplikace <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> chce vymazat nějaké dočasné soubory. Tato akce může vést ke zvýšenému využití baterie nebo mobilních dat."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Mazání dočasných souborů aplikace…"</string>
- <string name="clear" msgid="5524638938415865915">"Vymazat"</string>
<string name="allow" msgid="8885707816848569619">"Povolit"</string>
<string name="deny" msgid="6040983710442068936">"Zakázat"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 96ee522..181ea3e 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -19,7 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Medier"</string>
<string name="storage_description" msgid="4081716890357580107">"Lokalt lager"</string>
<string name="app_label" msgid="9035307001052716210">"Medielagring"</string>
- <string name="artist_label" msgid="8105600993099120273">"Kunstner"</string>
+ <string name="artist_label" msgid="8105600993099120273">"Musiker"</string>
<string name="unknown" msgid="2059049215682829375">"Ukendt"</string>
<string name="root_images" msgid="5861633549189045666">"Billeder"</string>
<string name="root_videos" msgid="8792703517064649453">"Videoer"</string>
@@ -37,10 +37,8 @@
<item quantity="one">Plus <xliff:g id="COUNT_1">^1</xliff:g> andet element</item>
<item quantity="other">Plus <xliff:g id="COUNT_1">^1</xliff:g> andre elementer</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Ryd midlertidige appfiler"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Vil du rydde midlertidige appfiler?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vil gerne rydde nogle midlertidige filer. Dette kan resultere i øget forbrug af batteri eller mobildata."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Rydder midlertidige appfiler…"</string>
- <string name="clear" msgid="5524638938415865915">"Ryd"</string>
<string name="allow" msgid="8885707816848569619">"Tillad"</string>
<string name="deny" msgid="6040983710442068936">"Afvis"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 8b19ff5..07f2c1f 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other"><xliff:g id="COUNT_1">^1</xliff:g> weitere Elemente</item>
<item quantity="one"><xliff:g id="COUNT_0">^1</xliff:g> weiteres Element</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Temporäre App-Dateien löschen"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Temporäre App-Dateien löschen?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> möchte einige temporäre Dateien löschen. Dadurch können Akkuverbrauch und mobile Datennutzung steigen."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Temporäre App-Dateien werden gelöscht…"</string>
- <string name="clear" msgid="5524638938415865915">"Löschen"</string>
<string name="allow" msgid="8885707816848569619">"Zulassen"</string>
<string name="deny" msgid="6040983710442068936">"Ablehnen"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 7045ca0..c30faf6 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">Συν <xliff:g id="COUNT_1">^1</xliff:g> επιπλέον στοιχεία</item>
<item quantity="one">Συν <xliff:g id="COUNT_0">^1</xliff:g> επιπλέον στοιχείο</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Διαγραφή προσωρινών αρχείων εφαρμογών"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Να διαγραφούν τα προσωρινά αρχεία εφαρμογών;"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"Η εφαρμογή <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> θέλει να διαγράψει ορισμένα προσωρινά αρχεία. Αυτό μπορεί να οδηγήσει σε αυξημένη χρήση μπαταριών ή δεδομένων κινητής τηλεφωνίας."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Διαγραφή προσωρινών αρχείων εφαρμογών…"</string>
- <string name="clear" msgid="5524638938415865915">"Διαγραφή"</string>
<string name="allow" msgid="8885707816848569619">"Να επιτρέπεται"</string>
<string name="deny" msgid="6040983710442068936">"Απόρριψη"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 1205a7e..42fbc5b 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">Plus <xliff:g id="COUNT_1">^1</xliff:g> additional items</item>
<item quantity="one">Plus <xliff:g id="COUNT_0">^1</xliff:g> additional item</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Clear temporary app files"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Clear temporary app files?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> would like to clear some temporary files. This may result in an increased usage of battery or mobile data."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Clearing temporary app files…"</string>
- <string name="clear" msgid="5524638938415865915">"Clear"</string>
<string name="allow" msgid="8885707816848569619">"Allow"</string>
<string name="deny" msgid="6040983710442068936">"Deny"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index 1205a7e..42fbc5b 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">Plus <xliff:g id="COUNT_1">^1</xliff:g> additional items</item>
<item quantity="one">Plus <xliff:g id="COUNT_0">^1</xliff:g> additional item</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Clear temporary app files"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Clear temporary app files?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> would like to clear some temporary files. This may result in an increased usage of battery or mobile data."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Clearing temporary app files…"</string>
- <string name="clear" msgid="5524638938415865915">"Clear"</string>
<string name="allow" msgid="8885707816848569619">"Allow"</string>
<string name="deny" msgid="6040983710442068936">"Deny"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 1205a7e..42fbc5b 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">Plus <xliff:g id="COUNT_1">^1</xliff:g> additional items</item>
<item quantity="one">Plus <xliff:g id="COUNT_0">^1</xliff:g> additional item</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Clear temporary app files"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Clear temporary app files?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> would like to clear some temporary files. This may result in an increased usage of battery or mobile data."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Clearing temporary app files…"</string>
- <string name="clear" msgid="5524638938415865915">"Clear"</string>
<string name="allow" msgid="8885707816848569619">"Allow"</string>
<string name="deny" msgid="6040983710442068936">"Deny"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 1205a7e..42fbc5b 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">Plus <xliff:g id="COUNT_1">^1</xliff:g> additional items</item>
<item quantity="one">Plus <xliff:g id="COUNT_0">^1</xliff:g> additional item</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Clear temporary app files"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Clear temporary app files?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> would like to clear some temporary files. This may result in an increased usage of battery or mobile data."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Clearing temporary app files…"</string>
- <string name="clear" msgid="5524638938415865915">"Clear"</string>
<string name="allow" msgid="8885707816848569619">"Allow"</string>
<string name="deny" msgid="6040983710442068936">"Deny"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index 18ba860..8bbcd26 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">Plus <xliff:g id="COUNT_1">^1</xliff:g> additional items</item>
<item quantity="one">Plus <xliff:g id="COUNT_0">^1</xliff:g> additional item</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Clear temporary app files"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Clear temporary app files?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> would like to clear some temporary files. This may result in an increased usage of battery or cellular data."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Clearing temporary app files…"</string>
- <string name="clear" msgid="5524638938415865915">"Clear"</string>
<string name="allow" msgid="8885707816848569619">"Allow"</string>
<string name="deny" msgid="6040983710442068936">"Deny"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index e2ffe99..67c0e30 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other"><xliff:g id="COUNT_1">^1</xliff:g> elementos adicionales</item>
<item quantity="one"><xliff:g id="COUNT_0">^1</xliff:g> elemento adicional</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Borra archivos temporales de apps"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"¿Deseas borrar archivos temporales de apps?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> quiere borrar algunos archivos temporales, lo que podría ocasionar un mayor uso de la batería o los datos móviles."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Borrando archivos temporales de apps…"</string>
- <string name="clear" msgid="5524638938415865915">"Borrar"</string>
<string name="allow" msgid="8885707816848569619">"Permitir"</string>
<string name="deny" msgid="6040983710442068936">"Rechazar"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 7d0ee18..2b34dcd 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">Y <xliff:g id="COUNT_1">^1</xliff:g> elementos más</item>
<item quantity="one">Y <xliff:g id="COUNT_0">^1</xliff:g> elemento más</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Borrar archivos de aplicaciones temporales"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"¿Borrar archivos temporales de aplicaciones?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> quiere borrar algunos archivos temporales. Esta acción puede aumentar el uso de la batería o los datos móviles."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Borrando archivos temporales de aplicaciones…"</string>
- <string name="clear" msgid="5524638938415865915">"Borrar"</string>
<string name="allow" msgid="8885707816848569619">"Permitir"</string>
<string name="deny" msgid="6040983710442068936">"Denegar"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 1dbfc5c..104bb9b 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">Veel <xliff:g id="COUNT_1">^1</xliff:g> lisaüksust</item>
<item quantity="one">Veel <xliff:g id="COUNT_0">^1</xliff:g> lisaüksus</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Rakenduse ajutiste failide kustutamine"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Kas soovite rakenduse ajutised failid kustutada?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> soovib kustutada mõned ajutised failid. See võib aku või mobiilse andmeside kasutust suurendada."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Rakenduse ajutiste failide kustutamine …"</string>
- <string name="clear" msgid="5524638938415865915">"Kustuta"</string>
<string name="allow" msgid="8885707816848569619">"Luba"</string>
<string name="deny" msgid="6040983710442068936">"Keela"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 982a7c8..a85c489 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">Eta beste <xliff:g id="COUNT_1">^1</xliff:g> elementu</item>
<item quantity="one">Eta beste <xliff:g id="COUNT_0">^1</xliff:g> elementu</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Garbitu aplikazioen aldi baterako fitxategiak"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Aplikazioen aldi baterako fitxategiak garbitu nahi dituzu?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aplikazioak aldi baterako fitxategi batzuk ezabatu nahi ditu. Ondorioz, baliteke bateria edo datu-konexioko datu gehiago erabiltzea."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Aplikazioaren aldi baterako fitxategiak garbitzen…"</string>
- <string name="clear" msgid="5524638938415865915">"Garbitu"</string>
<string name="allow" msgid="8885707816848569619">"Baimendu"</string>
<string name="deny" msgid="6040983710442068936">"Ukatu"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index fc0d11e..de705e3 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -28,7 +28,7 @@
<string name="permission_required" msgid="1460820436132943754">"برای اصلاح یا حذف این مورد مجوز لازم است."</string>
<string name="permission_required_action" msgid="706370952366113539">"ادامه"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"مجاز"</string>
- <string name="grant_dialog_button_deny" msgid="6190589471415815741">"مجاز نبودن"</string>
+ <string name="grant_dialog_button_deny" msgid="6190589471415815741">"رد کردن"</string>
<plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
<item quantity="one">+<xliff:g id="COUNT_1">^1</xliff:g></item>
<item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
@@ -37,12 +37,10 @@
<item quantity="one">بهعلاوه <xliff:g id="COUNT_1">^1</xliff:g> مورد دیگر</item>
<item quantity="other">بهعلاوه <xliff:g id="COUNT_1">^1</xliff:g> مورد دیگر</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"پاک کردن فایلهای موقت برنامه"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"فایلهای موقت برنامه پاک شوند؟"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> میخواهد برخی از فایلهای موقت را پاک کند. این کار ممکن است استفاده از باتری یا داده شبکه تلفن همراه را افزایش دهد."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"درحال پاک کردن فایلهای موقتی برنامه…"</string>
- <string name="clear" msgid="5524638938415865915">"پاک کردن"</string>
<string name="allow" msgid="8885707816848569619">"اجازه دادن"</string>
- <string name="deny" msgid="6040983710442068936">"مجاز نبودن"</string>
+ <string name="deny" msgid="6040983710442068936">"رد کردن"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
<item quantity="one">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> فایل صوتی را تغییر دهد؟</item>
<item quantity="other">به <xliff:g id="APP_NAME_1">^1</xliff:g> اجازه میدهید <xliff:g id="COUNT">^2</xliff:g> فایل صوتی را تغییر دهد؟</item>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 39517ba..c100861 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">Ja <xliff:g id="COUNT_1">^1</xliff:g> muuta asiaa</item>
<item quantity="one">Ja <xliff:g id="COUNT_0">^1</xliff:g> muu asia</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Poista väliaikaisia sovellustiedostoja"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Poistetaanko väliaikaisia sovellustiedostoja?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> haluaa poistaa joitain väliaikaisia tiedostoja. Tämä voi lisätä akun tai mobiilidatan käyttöä."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Poistetaan väliaikaisia sovellustiedostoja…"</string>
- <string name="clear" msgid="5524638938415865915">"Poista"</string>
<string name="allow" msgid="8885707816848569619">"Salli"</string>
<string name="deny" msgid="6040983710442068936">"Estä"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 021ba70..6ec897f 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -37,10 +37,8 @@
<item quantity="one">Plus <xliff:g id="COUNT_1">^1</xliff:g> élément supplémentaire</item>
<item quantity="other">Plus <xliff:g id="COUNT_1">^1</xliff:g> éléments supplémentaires</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Effacer les fichiers temporaires des applications"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Effacer les fichiers temporaires des applications?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aimerait supprimer certains fichiers temporaires. Ceci peut entraîner une utilisation accrue de la pile ou des données cellulaires."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Suppression des fichiers temporaires des applications en cours…"</string>
- <string name="clear" msgid="5524638938415865915">"Effacer"</string>
<string name="allow" msgid="8885707816848569619">"Autoriser"</string>
<string name="deny" msgid="6040983710442068936">"Refuser"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 7b0d694..7c28423 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -30,81 +30,79 @@
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Autoriser"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Refuser"</string>
<plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="one">+ <xliff:g id="COUNT_1">^1</xliff:g></item>
+ <item quantity="one">+<xliff:g id="COUNT_1">^1</xliff:g></item>
<item quantity="other">+ <xliff:g id="COUNT_1">^1</xliff:g></item>
</plurals>
<plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
<item quantity="one">Plus <xliff:g id="COUNT_1">^1</xliff:g> autre élément</item>
<item quantity="other">Plus <xliff:g id="COUNT_1">^1</xliff:g> autres éléments</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Effacer les fichiers d\'application temporaires"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Effacer les fichiers d\'application temporaires ?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> souhaite supprimer quelques fichiers temporaires, ce qui risque de solliciter davantage la batterie et le réseau de données mobiles."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Suppression des fichiers d\'application temporaires…"</string>
- <string name="clear" msgid="5524638938415865915">"Effacer"</string>
<string name="allow" msgid="8885707816848569619">"Autoriser"</string>
<string name="deny" msgid="6040983710442068936">"Refuser"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">Autoriser l\'application <xliff:g id="APP_NAME_1">^1</xliff:g> à modifier <xliff:g id="COUNT">^2</xliff:g> fichier audio ?</item>
+ <item quantity="one">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> audio files?</item>
<item quantity="other">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_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="one">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> videos?</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_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="one">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> photos?</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_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="one">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to modify <xliff:g id="COUNT">^2</xliff:g> items?</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_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="one">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">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_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="one">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">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_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="one">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">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_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="one">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">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_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="one">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">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_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="one">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">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_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="one">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">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_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="one">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">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_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="one">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">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_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="one">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> videos?</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_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="one">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> photos?</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_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="one">Allow <xliff:g id="APP_NAME_1">^1</xliff:g> to delete <xliff:g id="COUNT">^2</xliff:g> items?</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>
</resources>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 93e281d..69eb572 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">E <xliff:g id="COUNT_1">^1</xliff:g> elementos adicionais</item>
<item quantity="one">E <xliff:g id="COUNT_0">^1</xliff:g> elemento adicional</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Borrar os ficheiros temporais das aplicacións"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Queres borrar os ficheiros temporais da aplicación?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> quere borrar algúns ficheiros temporais. Por este motivo, pode aumentar o uso da batería e dos datos móbiles."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Borrando ficheiros temporais das aplicacións…"</string>
- <string name="clear" msgid="5524638938415865915">"Borrar"</string>
<string name="allow" msgid="8885707816848569619">"Permitir"</string>
<string name="deny" msgid="6040983710442068936">"Denegar"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index e7c5619..47be764 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -22,8 +22,8 @@
<string name="artist_label" msgid="8105600993099120273">"કલાકાર"</string>
<string name="unknown" msgid="2059049215682829375">"અજાણ"</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_videos" msgid="8792703517064649453">"વિડિઓઝ"</string>
+ <string name="root_audio" msgid="3505830755201326018">"ઑડિઓ"</string>
<string name="root_documents" msgid="3829103301363849237">"દસ્તાવેજો"</string>
<string name="permission_required" msgid="1460820436132943754">"આ આઇટમમાં ફેરફાર કરવા માટે અથવા તેને ડિલીટ કરવા માટે પરવાનગી હોવી જરૂરી છે."</string>
<string name="permission_required_action" msgid="706370952366113539">"આગળ વધો"</string>
@@ -37,10 +37,8 @@
<item quantity="one">ઉપરાંત <xliff:g id="COUNT_1">^1</xliff:g> વધારાની આઇટમ</item>
<item quantity="other">ઉપરાંત <xliff:g id="COUNT_1">^1</xliff:g> વધારાની આઇટમ</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"ઍપની બધી અસ્થાયી ફાઇલ સાફ કરો"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"ઍપની બધી અસ્થાયી ફાઇલોને સાફ કરીએ?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> કેટલીક અસ્થાયી ફાઇલોને સાફ કરવા માગે છે. આના પરિણામે બૅટરી અથવા સેલ્યુલર ડેટાના વપરાશમાં વધારો થઈ શકે છે."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"ઍપની બધી અસ્થાયી ફાઇલો સાફ કરી રહ્યાં છીએ…"</string>
- <string name="clear" msgid="5524638938415865915">"સાફ કરો"</string>
<string name="allow" msgid="8885707816848569619">"મંજૂરી આપો"</string>
<string name="deny" msgid="6040983710442068936">"નકારો"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 3937bd2..345cbf8 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/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>
@@ -37,14 +37,12 @@
<item quantity="one">अन्य <xliff:g id="COUNT_1">^1</xliff:g> आइटम</item>
<item quantity="other">अन्य <xliff:g id="COUNT_1">^1</xliff:g> आइटम</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"ऐप्लिकेशन से जुड़ी, कुछ समय तक सेव रहने वाली फ़ाइलें मिटाएं"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"ऐप्लिकेशन से जुड़ी, कुछ समय तक रहने वाली फ़ाइलें मिटाना चाहते हैं?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> कुछ समय तक रहने वाली फ़ाइलें हटाना चाहता है. इससे बैटरी या मोबाइल डेटा का इस्तेमाल बढ़ सकता है."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"ऐप्लिकेशन से जुड़ी, कुछ समय तक सेव रहने वाली फ़ाइलें मिटाई जा रही हैं…"</string>
- <string name="clear" msgid="5524638938415865915">"मिटाएं"</string>
<string name="allow" msgid="8885707816848569619">"अनुमति दें"</string>
<string name="deny" msgid="6040983710442068936">"अनुमति न दें"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="one">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइल में बदलाव करने की अनुमति देना चाहते हैं?</item>
+ <item quantity="one">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइलों में बदलाव करने की अनुमति देना चाहते हैं?</item>
<item quantity="other">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइलों में बदलाव करने की अनुमति देना चाहते हैं?</item>
</plurals>
<plurals name="permission_write_video" formatted="false" msgid="1098082003326873084">
@@ -60,7 +58,7 @@
<item quantity="other">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</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="one">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइलें, ट्रैश में मूव करने की अनुमति देना चाहते हैं?</item>
<item quantity="other">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइलें, ट्रैश में मूव करने की अनुमति देना चाहते हैं?</item>
</plurals>
<plurals name="permission_trash_video" formatted="false" msgid="4672871911555787438">
@@ -76,7 +74,7 @@
<item quantity="other">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</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="one">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइलें, ट्रैश से बाहर निकालने की अनुमति देना चाहते हैं?</item>
<item quantity="other">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइलें, ट्रैश से बाहर निकालने की अनुमति देना चाहते हैं?</item>
</plurals>
<plurals name="permission_untrash_video" formatted="false" msgid="332894888445508879">
@@ -92,7 +90,7 @@
<item quantity="other">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</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="one">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइलें मिटाने की अनुमति देना चाहते हैं?</item>
<item quantity="other">क्या आप <xliff:g id="APP_NAME_1">^1</xliff:g> को <xliff:g id="COUNT">^2</xliff:g> ऑडियो फ़ाइलें मिटाने की अनुमति देना चाहते हैं?</item>
</plurals>
<plurals name="permission_delete_video" formatted="false" msgid="1251942606336748563">
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 6b4abd9..3f07d2c 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -39,10 +39,8 @@
<item quantity="few">Plus sljedeći broj dodatnih stavki: <xliff:g id="COUNT_1">^1</xliff:g></item>
<item quantity="other">Plus sljedeći broj dodatnih stavki: <xliff:g id="COUNT_1">^1</xliff:g></item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Brisanje privremenih datoteka aplikacija"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Želite li izbrisati privremene datoteke aplikacija?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> želi izbrisati neke privremene datoteke. Zbog toga može doći do veće upotrebe baterije ili mobilnih podataka."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Brišu se privremene datoteke aplikacija…"</string>
- <string name="clear" msgid="5524638938415865915">"Izbriši"</string>
<string name="allow" msgid="8885707816848569619">"Dopusti"</string>
<string name="deny" msgid="6040983710442068936">"Odbij"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 1e39499..6e39b1a 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">És további <xliff:g id="COUNT_1">^1</xliff:g> elem</item>
<item quantity="one">És további <xliff:g id="COUNT_0">^1</xliff:g> elem</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Ideiglenes alkalmazásfájlok törlése"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Törli az ideiglenes alkalmazásfájlokat?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"A(z) <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> szeretne törölni néhány ideiglenes fájlt. Ez nagyobb akkumulátor- és mobiladat-használatot eredményezhet."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Ideiglenes alkalmazásfájlok törlése…"</string>
- <string name="clear" msgid="5524638938415865915">"Törlés"</string>
<string name="allow" msgid="8885707816848569619">"Engedélyezés"</string>
<string name="deny" msgid="6040983710442068936">"Tiltás"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 862e27a..1fceb95 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -37,10 +37,8 @@
<item quantity="one">Ու ևս <xliff:g id="COUNT_1">^1</xliff:g> լրացուցիչ տարր</item>
<item quantity="other">Ու ևս <xliff:g id="COUNT_1">^1</xliff:g> լրացուցիչ տարր</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Ջնջեք հավելվածի լրացուցիչ ֆայլերը"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Ջնջե՞լ հավելվածի լրացուցիչ ֆայլերը"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> հավելվածը հայցում է որոշ ժամանակավոր ֆայլեր ջնջելու թույլտվություն։ Դրա համար կարող է օգտագործվել ավելի շատ մարտկոցի լիցք կամ բջջային ինտերնետ։"</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Հավելվածների ժամանակավոր ֆայլերը ջնջվում են…"</string>
- <string name="clear" msgid="5524638938415865915">"Ջնջել"</string>
<string name="allow" msgid="8885707816848569619">"Թույլատրել"</string>
<string name="deny" msgid="6040983710442068936">"Մերժել"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 42406d7..4ac8d71 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">Plus <xliff:g id="COUNT_1">^1</xliff:g> item tambahan</item>
<item quantity="one">Plus <xliff:g id="COUNT_0">^1</xliff:g> item tambahan</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Menghapus file aplikasi sementara"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Hapus file aplikasi sementara?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ingin menghapus beberapa file sementara. Ini dapat meningkatkan penggunaan baterai atau data seluler."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Menghapus file aplikasi sementara…"</string>
- <string name="clear" msgid="5524638938415865915">"Hapus"</string>
<string name="allow" msgid="8885707816848569619">"Izinkan"</string>
<string name="deny" msgid="6040983710442068936">"Tolak"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index fb7daa3..f389881 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -37,10 +37,8 @@
<item quantity="one">Auk <xliff:g id="COUNT_1">^1</xliff:g> atriðis til viðbótar</item>
<item quantity="other">Auk <xliff:g id="COUNT_1">^1</xliff:g> atriða til viðbótar</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Hreinsa tímabundnar forritaskrár"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Hreinsa tímabundnar forritaskrár?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vill fá að hreinsa sumar tímabundnar skrár. Þetta getur haft í för með sér aukna rafhlöðunotkun eða notkun farsímagagna."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Verið er að eyða tímabundnum forritaskrám…"</string>
- <string name="clear" msgid="5524638938415865915">"Hreinsa"</string>
<string name="allow" msgid="8885707816848569619">"Leyfa"</string>
<string name="deny" msgid="6040983710442068936">"Hafna"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 9b5d598..100921d 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">Più altri <xliff:g id="COUNT_1">^1</xliff:g> elementi</item>
<item quantity="one">Più <xliff:g id="COUNT_0">^1</xliff:g> altro elemento</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Cancellare file temporanei delle app"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Cancellare i file temporanei delle app?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"L\'app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vorrebbe cancellare alcuni file temporanei. In tal caso potrebbe verificarsi un maggiore utilizzo della batteria o della rete dati."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Cancellazione dei file temporanei delle app…"</string>
- <string name="clear" msgid="5524638938415865915">"Cancella"</string>
<string name="allow" msgid="8885707816848569619">"Consenti"</string>
<string name="deny" msgid="6040983710442068936">"Rifiuta"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 24f2c3a..d4d0107 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -41,10 +41,8 @@
<item quantity="other">ועוד <xliff:g id="COUNT_1">^1</xliff:g> פריטים נוספים</item>
<item quantity="one">ועוד פריט אחד נוסף (<xliff:g id="COUNT_0">^1</xliff:g>)</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"ניקוי קובצי אפליקציה זמניים"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"לנקות קובצי אפליקציה זמניים?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"קיבלת בקשה מהאפליקציה <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> לניקוי של חלק מהקבצים הזמניים. הפעולה עשויה להגביר את השימוש בסוללה או בחבילת הגלישה."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"מתבצע ניקוי של קובצי אפליקציה זמניים…"</string>
- <string name="clear" msgid="5524638938415865915">"ניקוי"</string>
<string name="allow" msgid="8885707816848569619">"אישור"</string>
<string name="deny" msgid="6040983710442068936">"דחייה"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index a7795b9..8f13004 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">他 <xliff:g id="COUNT_1">^1</xliff:g> 件の項目</item>
<item quantity="one">他 <xliff:g id="COUNT_0">^1</xliff:g> 件の項目</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"アプリの一時ファイルの削除"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"アプリの一時ファイルを削除しますか?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>が一部の一時ファイルを削除する権限を求めています。この処理により、電池やモバイルデータの使用量が増えることがあります。"</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"アプリの一時ファイルを削除しています…"</string>
- <string name="clear" msgid="5524638938415865915">"削除"</string>
<string name="allow" msgid="8885707816848569619">"許可"</string>
<string name="deny" msgid="6040983710442068936">"許可しない"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 420e846..5a77cda 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">და კიდევ <xliff:g id="COUNT_1">^1</xliff:g> დამატებითი ერთეული</item>
<item quantity="one">და კიდევ <xliff:g id="COUNT_0">^1</xliff:g> დამატებითი ერთეული</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"გასუფთავდეს აპის დროებითი ფაილები?"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"გასუფთავდეს აპის დროებითი ფაილები?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-ს სურს, გაასუფთაოს დროებითი ფაილები. ამან შეიძლება ზოგიერთი ბატარეის ან მობილური ინტერნეტის გაზრდილი მოხმარება გამოიწვიოს."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"მიმდინარეობს აპის დროებითი ფაილების გასუფთავება…"</string>
- <string name="clear" msgid="5524638938415865915">"გასუფთავება"</string>
<string name="allow" msgid="8885707816848569619">"დაშვება"</string>
<string name="deny" msgid="6040983710442068936">"უარყოფა"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 1b899b2..efe23b2 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">Тағы <xliff:g id="COUNT_1">^1</xliff:g> элемент</item>
<item quantity="one">Тағы <xliff:g id="COUNT_0">^1</xliff:g> элемент</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Қолданбаның уақытша файлдарын өшіру"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Қолданбаның уақытша файлдарын өшіру керек пе?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> кейбір уақытша файлдарды өшіргісі келеді. Соған байланысты батарея тез отыруы немесе мобильдік интернет трафигі көп кетуі мүмкін."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Қолданбаның уақытша файлдары өшірілуде…"</string>
- <string name="clear" msgid="5524638938415865915">"Өшіру"</string>
<string name="allow" msgid="8885707816848569619">"Рұқсат ету"</string>
<string name="deny" msgid="6040983710442068936">"Тыйым салу"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index b4a930f..dbc2e18 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">បូករួមទាំងធាតុ <xliff:g id="COUNT_1">^1</xliff:g> បន្ថែមទៀត</item>
<item quantity="one">បូករួមទាំងធាតុ <xliff:g id="COUNT_0">^1</xliff:g> បន្ថែមទៀត</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"សម្អាតឯកសារកម្មវិធីបណ្ដោះអាសន្ន"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"សម្អាតឯកសារកម្មវិធីបណ្ដោះអាសន្នឬ?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ចង់សម្អាតឯកសារបណ្ដោះអាសន្នមួយចំនួន។ សកម្មភាពនេះអាចបណ្ដាលឱ្យការប្រើប្រាស់ថ្ម ឬទិន្នន័យទូរសព្ទចល័តមានការកើនឡើង។"</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"កំពុងសម្អាតឯកសារកម្មវិធីបណ្ដោះអាសន្ន…"</string>
- <string name="clear" msgid="5524638938415865915">"សម្អាត"</string>
<string name="allow" msgid="8885707816848569619">"អនុញ្ញាត"</string>
<string name="deny" msgid="6040983710442068936">"បដិសេធ"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index a39e574..45bde30 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -37,10 +37,8 @@
<item quantity="one">ಜೊತೆಗೆ <xliff:g id="COUNT_1">^1</xliff:g> ಹೆಚ್ಚುವರಿ ಐಟಂಗಳು</item>
<item quantity="other">ಜೊತೆಗೆ <xliff:g id="COUNT_1">^1</xliff:g> ಹೆಚ್ಚುವರಿ ಐಟಂಗಳು</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"ತಾತ್ಕಾಲಿಕ ಆ್ಯಪ್ ಫೈಲ್ಗಳನ್ನು ತೆರವುಗೊಳಿಸಿ"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"ತಾತ್ಕಾಲಿಕ ಆ್ಯಪ್ ಫೈಲ್ಗಳನ್ನು ತೆರವುಗೊಳಿಸಬೇಕೇ?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>, ಕೆಲವು ತಾತ್ಕಾಲಿಕ ಫೈಲ್ಗಳನ್ನು ತೆರವುಗೊಳಿಸಲು ಬಯಸುತ್ತಿದೆ. ಬ್ಯಾಟರಿ ಅಥವಾ ಸೆಲ್ಯುಲರ್ ಡೇಟಾ ಹೆಚ್ಚು ಬಳಕೆಯಾಗಲು ಇದು ಕಾರಣವಾಗಬಹುದು."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"ತಾತ್ಕಾಲಿಕ ಆ್ಯಪ್ ಫೈಲ್ಗಳನ್ನು ತೆರವುಗೊಳಿಸಲಾಗುತ್ತಿದೆ…"</string>
- <string name="clear" msgid="5524638938415865915">"ತೆರವುಗೊಳಿಸಿ"</string>
<string name="allow" msgid="8885707816848569619">"ಅನುಮತಿಸಿ"</string>
<string name="deny" msgid="6040983710442068936">"ನಿರಾಕರಿಸಿ"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 97fa4ec..c6ed4a1 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">그 외 항목 <xliff:g id="COUNT_1">^1</xliff:g>개</item>
<item quantity="one">그 외 항목 <xliff:g id="COUNT_0">^1</xliff:g>개</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"임시 앱 파일 삭제"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"임시 앱 파일을 삭제하시겠습니까?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>에서 일부 임시 파일을 삭제하려고 합니다. 이로 인해 배터리 또는 모바일 데이터 사용량이 늘어날 수 있습니다."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"임시 앱 파일 삭제 중…"</string>
- <string name="clear" msgid="5524638938415865915">"삭제"</string>
<string name="allow" msgid="8885707816848569619">"허용"</string>
<string name="deny" msgid="6040983710442068936">"거부"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 6198dba..5b6881c 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">Дагы <xliff:g id="COUNT_1">^1</xliff:g> нерсе</item>
<item quantity="one">Дагы <xliff:g id="COUNT_0">^1</xliff:g> нерсе</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Колдонмодогу убактылуу файлдарды өчүрүү"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Колдонмодогу убактылуу файлдарды өчүрөсүзбү?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> айрым убактылуу файлдарды өчүргөнү жатат. Батарея же мобилдик Интернет өтө көп колдонулушу мүмкүн."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Колдонмодогу убактылуу файлдар өчүрүлүүдө…"</string>
- <string name="clear" msgid="5524638938415865915">"Тазалоо"</string>
<string name="allow" msgid="8885707816848569619">"Уруксат берүү"</string>
<string name="deny" msgid="6040983710442068936">"Тыюу салуу"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 568d20a..4e490fd 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">ຮວມ <xliff:g id="COUNT_1">^1</xliff:g> ລາຍການເພີ່ມເຕີມ</item>
<item quantity="one">ຮວມ <xliff:g id="COUNT_0">^1</xliff:g> ລາຍການເພີ່ມເຕີມ</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"ລຶບລ້າງໄຟລ໌ແອັບຊົ່ວຄາວ"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"ລຶບລ້າງໄຟລ໌ແອັບຊົ່ວຄາວບໍ?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ຕ້ອງການລ້າງໄຟລ໌ຊົ່ວຄາວບາງຢ່າງອອກ. ນີ້ອາດເຮັດໃຫ້ມີການໃຊ້ແບັດເຕີຣີ ຫຼື ອິນເຕີເນັດມືຖືຫຼາຍຂຶ້ນ."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"ກຳລັງລຶບລ້າງໄຟລ໌ແອັບຊົ່ວຄາວ…"</string>
- <string name="clear" msgid="5524638938415865915">"ລຶບລ້າງ"</string>
<string name="allow" msgid="8885707816848569619">"ອະນຸຍາດ"</string>
<string name="deny" msgid="6040983710442068936">"ປະຕິເສດ"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 4190c0f..8adcbab 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -41,10 +41,8 @@
<item quantity="many">Dar <xliff:g id="COUNT_1">^1</xliff:g> papildomo elemento</item>
<item quantity="other">Dar <xliff:g id="COUNT_1">^1</xliff:g> papildomų elementų</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Laikinų programų failų išvalymas"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Išvalyti laikinus programų failus?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"Programa „<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“ nori išvalyti kai kuriuos laikinus failus. Dėl to gali būti suvartojama daugiau akumuliatoriaus energijos ar mobiliojo ryšio duomenų."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Išvalomi laikini programos failai…"</string>
- <string name="clear" msgid="5524638938415865915">"Išvalyti"</string>
<string name="allow" msgid="8885707816848569619">"Leisti"</string>
<string name="deny" msgid="6040983710442068936">"Atmesti"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 7bc296f..f12414b 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -39,10 +39,8 @@
<item quantity="one">Un vēl <xliff:g id="COUNT_1">^1</xliff:g> vienums</item>
<item quantity="other">Un vēl <xliff:g id="COUNT_1">^1</xliff:g> vienumi</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Lietotņu pagaidu failu notīrīšana"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Vai notīrīt lietotņu pagaidu failus?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"Lietotne <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vēlas iegūt atļauju notīrīt dažus pagaidu failus. Tas var palielināt akumulatora izmantošanas ilgumu vai mobilo datu lietojumu."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Notiek lietotņu pagaidu failu notīrīšana…"</string>
- <string name="clear" msgid="5524638938415865915">"Notīrīt"</string>
<string name="allow" msgid="8885707816848569619">"Atļaut"</string>
<string name="deny" msgid="6040983710442068936">"Neatļaut"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 9dd0bf3..b136efb 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -37,10 +37,8 @@
<item quantity="one">Плус <xliff:g id="COUNT_1">^1</xliff:g> дополнителна ставка</item>
<item quantity="other">Плус <xliff:g id="COUNT_1">^1</xliff:g> дополнителни ставки</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Избриши ги привремените датотеки на апликацијата"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Да се избришат привремените датотеки на апликацијата?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> сака да избрише некои привремени датотеки. Поради тоа може да дојде до зголемено користење на батеријата или мобилниот интернет."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Се бришат привремените датотеки на апликацијата…"</string>
- <string name="clear" msgid="5524638938415865915">"Избриши"</string>
<string name="allow" msgid="8885707816848569619">"Дозволи"</string>
<string name="deny" msgid="6040983710442068936">"Одбиј"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 05d8624..a556f66 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other"><xliff:g id="COUNT_1">^1</xliff:g> അധിക ഇനങ്ങൾ കൂടി</item>
<item quantity="one"><xliff:g id="COUNT_0">^1</xliff:g> അധിക ഇനം കൂടി</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"താൽക്കാലികമായ ആപ്പ് ഫയലുകൾ മായ്ക്കുക"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"താൽക്കാലികമായ ആപ്പ് ഫയലുകൾ മായ്ക്കണോ?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> എന്നതിന് കുറച്ച് താൽക്കാലിക ഫയലുകൾ മായ്ക്കണമെന്നുണ്ട്. ബാറ്ററിയുടെയോ സെല്ലുലാർ ഡാറ്റയുടെയോ വർദ്ധിച്ച ഉപയോഗത്തിന് ഇത് കാരണമായേക്കാം."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"താൽക്കാലികമായ ആപ്പ് ഫയലുകൾ മായ്ക്കുന്നു…"</string>
- <string name="clear" msgid="5524638938415865915">"മായ്ക്കുക"</string>
<string name="allow" msgid="8885707816848569619">"അനുവദിക്കൂ"</string>
<string name="deny" msgid="6040983710442068936">"നിരസിക്കുക"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 8c7a39b..0e5dc0d 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">Нэмээд нэмэлт <xliff:g id="COUNT_1">^1</xliff:g> зүйл</item>
<item quantity="one">Нэмээд нэмэлт <xliff:g id="COUNT_0">^1</xliff:g> зүйл</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Аппын түр зуурын файлуудыг арилгах"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Аппын түр зуурын файлуудыг арилгах уу?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> нь түр зуурын зарим файлыг арилгах хүсэлтэй байна. Энэ нь батарей эсвэл мобайл дата ашиглалтыг нэмэгдүүлэхэд хүргэж болзошгүй."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Аппын түр зуурын файлуудыг арилгаж байна…"</string>
- <string name="clear" msgid="5524638938415865915">"Арилгах"</string>
<string name="allow" msgid="8885707816848569619">"Зөвшөөрөх"</string>
<string name="deny" msgid="6040983710442068936">"Татгалзах"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 9a6b24e..331ee53 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">आणखी <xliff:g id="COUNT_1">^1</xliff:g> अतिरिक्त आयटम</item>
<item quantity="one">आणखी <xliff:g id="COUNT_0">^1</xliff:g> अतिरिक्त आयटम</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"तात्पुरत्या अॅप फाइल साफ करा"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"तात्पुरत्या अॅप फाइल काढून टाकायच्या आहेत का?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ला काही तात्पुरत्या फाइल साफ करायच्या आहेत. यामुळे बॅटरी किंवा सेल्युलर डेटाच्या वापरात वाढ होऊ शकते."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"तात्पुरत्या ॲप फाइल काढून टाकत आहे…"</string>
- <string name="clear" msgid="5524638938415865915">"साफ करा"</string>
<string name="allow" msgid="8885707816848569619">"अनुमती द्या"</string>
<string name="deny" msgid="6040983710442068936">"नकार द्या"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 067cc43..6846e67 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">Serta <xliff:g id="COUNT_1">^1</xliff:g> item tambahan</item>
<item quantity="one">Serta <xliff:g id="COUNT_0">^1</xliff:g> item tambahan</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Kosongkan fail apl sementara"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Kosongkan fail apl sementara?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> mahu mengosongkan beberapa fail sementara. Tindakan ini mungkin menyebabkan peningkatan penggunaan bateri atau data selular."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Mengosongkan fail apl sementara…"</string>
- <string name="clear" msgid="5524638938415865915">"Kosongkan"</string>
<string name="allow" msgid="8885707816848569619">"Benarkan"</string>
<string name="deny" msgid="6040983710442068936">"Tolak"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index ab3766c..a10071e 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">ထပ်ပေါင်းအရာ <xliff:g id="COUNT_1">^1</xliff:g> ခု အပါအဝင်</item>
<item quantity="one">ထပ်ပေါင်းအရာ <xliff:g id="COUNT_0">^1</xliff:g> ခု အပါအဝင်</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"ယာယီအက်ပ်ဖိုင်များ ရှင်းရန်"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"ယာယီအက်ပ်ဖိုင်များ ရှင်းထုတ်မလား။"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> သည် ယာယီဖိုင်များမှ အချို့ကို ရှင်းထုတ်လိုသည်။ ထို့ကြောင့် ဘက်ထရီ သို့မဟုတ် ဆယ်လူလာဒေတာ အသုံးပြုမှု မြင့်တက်လာနိုင်သည်။"</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"ယာယီအက်ပ်ဖိုင်များ ရှင်းထုတ်နေသည်…"</string>
- <string name="clear" msgid="5524638938415865915">"ရှင်းရန်"</string>
<string name="allow" msgid="8885707816848569619">"ခွင့်ပြုရန်"</string>
<string name="deny" msgid="6040983710442068936">"ပယ်ရန်"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 35fd892..d3b049d 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">Pluss <xliff:g id="COUNT_1">^1</xliff:g> elementer til</item>
<item quantity="one">Pluss <xliff:g id="COUNT_0">^1</xliff:g> element til</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Slett midlertidige appfiler"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Vil du slette midlertidige appfiler?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vil slette noen midlertidige filer. Dette kan resultere i økt bruk av batteri eller mobildata."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Sletter midlertidige appfiler …"</string>
- <string name="clear" msgid="5524638938415865915">"Slett"</string>
<string name="allow" msgid="8885707816848569619">"Tillat"</string>
<string name="deny" msgid="6040983710442068936">"Avvis"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 0735ae7..9658899 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">थप <xliff:g id="COUNT_1">^1</xliff:g> वटा अतिरिक्त वस्तु</item>
<item quantity="one">थप <xliff:g id="COUNT_0">^1</xliff:g> अतिरिक्त वस्तु</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"एपका अस्थायी फाइलहरू हटाउनुहोस्"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"अनुप्रयोगका अस्थायी फाइलहरू मेटाउने हो?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> केही अस्थायी फाइलहरू मेटाउन चाहन्छ। यो कार्य गर्ने अनुमति दिनुभएका खण्डमा ब्याट्रीको खपत वा सेलुलर डेटाको प्रयोग बढ्न सक्छ।"</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"एपका अस्थायी फाइलहरू मेटाउँदै…"</string>
- <string name="clear" msgid="5524638938415865915">"हटाउनुहोस्"</string>
<string name="allow" msgid="8885707816848569619">"अनुमति दिनुहोस्"</string>
<string name="deny" msgid="6040983710442068936">"अस्वीकार गर्नुहोस्"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
@@ -52,8 +50,8 @@
<item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो भिडियो परिमार्जन गर्न दिने हो?</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>
+ <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> लाई यी <xliff:g id="COUNT">^2</xliff:g> तस्बिरहरू परिमार्जन गर्न दिने हो?</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो तस्बिर परिमार्जन गर्न दिने हो?</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>
@@ -68,8 +66,8 @@
<item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो भिडियो सारेर रद्दीको टोकरीमा लैजान दिने हो?</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>
+ <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> लाई यी <xliff:g id="COUNT">^2</xliff:g> तस्बिरहरू सारेर रद्दीको टोकरीमा लैजान दिने हो?</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो तस्बिर सारेर रद्दीको टोकरीमा लैजान दिने हो?</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>
@@ -84,8 +82,8 @@
<item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो भिडियो रद्दीको टोकरीबाट निकालेर ल्याउन दिने हो?</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>
+ <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> लाई यी <xliff:g id="COUNT">^2</xliff:g> तस्बिरहरू रद्दीको टोकरीबाट निकालेर ल्याउन दिने हो?</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो तस्बिर रद्दीको टोकरीबाट निकालेर ल्याउन दिने हो?</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>
@@ -100,8 +98,8 @@
<item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो भिडियो मेटाउन दिने हो?</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>
+ <item quantity="other"><xliff:g id="APP_NAME_1">^1</xliff:g> लाई यी <xliff:g id="COUNT">^2</xliff:g> तस्बिरहरू मेटाउन दिने हो?</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">^1</xliff:g> लाई यो तस्बिर मेटाउन दिने हो?</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>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index d685675..69e344b 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -25,7 +25,7 @@
<string name="root_videos" msgid="8792703517064649453">"Video\'s"</string>
<string name="root_audio" msgid="3505830755201326018">"Audio"</string>
<string name="root_documents" msgid="3829103301363849237">"Documenten"</string>
- <string name="permission_required" msgid="1460820436132943754">"Rechten vereist om dit item aan te passen of te verwijderen."</string>
+ <string name="permission_required" msgid="1460820436132943754">"Toestemming vereist om dit item aan te passen of te verwijderen."</string>
<string name="permission_required_action" msgid="706370952366113539">"Doorgaan"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"Toestaan"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"Weigeren"</string>
@@ -37,10 +37,8 @@
<item quantity="other">Plus <xliff:g id="COUNT_1">^1</xliff:g> extra items</item>
<item quantity="one">Plus <xliff:g id="COUNT_0">^1</xliff:g> extra item</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Tijdelijke app-bestanden wissen"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Tijdelijke app-bestanden wissen?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> wil een aantal tijdelijke bestanden wissen. Dit kan leiden tot een groter verbruik van de batterij of mobiele data."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Tijdelijke app-bestanden wissen…"</string>
- <string name="clear" msgid="5524638938415865915">"Wissen"</string>
<string name="allow" msgid="8885707816848569619">"Toestaan"</string>
<string name="deny" msgid="6040983710442068936">"Weigeren"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 86491af..ea938eb 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -28,7 +28,7 @@
<string name="permission_required" msgid="1460820436132943754">"ଏହି ଆଇଟମ୍କୁ ସଂଶୋଧନ କିମ୍ବା ଡିଲିଟ୍ କରିବାକୁ ଅନୁମତି ଆବଶ୍ୟକ।"</string>
<string name="permission_required_action" msgid="706370952366113539">"ଜାରି ରଖନ୍ତୁ"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"ଅନୁମତି ଦିଅନ୍ତୁ"</string>
- <string name="grant_dialog_button_deny" msgid="6190589471415815741">"ଅଗ୍ରାହ୍ୟ କରନ୍ତୁ"</string>
+ <string name="grant_dialog_button_deny" msgid="6190589471415815741">"ଅସ୍ଵୀକାର କରନ୍ତୁ"</string>
<plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
<item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
<item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
@@ -37,12 +37,10 @@
<item quantity="other">ଅଧିକ <xliff:g id="COUNT_1">^1</xliff:g>ଟି ଅତିରିକ୍ତ ଆଇଟମ୍</item>
<item quantity="one">ଅଧିକ <xliff:g id="COUNT_0">^1</xliff:g>ଟି ଅତିରିକ୍ତ ଆଇଟମ୍</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"ଅସ୍ଥାୟୀ ଆପ୍ ଫାଇଲଗୁଡ଼ିକୁ ଖାଲି କରନ୍ତୁ"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"ଅସ୍ଥାୟୀ ଆପ୍ ଫାଇଲଗୁଡ଼ିକୁ ଖାଲି କରିବେ?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> କିଛି ଅସ୍ଥାୟୀ ଫାଇଲ୍ ଖାଲି କରିବାକୁ ଚାହୁଁଛି। ଏହା ଫଳରେ ବ୍ୟାଟେରୀ କିମ୍ବା ସେଲ୍ୟୁଲାର୍ ଡାଟାର ବ୍ୟବହାର ଅଧିକ ହୋଇପାରେ।"</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"ଅସ୍ଥାୟୀ ଆପ୍ ଫାଇଲଗୁଡ଼ିକୁ ଖାଲି କରାଯାଉଛି…"</string>
- <string name="clear" msgid="5524638938415865915">"ଖାଲି କରନ୍ତୁ"</string>
<string name="allow" msgid="8885707816848569619">"ଅନୁମତି ଦିଅନ୍ତୁ"</string>
- <string name="deny" msgid="6040983710442068936">"ଅଗ୍ରାହ୍ୟ କରନ୍ତୁ"</string>
+ <string name="deny" msgid="6040983710442068936">"ପ୍ରତ୍ୟାଖ୍ୟାନ କରନ୍ତୁ"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
<item quantity="other"><xliff:g id="COUNT">^2</xliff:g>ଟି ଅଡିଓ ଫାଇଲକୁ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ <xliff:g id="APP_NAME_1">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
<item quantity="one">ଏହି ଅଡିଓ ଫାଇଲକୁ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">^1</xliff:g>କୁ ଅନୁମତି ଦେବେ?</item>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index cb5e9b4..1efefcd 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/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>
@@ -37,10 +37,8 @@
<item quantity="one">ਇਸ ਤੋਂ ਇਲਾਵਾ <xliff:g id="COUNT_1">^1</xliff:g> ਵਾਧੂ ਆਈਟਮ</item>
<item quantity="other">ਇਸ ਤੋਂ ਇਲਾਵਾ <xliff:g id="COUNT_1">^1</xliff:g> ਵਾਧੂ ਆਈਟਮਾਂ</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"ਅਸਥਾਈ ਐਪ ਫ਼ਾਈਲਾਂ ਨੂੰ ਕਲੀਅਰ ਕਰੋ"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"ਕੀ ਅਸਥਾਈ ਐਪ ਫ਼ਾਈਲਾਂ ਨੂੰ ਕਲੀਅਰ ਕਰਨਾ ਹੈ?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ਕੁਝ ਅਸਥਾਈ ਫ਼ਾਈਲਾਂ ਕਲੀਅਰ ਕਰਨਾ ਚਾਹੁੰਦੀ ਹੈ। ਇਸਦੇ ਨਤੀਜੇ ਵਜੋਂ ਬੈਟਰੀ ਜਾਂ ਸੈਲਿਊਲਰ ਡਾਟਾ ਦੀ ਵਰਤੋਂ ਵਧ ਸਕਦੀ ਹੈ।"</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"ਅਸਥਾਈ ਐਪ ਫ਼ਾਈਲਾਂ ਨੂੰ ਕਲੀਅਰ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
- <string name="clear" msgid="5524638938415865915">"ਕਲੀਅਰ ਕਰੋ"</string>
<string name="allow" msgid="8885707816848569619">"ਆਗਿਆ ਦਿਓ"</string>
<string name="deny" msgid="6040983710442068936">"ਮਨ੍ਹਾ ਕਰੋ"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index ea4a8ce..09eac9f 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -21,7 +21,7 @@
<string name="app_label" msgid="9035307001052716210">"Przechowywanie multimediów"</string>
<string name="artist_label" msgid="8105600993099120273">"Wykonawca"</string>
<string name="unknown" msgid="2059049215682829375">"Nieznany"</string>
- <string name="root_images" msgid="5861633549189045666">"Obrazy"</string>
+ <string name="root_images" msgid="5861633549189045666">"Grafika"</string>
<string name="root_videos" msgid="8792703517064649453">"Filmy"</string>
<string name="root_audio" msgid="3505830755201326018">"Dźwięk"</string>
<string name="root_documents" msgid="3829103301363849237">"Dokumenty"</string>
@@ -41,10 +41,8 @@
<item quantity="other">I <xliff:g id="COUNT_1">^1</xliff:g> dodatkowego elementu</item>
<item quantity="one">I <xliff:g id="COUNT_0">^1</xliff:g> dodatkowy element</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Usuwanie tymczasowych plików aplikacji"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Usunąć tymczasowe pliki aplikacji?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"Aplikacja <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> chce usunąć niektóre pliki tymczasowe. Może to spowodować zwiększenie wykorzystania baterii lub komórkowej transmisji danych."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Usuwam tymczasowe pliki aplikacji…"</string>
- <string name="clear" msgid="5524638938415865915">"Wyczyść"</string>
<string name="allow" msgid="8885707816848569619">"Zezwól"</string>
<string name="deny" msgid="6040983710442068936">"Odmów"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-pt-rBR/strings.xml b/res/values-pt-rBR/strings.xml
index d0ff6d6..47b3beb 100644
--- a/res/values-pt-rBR/strings.xml
+++ b/res/values-pt-rBR/strings.xml
@@ -37,10 +37,8 @@
<item quantity="one">Mais <xliff:g id="COUNT_1">^1</xliff:g> item extra</item>
<item quantity="other">Mais <xliff:g id="COUNT_1">^1</xliff:g> itens extras</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Limpar arquivos temporários de apps"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Limpar arquivos temporários de apps?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"O app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> quer apagar alguns arquivos temporários. Isso pode aumentar o uso de bateria ou de dados da rede celular."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Limpando arquivos temporários de apps…"</string>
- <string name="clear" msgid="5524638938415865915">"Limpar"</string>
<string name="allow" msgid="8885707816848569619">"Permitir"</string>
<string name="deny" msgid="6040983710442068936">"Negar"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 27c1cb0..00c1c13 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -37,74 +37,72 @@
<item quantity="other">E <xliff:g id="COUNT_1">^1</xliff:g> itens adicionais</item>
<item quantity="one">E <xliff:g id="COUNT_0">^1</xliff:g> item adicional</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Limpe ficheiros de apps temporários"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Pretende limpar os ficheiros temporários da app?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"A app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> pretende limpar alguns ficheiros temporários. Isto pode resultar num aumento da utilização da bateria ou dos dados móveis."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"A limpar ficheiros temporários da app…"</string>
- <string name="clear" msgid="5524638938415865915">"Limpar"</string>
<string name="allow" msgid="8885707816848569619">"Permitir"</string>
<string name="deny" msgid="6040983710442068936">"Recusar"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other">Permitir que a app <xliff:g id="APP_NAME_1">^1</xliff:g> modifique <xliff:g id="COUNT">^2</xliff:g> ficheiros de áudio?</item>
- <item quantity="one">Permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este ficheiro de áudio?</item>
+ <item quantity="other">Pretende 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">Pretende permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este 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>
+ <item quantity="other">Pretende 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">Pretende permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este 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>
+ <item quantity="other">Pretende 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">Pretende permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> modifique esta 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>
+ <item quantity="other">Pretende 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">Pretende permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> modifique este 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>
+ <item quantity="other">Pretende 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">Pretende 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_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>
+ <item quantity="other">Pretende 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">Pretende 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_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>
+ <item quantity="other">Pretende 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">Pretende permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> mova esta 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>
+ <item quantity="other">Pretende 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">Pretende permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> mova este 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>
+ <item quantity="other">Pretende 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">Pretende 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_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>
+ <item quantity="other">Pretende 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">Pretende permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> retire este 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>
+ <item quantity="other">Pretende 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">Pretende permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> retire esta 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>
+ <item quantity="other">Pretende 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">Pretende permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> retire este 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>
+ <item quantity="other">Pretende 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">Pretende permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> elimine este 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>
+ <item quantity="other">Pretende 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">Pretende permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> elimine este 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>
+ <item quantity="other">Pretende 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">Pretende permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> elimine esta 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>
+ <item quantity="other">Pretende 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">Pretende permitir que a app <xliff:g id="APP_NAME_0">^1</xliff:g> elimine este item?</item>
</plurals>
</resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index d0ff6d6..47b3beb 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -37,10 +37,8 @@
<item quantity="one">Mais <xliff:g id="COUNT_1">^1</xliff:g> item extra</item>
<item quantity="other">Mais <xliff:g id="COUNT_1">^1</xliff:g> itens extras</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Limpar arquivos temporários de apps"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Limpar arquivos temporários de apps?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"O app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> quer apagar alguns arquivos temporários. Isso pode aumentar o uso de bateria ou de dados da rede celular."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Limpando arquivos temporários de apps…"</string>
- <string name="clear" msgid="5524638938415865915">"Limpar"</string>
<string name="allow" msgid="8885707816848569619">"Permitir"</string>
<string name="deny" msgid="6040983710442068936">"Negar"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 8df20b8..9d6efda 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -39,10 +39,8 @@
<item quantity="other">Și încă <xliff:g id="COUNT_1">^1</xliff:g> de elemente</item>
<item quantity="one">Și încă <xliff:g id="COUNT_0">^1</xliff:g> element</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Ștergeți fișierele temporare ale aplicațiilor"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Ștergeți fișierele temporare ale aplicațiilor?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vrea să șteargă fișiere temporare. Aceasta poate duce la creșterea gradului de utilizare a bateriei sau a datelor mobile."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Se șterg fișierele temporare ale aplicațiilor…"</string>
- <string name="clear" msgid="5524638938415865915">"Ștergeți"</string>
<string name="allow" msgid="8885707816848569619">"Permiteți"</string>
<string name="deny" msgid="6040983710442068936">"Refuzați"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index d7a1aaa..3dd6f19 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -41,10 +41,8 @@
<item quantity="many">и ещё <xliff:g id="COUNT_1">^1</xliff:g> объектов</item>
<item quantity="other">и ещё <xliff:g id="COUNT_1">^1</xliff:g> объекта</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Удалить временные файлы приложений"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Удалить временные файлы приложения?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"Приложение \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" запрашивает разрешение на удаление временных файлов. Это может увеличить расход заряда или трафика."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Удаление временных файлов приложения…"</string>
- <string name="clear" msgid="5524638938415865915">"Удалить"</string>
<string name="allow" msgid="8885707816848569619">"Разрешить"</string>
<string name="deny" msgid="6040983710442068936">"Запретить"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index ad22297..932e979 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -37,10 +37,8 @@
<item quantity="one">සහ අමතර අයිතම <xliff:g id="COUNT_1">^1</xliff:g></item>
<item quantity="other">සහ අමතර අයිතම <xliff:g id="COUNT_1">^1</xliff:g></item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"තාවකාලික යෙදුම් ගොනු හිස් කරන්න"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"තාවකාලික යෙදුම් ගොනු හිස් කරන්නද?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> සමහර තාවකාලික ගොනු හිස් කිරීමට කැමතියි. මෙය බැටරි බලයේ හෝ සෙලියුලර් දත්තවල වැඩි භාවිතයකට හේතු විය හැකිය."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"තාවකාලික යෙදුම් ගොනු හිස් කරමින්…"</string>
- <string name="clear" msgid="5524638938415865915">"හිස් කරන්න"</string>
<string name="allow" msgid="8885707816848569619">"ඉඩ දෙන්න"</string>
<string name="deny" msgid="6040983710442068936">"ප්රතික්ෂේප කරන්න"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 359ba24..fb343c2 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -41,10 +41,8 @@
<item quantity="other">A <xliff:g id="COUNT_1">^1</xliff:g> ďalších položiek</item>
<item quantity="one">A <xliff:g id="COUNT_0">^1</xliff:g> ďalšia položka</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Vymazanie dočasných súborov aplikácií"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Chcete vymazať dočasné súbory aplikácie?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"Aplikácia <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> chce vymazať niekoľko dočasných súborov. To môže viesť k vyššiemu využívaniu batérie alebo mobilných dát."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Odstraňujú sa dočasné súbory aplikácie…"</string>
- <string name="clear" msgid="5524638938415865915">"Vymazať"</string>
<string name="allow" msgid="8885707816848569619">"Povoliť"</string>
<string name="deny" msgid="6040983710442068936">"Zamietnuť"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index ff63498..ec2ac11 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -41,10 +41,8 @@
<item quantity="few">In še <xliff:g id="COUNT_1">^1</xliff:g> dodatni elementi</item>
<item quantity="other">In še <xliff:g id="COUNT_1">^1</xliff:g> dodatnih elementov</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Brisanje začasnih datotek aplikacij"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Želite izbrisati začasne datoteke aplikacij?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"Aplikacija <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> želi izbrisati nekaj začasnih datotek. To lahko povzroči povečano porabo energije baterije ali povečan prenos podatkov v mobilnem omrežju."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Brisanje začasnih datotek aplikacij …"</string>
- <string name="clear" msgid="5524638938415865915">"Počisti"</string>
<string name="allow" msgid="8885707816848569619">"Dovoli"</string>
<string name="deny" msgid="6040983710442068936">"Zavrni"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 1e90f35..b8e5714 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">Dhe <xliff:g id="COUNT_1">^1</xliff:g> artikuj të tjerë</item>
<item quantity="one">Dhe <xliff:g id="COUNT_0">^1</xliff:g> artikull tjetër</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Pastro skedarët e përkohshëm të aplikacioneve"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Të pastrohen skedarët e përkohshëm të aplikacioneve?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> dëshiron të pastrojë disa skedarë të përkohshëm. Kjo mund të rezultojë në një rritje të përdorimit të baterisë ose të të dhënave celulare."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Skedarët e përkohshëm të aplikacioneve po pastrohen…"</string>
- <string name="clear" msgid="5524638938415865915">"Pastro"</string>
<string name="allow" msgid="8885707816848569619">"Lejo"</string>
<string name="deny" msgid="6040983710442068936">"Refuzo"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 079f9aa..b6cf4dd 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -39,10 +39,8 @@
<item quantity="few">И још <xliff:g id="COUNT_1">^1</xliff:g> ставке</item>
<item quantity="other">И још <xliff:g id="COUNT_1">^1</xliff:g> ставки</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Обришите привремене датотеке апликација"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Желите ли да обришете привремене датотеке апликација?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> жели да обрише неке привремене датотеке. Ово може да доведе до повећане потрошње батерије или мобилних података."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Бришу се привремене датотеке апликација…"</string>
- <string name="clear" msgid="5524638938415865915">"Обриши"</string>
<string name="allow" msgid="8885707816848569619">"Дозволи"</string>
<string name="deny" msgid="6040983710442068936">"Одбиј"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index b0b990c..51a5d6c 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -22,7 +22,7 @@
<string name="artist_label" msgid="8105600993099120273">"Artist"</string>
<string name="unknown" msgid="2059049215682829375">"Okänd"</string>
<string name="root_images" msgid="5861633549189045666">"Bilder"</string>
- <string name="root_videos" msgid="8792703517064649453">"Videor"</string>
+ <string name="root_videos" msgid="8792703517064649453">"Videoklipp"</string>
<string name="root_audio" msgid="3505830755201326018">"Ljud"</string>
<string name="root_documents" msgid="3829103301363849237">"Dokument"</string>
<string name="permission_required" msgid="1460820436132943754">"Behörighet krävs för att ändra eller radera objektet."</string>
@@ -37,10 +37,8 @@
<item quantity="other">och <xliff:g id="COUNT_1">^1</xliff:g> objekt till</item>
<item quantity="one">och <xliff:g id="COUNT_0">^1</xliff:g> objekt till</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Rensa tillfälliga appfiler"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Vill du ta bort tillfälliga appfiler?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vill ta bort några tillfälliga filer. Det kan leda till ökad batteriförbrukning eller användning av mobildata."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Tillfälliga appfiler rensas …"</string>
- <string name="clear" msgid="5524638938415865915">"Rensa"</string>
<string name="allow" msgid="8885707816848569619">"Tillåt"</string>
<string name="deny" msgid="6040983710442068936">"Neka"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 4917e30..f66d910 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">Pamoja na vipengee <xliff:g id="COUNT_1">^1</xliff:g> zaidi</item>
<item quantity="one">Pamoja na kipengee <xliff:g id="COUNT_0">^1</xliff:g> zaidi</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Futa faili za muda za programu"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Ungependa kufuta faili za muda za programu?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ingependa kufuta baadhi ya faili za muda. Huenda hii ikasababisha kuongezeka kwa matumizi ya betri au data ya mtandao wa simu."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Inafuta faili za muda za programu…"</string>
- <string name="clear" msgid="5524638938415865915">"Futa"</string>
<string name="allow" msgid="8885707816848569619">"Ruhusu"</string>
<string name="deny" msgid="6040983710442068936">"Kataa"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index a98d701..8e3c9c7 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -29,82 +29,31 @@
<string name="permission_required_action" msgid="706370952366113539">"தொடர்க"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"அனுமதி"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"நிராகரி"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
+ <!-- no translation found for permission_more_thumb (4392079224649478923) -->
<plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
<item quantity="other">அத்துடன் கூடுதலாக <xliff:g id="COUNT_1">^1</xliff:g></item>
<item quantity="one">அத்துடன் கூடுதலாக <xliff:g id="COUNT_0">^1</xliff:g></item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"ஆப்ஸின் தற்காலிக ஃபைல்களை அழித்தல்"</string>
- <string name="cache_clearing_dialog_text" msgid="7057784635111940957">"சில தற்காலிக ஃபைல்களை நீக்குவதற்கு <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> அனுமதி கேட்கின்றது. இதனால் பேட்டரி அல்லது மொபைல் டேட்டா பயன்பாடு அதிகரிக்கக்கூடும்."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"ஆப்ஸிலுள்ள தற்காலிக ஃபைல்களை அழிக்கிறது…"</string>
- <string name="clear" msgid="5524638938415865915">"அழி"</string>
+ <!-- no translation found for cache_clearing_dialog_title (4672878017407595782) -->
+ <skip />
+ <!-- no translation found for cache_clearing_dialog_text (7057784635111940957) -->
+ <skip />
<string name="allow" msgid="8885707816848569619">"அனுமதி"</string>
<string name="deny" msgid="6040983710442068936">"நிராகரி"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ஆடியோ ஃபைல்களில் மாற்றங்களைச் செய்ய <xliff:g id="APP_NAME_1">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- <item quantity="one">இந்த ஆடியோ ஃபைலில் மாற்றங்களைச் செய்ய <xliff:g id="APP_NAME_0">^1</xliff:g> ஆப்ஸை அனுமதிக்கவா?</item>
- </plurals>
- <plurals name="permission_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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>
+ <!-- no translation found for permission_write_audio (8914759422381305478) -->
+ <!-- no translation found for permission_write_video (1098082003326873084) -->
+ <!-- no translation found for permission_write_image (748745548893845892) -->
+ <!-- no translation found for permission_write_generic (3270172714743671779) -->
+ <!-- no translation found for permission_trash_audio (8907813869381755423) -->
+ <!-- no translation found for permission_trash_video (4672871911555787438) -->
+ <!-- no translation found for permission_trash_image (6400475304599873227) -->
+ <!-- no translation found for permission_trash_generic (3814167365075039711) -->
+ <!-- no translation found for permission_untrash_audio (7795265980168966321) -->
+ <!-- no translation found for permission_untrash_video (332894888445508879) -->
+ <!-- no translation found for permission_untrash_image (7024071378733595056) -->
+ <!-- no translation found for permission_untrash_generic (6872817093731198374) -->
+ <!-- no translation found for permission_delete_audio (6848547621165184719) -->
+ <!-- no translation found for permission_delete_video (1251942606336748563) -->
+ <!-- no translation found for permission_delete_image (2303409455224710111) -->
+ <!-- no translation found for permission_delete_generic (1412218850351841181) -->
</resources>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 30c0faa..d5ed82c 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/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>
@@ -29,82 +29,31 @@
<string name="permission_required_action" msgid="706370952366113539">"కొనసాగించు"</string>
<string name="grant_dialog_button_allow" msgid="1644287024501033471">"అనుమతించు"</string>
<string name="grant_dialog_button_deny" msgid="6190589471415815741">"తిరస్కరించు"</string>
- <plurals name="permission_more_thumb" formatted="false" msgid="4392079224649478923">
- <item quantity="other">+<xliff:g id="COUNT_1">^1</xliff:g></item>
- <item quantity="one">+<xliff:g id="COUNT_0">^1</xliff:g></item>
- </plurals>
+ <!-- no translation found for permission_more_thumb (4392079224649478923) -->
<plurals name="permission_more_text" formatted="false" msgid="7291997297174507324">
<item quantity="other">దానితో పాటు, <xliff:g id="COUNT_1">^1</xliff:g> అదనపు అంశాలు</item>
<item quantity="one">దానితో పాటు, <xliff:g id="COUNT_0">^1</xliff:g> అదనపు అంశం</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"తాత్కాలిక యాప్ ఫైల్లను క్లియర్ చేయండి"</string>
- <string name="cache_clearing_dialog_text" msgid="7057784635111940957">"కొన్ని తాత్కాలిక ఫైల్స్ను క్లియర్ చేయడానికి <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> అనుమతి కోరుతోంది. దీని వలన బ్యాటరీ లేదా సెల్యూలార్ డేటా వినియోగం పెరగవచ్చు."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"తాత్కాలిక యాప్ ఫైల్లను క్లియర్ చేస్తోంది…"</string>
- <string name="clear" msgid="5524638938415865915">"క్లియర్ చేయండి"</string>
+ <!-- no translation found for cache_clearing_dialog_title (4672878017407595782) -->
+ <skip />
+ <!-- no translation found for cache_clearing_dialog_text (7057784635111940957) -->
+ <skip />
<string name="allow" msgid="8885707816848569619">"అనుమతించు"</string>
<string name="deny" msgid="6040983710442068936">"నిరాకరించు"</string>
- <plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
- <item quantity="other"><xliff:g id="COUNT">^2</xliff:g> ఆడియో ఫైల్లను సవరించడానికి <xliff:g id="APP_NAME_1">^1</xliff:g>ను అనుమతించాలా?</item>
- <item quantity="one">ఈ ఆడియో ఫైల్ను సవరించడానికి <xliff:g id="APP_NAME_0">^1</xliff:g>ను అనుమతించాలా?</item>
- </plurals>
- <plurals name="permission_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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>
+ <!-- no translation found for permission_write_audio (8914759422381305478) -->
+ <!-- no translation found for permission_write_video (1098082003326873084) -->
+ <!-- no translation found for permission_write_image (748745548893845892) -->
+ <!-- no translation found for permission_write_generic (3270172714743671779) -->
+ <!-- no translation found for permission_trash_audio (8907813869381755423) -->
+ <!-- no translation found for permission_trash_video (4672871911555787438) -->
+ <!-- no translation found for permission_trash_image (6400475304599873227) -->
+ <!-- no translation found for permission_trash_generic (3814167365075039711) -->
+ <!-- no translation found for permission_untrash_audio (7795265980168966321) -->
+ <!-- no translation found for permission_untrash_video (332894888445508879) -->
+ <!-- no translation found for permission_untrash_image (7024071378733595056) -->
+ <!-- no translation found for permission_untrash_generic (6872817093731198374) -->
+ <!-- no translation found for permission_delete_audio (6848547621165184719) -->
+ <!-- no translation found for permission_delete_video (1251942606336748563) -->
+ <!-- no translation found for permission_delete_image (2303409455224710111) -->
+ <!-- no translation found for permission_delete_generic (1412218850351841181) -->
</resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index db76bee..d015e15 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">และอีก <xliff:g id="COUNT_1">^1</xliff:g> รายการ</item>
<item quantity="one">และอีก <xliff:g id="COUNT_0">^1</xliff:g> รายการ</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"ล้างไฟล์แอปชั่วคราว"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"ล้างไฟล์แอปชั่วคราวใช่ไหม"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ต้องการล้างไฟล์ชั่วคราวบางไฟล์ ซึ่งอาจส่งผลให้ใช้แบตเตอรี่หรืออินเทอร์เน็ตมือถือมากขึ้น"</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"กำลังล้างไฟล์แอปชั่วคราว…"</string>
- <string name="clear" msgid="5524638938415865915">"ล้าง"</string>
<string name="allow" msgid="8885707816848569619">"อนุญาต"</string>
<string name="deny" msgid="6040983710442068936">"ปฏิเสธ"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 79452b3..a58d95c 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -37,10 +37,8 @@
<item quantity="one">At <xliff:g id="COUNT_1">^1</xliff:g> pang item</item>
<item quantity="other">At <xliff:g id="COUNT_1">^1</xliff:g> pang item</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"I-clear ang mga pansamantalang file ng app"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"I-clear ang mga pansamantalang file ng app?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"Gustong mag-clear ng <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ng ilang pansamantalang file. Baka lumakas ang paggamit ng baterya o cellular data dahil dito."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Kini-clear ang mga pansamantalang file ng app…"</string>
- <string name="clear" msgid="5524638938415865915">"I-clear"</string>
<string name="allow" msgid="8885707816848569619">"Payagan"</string>
<string name="deny" msgid="6040983710442068936">"Tanggihan"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 669eac9..893ffca 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">Ayrıca <xliff:g id="COUNT_1">^1</xliff:g> ek öğe</item>
<item quantity="one">Ayrıca <xliff:g id="COUNT_0">^1</xliff:g> ek öğe</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Geçici uygulama dosyalarını temizleyin"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Geçici uygulama dosyaları temizlensin mi?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> bazı geçici dosyaları temizlemek istiyor. Bu, pil veya hücresel veri kullanımında artışa yol açabilir."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Geçici uygulama dosyaları temizleniyor…"</string>
- <string name="clear" msgid="5524638938415865915">"Temizle"</string>
<string name="allow" msgid="8885707816848569619">"İzin ver"</string>
<string name="deny" msgid="6040983710442068936">"Reddet"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 281c8b2..155d247 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -41,10 +41,8 @@
<item quantity="many">Ще <xliff:g id="COUNT_1">^1</xliff:g> додаткових елементів</item>
<item quantity="other">Ще <xliff:g id="COUNT_1">^1</xliff:g> додаткового елемента</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Видалити тимчасові файли додатків"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Видалити тимчасові файли додатків?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"Додаток <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> хоче видалити деякі тимчасові файли. Це може збільшити використання акумулятора або мобільного трафіку."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Видалення тимчасових файлів додатків…"</string>
- <string name="clear" msgid="5524638938415865915">"Очистити"</string>
<string name="allow" msgid="8885707816848569619">"Дозволити"</string>
<string name="deny" msgid="6040983710442068936">"Заборонити"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index ad6cdf1..26f8b72 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">مزید <xliff:g id="COUNT_1">^1</xliff:g> اضافی آئٹمز</item>
<item quantity="one">مزید <xliff:g id="COUNT_0">^1</xliff:g> اضافی آئٹم</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"ایپ کی عارضی فائلز کو صاف کریں"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"ایپ کی عارضی فائلز کو صاف کریں؟"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> کچھ عارضی فائلز کو صاف کرنا چاہتی ہے۔ اس کی وجہ سے بیٹری یا سیلولر ڈیٹا کے استعمال میں اضافہ ہو سکتا ہے۔"</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"ایپ کی عارضی فائلز کو صاف کیا جا رہا ہے…"</string>
- <string name="clear" msgid="5524638938415865915">"صاف کریں"</string>
<string name="allow" msgid="8885707816848569619">"اجازت دیں"</string>
<string name="deny" msgid="6040983710442068936">"مسترد کریں"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 0dcad99..e947486 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">Yana <xliff:g id="COUNT_1">^1</xliff:g> ta qoʻshimcha element</item>
<item quantity="one">Yana <xliff:g id="COUNT_0">^1</xliff:g> ta qoʻshimcha element</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Ilovaga oid vaqtincha fayllarni tozalash"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Ilovaga oid vaqtincha fayllar tozalansinmi?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ayrim vaqtincha fayllarni tozalamoqchi. Bunda batareya quvvati yoki internet trafigi ortiqcha sarflanishi mumkin."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Ilovaga oid vaqtincha fayllar tozalanmoqda…"</string>
- <string name="clear" msgid="5524638938415865915">"Tozalash"</string>
<string name="allow" msgid="8885707816848569619">"Ruxsat"</string>
<string name="deny" msgid="6040983710442068936">"Rad etish"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index d0cbec7..922ec32 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">Và <xliff:g id="COUNT_1">^1</xliff:g> mục khác</item>
<item quantity="one">Và <xliff:g id="COUNT_0">^1</xliff:g> mục khác</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Xóa tệp ứng dụng tạm thời"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Xóa tệp ứng dụng tạm thời?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> muốn xóa một số tệp tạm thời. Điều này có thể làm tăng mức sử dụng pin hoặc dữ liệu di động."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Đang xóa tệp ứng dụng tạm thời…"</string>
- <string name="clear" msgid="5524638938415865915">"Xóa"</string>
<string name="allow" msgid="8885707816848569619">"Cho phép"</string>
<string name="deny" msgid="6040983710442068936">"Từ chối"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 6cd41f7..76c06ad 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">还有 <xliff:g id="COUNT_1">^1</xliff:g> 项</item>
<item quantity="one">还有 <xliff:g id="COUNT_0">^1</xliff:g> 项</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"清除临时应用文件"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"要清除临时应用文件吗?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>请求清除一些临时文件。这可能消耗更多电量或数据流量。"</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"正在清除临时应用文件…"</string>
- <string name="clear" msgid="5524638938415865915">"清除"</string>
<string name="allow" msgid="8885707816848569619">"允许"</string>
<string name="deny" msgid="6040983710442068936">"拒绝"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 3243f9b..0025294 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">以及另外 <xliff:g id="COUNT_1">^1</xliff:g> 個項目</item>
<item quantity="one">以及另外 <xliff:g id="COUNT_0">^1</xliff:g> 個項目</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"清除應用程式暫存檔案"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"要清除應用程式暫存檔案嗎?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」要求清除部分臨時檔案。這可能會導致耗電量或流動數據用量增加。"</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"正在清除應用程式暫存檔案…"</string>
- <string name="clear" msgid="5524638938415865915">"清除"</string>
<string name="allow" msgid="8885707816848569619">"允許"</string>
<string name="deny" msgid="6040983710442068936">"拒絕"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 1a37210..e5f5b48 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -37,10 +37,8 @@
<item quantity="other">還有另外 <xliff:g id="COUNT_1">^1</xliff:g> 個項目</item>
<item quantity="one">還有另外 <xliff:g id="COUNT_0">^1</xliff:g> 個項目</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"清除應用程式暫存檔案"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"要清除應用程式暫存檔案嗎?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」想要清除某些暫存檔案,這可能會導致耗電量或行動數據用量增加。"</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"正在清除應用程式暫存檔案…"</string>
- <string name="clear" msgid="5524638938415865915">"清除"</string>
<string name="allow" msgid="8885707816848569619">"允許"</string>
<string name="deny" msgid="6040983710442068936">"拒絕"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index e2f528d..e39d445 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -37,10 +37,8 @@
<item quantity="one">Kanye no-<xliff:g id="COUNT_1">^1</xliff:g> izinto ezingeziwe</item>
<item quantity="other">Kanye no-<xliff:g id="COUNT_1">^1</xliff:g> izinto ezingeziwe</item>
</plurals>
- <string name="cache_clearing_dialog_title" msgid="8907893815183913664">"Sula amafayela ohlelo lokusebenza wesikhashana"</string>
+ <string name="cache_clearing_dialog_title" msgid="4672878017407595782">"Sula amafayela ohlelo lokusebenza wesikhashana?"</string>
<string name="cache_clearing_dialog_text" msgid="7057784635111940957">"I-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ingathanda ukusula amanye amafayela esikhashana. Lokhu kungaholela ekusetshenzisweni kwebhethri okuphezulu noma kwedatha yeselula."</string>
- <string name="cache_clearing_in_progress_title" msgid="6902220064511664209">"Isula amafayela wohlelo lokusebenza wesikhashana…"</string>
- <string name="clear" msgid="5524638938415865915">"Sula"</string>
<string name="allow" msgid="8885707816848569619">"Vumela"</string>
<string name="deny" msgid="6040983710442068936">"Nqaba"</string>
<plurals name="permission_write_audio" formatted="false" msgid="8914759422381305478">
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 5130780..b6146be 100644
--- a/src/com/android/providers/media/DatabaseHelper.java
+++ b/src/com/android/providers/media/DatabaseHelper.java
@@ -800,7 +800,8 @@
+ "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)");
db.execSQL("CREATE TABLE log (time DATETIME, message TEXT)");
if (!mInternal) {
@@ -1395,6 +1396,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;");
}
@@ -1536,7 +1546,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 = 1201;
+ static final int VERSION_LATEST = VERSION_S;
/**
* This method takes care of updating all the tables in the database to the
@@ -1681,6 +1694,12 @@
if (fromVersion < 1115) {
updateAudioAlbumId(db, internal);
}
+ if (fromVersion < 1200) {
+ updateAddTranscodeSatus(db, internal);
+ }
+ if (fromVersion < 1201) {
+ updateAddVideoCodecType(db, internal);
+ }
// If this is the legacy database, it's not worth recomputing data
// values locally, since they'll be recomputed after the migration
diff --git a/src/com/android/providers/media/LocalCallingIdentity.java b/src/com/android/providers/media/LocalCallingIdentity.java
index 4d3700b..0d39f64 100644
--- a/src/com/android/providers/media/LocalCallingIdentity.java
+++ b/src/com/android/providers/media/LocalCallingIdentity.java
@@ -51,17 +51,19 @@
import android.os.UserManager;
import android.util.ArrayMap;
+import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import com.android.providers.media.util.LongArray;
public class LocalCallingIdentity {
- public final Context context;
public final int pid;
public final int uid;
- public final String packageNameUnchecked;
+ 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) {
@@ -140,8 +142,8 @@
return ident;
}
- private String packageName;
- private boolean packageNameResolved;
+ private volatile String packageName;
+ private volatile boolean packageNameResolved;
public String getPackageName() {
if (!packageNameResolved) {
@@ -158,8 +160,8 @@
return packageNameUnchecked;
}
- private String[] sharedPackageNames;
- private boolean sharedPackageNamesResolved;
+ private volatile String[] sharedPackageNames;
+ private volatile boolean sharedPackageNamesResolved;
public String[] getSharedPackageNames() {
if (!sharedPackageNamesResolved) {
@@ -174,8 +176,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) {
@@ -216,8 +218,8 @@
public static final int PERMISSION_IS_SYSTEM_GALLERY = 1 <<22;
- private int hasPermission;
- private int hasPermissionResolved;
+ private volatile int hasPermission;
+ private volatile int hasPermissionResolved;
public boolean hasPermission(int permission) {
if ((hasPermissionResolved & permission) == 0) {
@@ -336,42 +338,54 @@
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, 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, UNKNOWN_ROW_ID);
+ }
}
}
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index 22676a8..c4097c9 100644
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -24,6 +24,7 @@
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.PERMISSION_GRANTED;
+import static android.provider.MediaStore.EXTRA_ACCEPT_ORIGINAL_MEDIA_FORMAT;
import static android.provider.MediaStore.MATCH_DEFAULT;
import static android.provider.MediaStore.MATCH_EXCLUDE;
import static android.provider.MediaStore.MATCH_INCLUDE;
@@ -236,10 +237,10 @@
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 supports transforms and uid requires transcoding */
+ private static final int FLAG_TRANSFORM_TRANSCODING = 1;
+ /** File supports transforms */
+ private static final int FLAG_TRANSFORM_SUPPORTED = 1 << 30;
/**
* These directory names aren't declared in Environment as final variables, and so we need to
@@ -438,7 +439,6 @@
private int mExternalStorageAuthorityAppId;
private int mDownloadsAuthorityAppId;
-
private Size mThumbSize;
/**
@@ -692,6 +692,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
@@ -923,6 +926,8 @@
false, false, false, Column.class,
Metrics::logSchemaChange, mFilesListener, MIGRATION_LISTENER, mIdGenerator);
+ mTranscodeHelper = new TranscodeHelper(context, this);
+
final IntentFilter packageFilter = new IntentFilter();
packageFilter.setPriority(10);
packageFilter.addDataScheme("package");
@@ -973,10 +978,10 @@
// 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(
@@ -1087,7 +1092,7 @@
});
Log.d(TAG, "Pruned " + stalePackages + " unknown packages");
- // Delete any expired content; we're paranoid about wildly changing
+ // 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);
@@ -1228,6 +1233,7 @@
return false;
}
boolean result = isAppCloneUserPair(0, userId);
+
Log.w(TAG, "isAppCloneUserPair for user " + userId + ": " + result);
return result;
@@ -1301,6 +1307,77 @@
}
/**
+ * 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 uid app requesting transform
+ *
+ * Called from JNI in jni/MediaProviderWrapper.cpp
+ */
+ @Keep
+ public boolean transformForFuse(String src, String dst, int transforms, int uid) {
+ if ((transforms & FLAG_TRANSFORM_TRANSCODING) != 0) {
+ if (mTranscodeHelper.isTranscodeFileCached(src, dst)) {
+ Log.d(TAG, "Using transcode cache for " + src);
+ return true;
+ }
+ return mTranscodeHelper.transcode(src, dst, uid);
+ }
+ return true;
+ }
+
+ /**
+ * Called from FUSE to get IO path for {@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
+ *
+ * Called from JNI in jni/MediaProviderWrapper.cpp
+ */
+ @Keep
+ public String getIoPathForFuse(String path, int uid) {
+ return mTranscodeHelper.getIoPath(path, uid);
+ }
+
+ /**
+ * Called from FUSE to get transforms for {@code uid}
+ *
+ * If transforms are not supported for {@code path}, {@code 0} will be returned. Otherwise,
+ * a bitwise OR of supported transforms for {@code path} and actual transforms to perform for
+ * {@code uid} will be returned.
+ *
+ * @param path file path to get transforms for
+ * @param uid app requesting IO
+ *
+ * Called from JNI in jni/MediaProviderWrapper.cpp
+ * @see {@link transformForFuse}
+ */
+ @Keep
+ public int getTransformsForFuse(String path, int uid) {
+ int result = 0;
+ if (mTranscodeHelper.supportsTranscode(path)) {
+ result |= FLAG_TRANSFORM_SUPPORTED;
+
+ if (mTranscodeHelper.shouldTranscode(path, uid)) {
+ result |= FLAG_TRANSFORM_TRANSCODING;
+ }
+ }
+ return result;
+ }
+
+ /**
* Returns true if the app denoted by the given {@code uid} and {@code packageName} is allowed
* to clear other apps' cache directories.
*/
@@ -2569,7 +2646,7 @@
/**
* Get the various file-related {@link MediaColumns} in the given
- * {@link ContentValues} into sane condition. Also validates that defined
+ * {@link ContentValues} into a consistent condition. Also validates that defined
* columns are valid for the given {@link Uri}, such as ensuring that only
* {@code image/*} can be inserted into
* {@link android.provider.MediaStore.Images}.
@@ -2736,7 +2813,7 @@
}
}
- // Give ourselves sane defaults when missing
+ // Give ourselves reasonable defaults when missing
if (TextUtils.isEmpty(values.getAsString(MediaColumns.DISPLAY_NAME))) {
values.put(MediaColumns.DISPLAY_NAME,
String.valueOf(System.currentTimeMillis()));
@@ -2748,7 +2825,7 @@
}
mimeType = values.getAsString(MediaColumns.MIME_TYPE);
- // Sanity check MIME type against table
+ // Quick check MIME type against table
if (mimeType != null) {
final int actualMediaType = MimeUtils.resolveMediaType(mimeType);
if (defaultMediaType == FileColumns.MEDIA_TYPE_NONE) {
@@ -2920,7 +2997,7 @@
// files DISPLAY_NAME will not be same as file name.
FileUtils.computeValuesFromData(values, isFuseThread);
} else {
- assertFileColumnsSane(match, uri, values);
+ assertFileColumnsConsistent(match, uri, values);
}
// Drop columns that aren't relevant for special tables
@@ -2943,8 +3020,7 @@
/**
* @return the default dir if {@code file} is a child of default dir and it's missing,
- * {@code null} otherwise.
- */
+ * {@code null} otherwise. */
private File checkDefaultDirMissing(String volumeName, File file) {
String topLevelDir = FileUtils.extractTopLevelDir(file.getPath());
if (topLevelDir != null && FileUtils.isDefaultDirectoryName(topLevelDir)) {
@@ -2977,13 +3053,13 @@
* Check that any requested {@link MediaColumns#DATA} paths actually
* live on the storage volume being targeted.
*/
- private void assertFileColumnsSane(int match, Uri uri, ContentValues values)
+ private void assertFileColumnsConsistent(int match, Uri uri, ContentValues values)
throws VolumeArgumentException, VolumeNotFoundException {
if (!values.containsKey(MediaColumns.DATA)) return;
final String volumeName = resolveVolumeName(uri);
try {
- // Sanity check that the requested path actually lives on volume
+ // Quick check that the requested path actually lives on volume
final Collection<File> allowed = getVolumeScanPaths(volumeName);
final File actual = new File(values.getAsString(MediaColumns.DATA))
.getCanonicalFile();
@@ -3900,6 +3976,14 @@
}
}
+ /**
+ * Gets shared package names for the calling package
+ * TODO(b/170465810) Change the method name after refactoring.
+ */
+ String[] getSharedPackagesForUidForTranscoding(int uid) {
+ return getContext().getPackageManager().getPackagesForUid(uid);
+ }
+
@Deprecated
private String getSharedPackages() {
final String[] sharedPackageNames = mCallingIdentity.get().getSharedPackageNames();
@@ -3925,6 +4009,15 @@
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) {
+ return getQueryBuilder(type, match, uri, extras, honored);
+ }
+
+ /**
* Generate a {@link SQLiteQueryBuilder} that is filtered based on the
* runtime permissions and/or {@link Uri} grants held by the caller.
* <ul>
@@ -4260,7 +4353,7 @@
case AUDIO_ARTISTS_ID_ALBUMS: {
if (type == TYPE_QUERY) {
qb.setTables("audio_albums");
- qb.setProjectionMap(getProjectionMap(Audio.Albums.class));
+ qb.setProjectionMap(getProjectionMap(Audio.Artists.Albums.class));
final String artistId = uri.getPathSegments().get(3);
appendWhereStandalone(qb, "artist_id=?", artistId);
@@ -4924,7 +5017,7 @@
/**
* Generate the {@link PendingIntent} for the given grant request. This
- * method also sanity checks the incoming arguments for security purposes
+ * method also checks the incoming arguments for security purposes
* before creating the privileged {@link PendingIntent}.
*/
private @NonNull PendingIntent createRequest(@NonNull String method, @NonNull Bundle extras) {
@@ -5006,7 +5099,7 @@
* {@code ORDER BY} clauses.
*/
private @NonNull String ensureCustomCollator(@NonNull String locale) {
- // Quick sanity check that requested locale looks sane
+ // Quick check that requested locale looks reasonable
new ULocale(locale);
final String collationName = "custom_" + locale.replaceAll("[^a-zA-Z]", "");
@@ -5621,8 +5714,8 @@
Trace.endSection();
}
- // Make sure any updated paths look sane
- assertFileColumnsSane(match, uri, initialValues);
+ // Make sure any updated paths look consistent
+ assertFileColumnsConsistent(match, uri, initialValues);
if (initialValues.containsKey(FileColumns.DATA)) {
// If we're changing paths, invalidate any thumbnails
@@ -6101,18 +6194,20 @@
@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 {
uri = safeUncanonicalize(uri);
+ opts = opts == null ? new Bundle() : opts;
final boolean allowHidden = isCallingPackageAllowedHidden();
final int match = matchUri(uri, allowHidden);
@@ -6146,7 +6241,7 @@
}
}
- return openFileAndEnforcePathPermissionsHelper(uri, match, mode, signal);
+ return openFileAndEnforcePathPermissionsHelper(uri, match, mode, signal, opts);
}
@Override
@@ -6176,7 +6271,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);
}
@@ -6380,6 +6475,22 @@
return new File(filePath);
}
+ private ParcelFileDescriptor openWithTranscode(String filePath, int uid, int modeBits)
+ throws FileNotFoundException {
+ String transcodePath = mTranscodeHelper.getIoPath(filePath, uid);
+ File transcodeFile = new File(transcodePath);
+
+ if (mTranscodeHelper.isTranscodeFileCached(filePath, transcodePath)) {
+ Log.d(TAG, "Using FUSE with transcode cache for " + filePath + " Uid: " + uid);
+ return FileUtils.openSafely(getFuseFile(transcodeFile), modeBits);
+ } else if (mTranscodeHelper.transcode(filePath, transcodePath, uid)) {
+ Log.d(TAG, "Using FUSE with transcode for " + filePath + " Uid: " + uid);
+ return FileUtils.openSafely(getFuseFile(transcodeFile), modeBits);
+ } else {
+ throw new FileNotFoundException("Failed to transcode " + filePath);
+ }
+ }
+
private @NonNull FuseDaemon getFuseDaemonForFile(@NonNull File file)
throws FileNotFoundException {
final FuseDaemon daemon = ExternalStorageServiceImpl.getFuseDaemon(getVolumeId(file));
@@ -6418,7 +6529,8 @@
* 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;
if (forWrite) {
@@ -6507,33 +6619,31 @@
// First, handle any redaction that is needed for caller
final ParcelFileDescriptor pfd;
final String filePath = file.getPath();
+ final int uid = Binder.getCallingUid();
+ final boolean shouldTranscode = !opts.containsKey(EXTRA_ACCEPT_ORIGINAL_MEDIA_FORMAT)
+ && mTranscodeHelper.shouldTranscode(filePath, uid);
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.
+ Log.i(TAG, "Redacting with new FUSE for " + filePath);
+ long tid = android.os.Process.myTid();
+ synchronized (mShouldRedactThreadIds) {
+ mShouldRedactThreadIds.add(tid);
}
+
+ try {
+ if (shouldTranscode) {
+ pfd = openWithTranscode(filePath, uid, modeBits);
+ } else {
+ pfd = FileUtils.openSafely(getFuseFile(file), modeBits);
+ }
+ } finally {
+ synchronized (mShouldRedactThreadIds) {
+ mShouldRedactThreadIds.remove(mShouldRedactThreadIds.indexOf(tid));
+ }
+ }
+ } else if (shouldTranscode) {
+ pfd = openWithTranscode(filePath, uid, modeBits);
} else {
FuseDaemon daemon = null;
try {
@@ -6546,12 +6656,12 @@
boolean shouldOpenWithFuse = daemon != null
&& 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);
+ Log.w(TAG, "Using FUSE for " + filePath + ". Uid: " + uid);
pfd = FileUtils.openSafely(getFuseFile(file), modeBits);
try {
lowerFsFd.close();
@@ -6559,7 +6669,7 @@
Log.w(TAG, "Failed to close lower filesystem fd " + file.getPath(), e);
}
} else {
- Log.i(TAG, "Using lower FS for " + filePath);
+ Log.i(TAG, "Using 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.
@@ -7033,7 +7143,7 @@
// 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);
+ Log.e(TAG, "Couldn't find file: " + path, e);
return OsConstants.EACCES;
} catch (IllegalStateException | SecurityException e) {
Log.e(TAG, "Permission to access file: " + path + " is denied");
@@ -7690,7 +7800,7 @@
// First, does caller have the needed row-level access?
enforceCallingPermission(uri, extras, isWrite);
- // Second, does the path look sane?
+ // Second, does the path look consistent?
if (!FileUtils.contains(Environment.getStorageDirectory(), file)) {
checkWorldReadAccess(file.getAbsolutePath());
}
@@ -7827,6 +7937,15 @@
}
}
+ /**
+ * 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) {
@@ -7871,10 +7990,10 @@
"Opening and closing databases not allowed.");
}
- // Quick sanity check for shady volume names
+ // Quick check for shady volume names
MediaStore.checkArgumentVolumeName(volume);
- // Quick sanity check that volume actually exists
+ // Quick check that volume actually exists
if (!MediaStore.VOLUME_INTERNAL.equals(volume) && validate) {
try {
getVolumePath(volume);
@@ -7925,7 +8044,7 @@
"Opening and closing databases not allowed.");
}
- // Quick sanity check for shady volume names
+ // Quick check for shady volume names
MediaStore.checkArgumentVolumeName(volume);
if (MediaStore.VOLUME_INTERNAL.equals(volume)) {
@@ -7961,6 +8080,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;
diff --git a/src/com/android/providers/media/TranscodeHelper.java b/src/com/android/providers/media/TranscodeHelper.java
new file mode 100644
index 0000000..b3ae5ac
--- /dev/null
+++ b/src/com/android/providers/media/TranscodeHelper.java
@@ -0,0 +1,464 @@
+/*
+ * 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.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 android.widget.Toast;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.media.MediaFormat;
+import android.media.MediaTranscodeManager;
+import android.media.MediaTranscodeManager.TranscodingSession;
+import android.media.MediaTranscodeManager.TranscodingRequest;
+import android.media.MediaTranscodingException;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Environment;
+import android.os.SystemProperties;
+import android.provider.MediaStore.Files.FileColumns;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Pair;
+import android.util.SparseArray;
+
+import androidx.annotation.GuardedBy;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.providers.media.util.FileUtils;
+import com.android.providers.media.util.ForegroundThread;
+import com.android.providers.media.util.SQLiteQueryBuilder;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class TranscodeHelper {
+ private static final String TAG = "TranscodeHelper";
+ private static final String TRANSCODE_FILE_PREFIX = ".transcode_";
+
+ /** 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;
+ static final String DIRECTORY_CAMERA = "Camera";
+
+ private final Context mContext;
+ private final MediaProvider mMediaProvider;
+ private final MediaTranscodeManager mMediaTranscodeManager;
+ private final Handler mUiHandler;
+ private final File mTranscodeDirectory;
+ @GuardedBy("mTranscodingSessions")
+ private final Map<String, TranscodingSession> mTranscodingSessions = new ArrayMap<>();
+ @GuardedBy("mTranscodingSessions")
+ private final SparseArray<CountDownLatch> mTranscodingLatches = new SparseArray<>();
+
+ 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'";
+
+ /**
+ * Never transcode for these packages.
+ * TODO(b/169327180): Replace this with allow list from server.
+ */
+ private static final String[] ALLOW_LIST = new String[0];
+ /**
+ * Force transcode for these package names.
+ * TODO(b/169849854): Remove this when app capabilities can be used to make this decision.
+ */
+ private static String[] TRANSCODE_LIST = new String[] {
+ "com.facebook.katana",
+ "com.google.android.talk",
+ "com.snapchat.android",
+ "com.instagram.android",
+ // TODO: Add "com.google.android.apps.photos", to teamfood after investigating issue
+ "com.linecorp.b612.android",
+ "com.zhiliaoapp.musically",
+ "com.tencent.mm"
+ };
+
+ public TranscodeHelper(Context context, MediaProvider mediaProvider) {
+ mContext = context;
+ mMediaTranscodeManager = context.getSystemService(MediaTranscodeManager.class);
+ mMediaProvider = mediaProvider;
+ mUiHandler = new Handler(Looper.getMainLooper());
+ mTranscodeDirectory =
+ FileUtils.buildPath(Environment.getExternalStorageDirectory(), DIRECTORY_TRANSCODE);
+ mTranscodeDirectory.mkdirs();
+ }
+
+ /**
+ * 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]+)/\\.transcode/(?:\\d+)$");
+ private static final String DIRECTORY_TRANSCODE = ".transcode";
+
+ /**
+ * @return true if the file path matches transcode file path.
+ */
+ public static boolean isTranscodeFile(@NonNull String path) {
+ final Matcher matcher = PATTERN_TRANSCODE_PATH.matcher(path);
+ return matcher.matches();
+ }
+
+ @NonNull
+ public File getTranscodeDirectory() {
+ return mTranscodeDirectory;
+ }
+
+ /**
+ * @return transcode file's path for given {@code rowId}
+ */
+ @NonNull
+ public String getTranscodePath(long rowId) {
+ return new File(getTranscodeDirectory(), String.valueOf(rowId)).getAbsolutePath();
+ }
+
+ public boolean transcode(String src, String dst, int uid) {
+ TranscodingSession session = null;
+ CountDownLatch latch = null;
+
+ synchronized (mTranscodingSessions) {
+ session = mTranscodingSessions.get(src);
+ if (session == null) {
+ latch = new CountDownLatch(1);
+ try {
+ session = enqueueTranscodingSession(src, dst, uid, latch);
+ } catch (MediaTranscodingException | FileNotFoundException e) {
+ throw new IllegalStateException(e);
+ }
+
+ mTranscodingLatches.put(session.getSessionId(), latch);
+ mTranscodingSessions.put(src, session);
+ } else {
+ latch = mTranscodingLatches.get(session.getSessionId());
+ if (latch == null) {
+ throw new IllegalStateException("Expected latch for" + session);
+ }
+ }
+ }
+
+ boolean result = waitTranscodingResult(uid, src, session, latch);
+ if (result) {
+ updateTranscodeStatus(src, TRANSCODE_COMPLETE);
+ } else {
+ logEvent("Transcoding failed for " + src + ". session: ", true /* toast */, session);
+ // Attempt to workaround media transcoding deadlock, b/165374867
+ // Cancelling a deadlocked session seems to unblock the transcoder
+ finishTranscodingResult(uid, src, session, latch);
+ }
+ return result;
+ }
+
+ public String getIoPath(String path, int uid) {
+ if (!shouldTranscode(path, uid)) {
+ return path;
+ }
+
+ 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);
+ getTranscodeDirectory().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);
+ return path;
+ }
+
+ return transcodePath;
+ }
+
+ public boolean shouldTranscode(String path, int uid) {
+ final boolean transcodeEnabled
+ = SystemProperties.getBoolean("persist.fuse.sys.transcode", false);
+ if (!transcodeEnabled) {
+ return false;
+ }
+
+ if (!supportsTranscode(path) || uid < android.os.Process.FIRST_APPLICATION_UID) {
+ return false;
+ }
+
+ // Transcode only if file needs transcoding
+ try (Cursor cursor = queryFileForTranscode(path,
+ new String[] {FileColumns._VIDEO_CODEC_TYPE})) {
+ if (cursor == null || !cursor.moveToNext()) {
+ Log.d(TAG, "Couldn't find database row for path " + path +
+ ", Assuming no seamless transcoding needed.");
+ return false;
+ }
+ if (!MediaFormat.MIMETYPE_VIDEO_HEVC.equalsIgnoreCase(cursor.getString(0))) {
+ return false;
+ }
+ }
+
+ // TODO(b/169327180): We should also check app's targetSDK version to verify if app still
+ // qualifies to be on the allow list.
+ List<String> allowList = Arrays.asList(ALLOW_LIST);
+ List<String> transcodeList = Arrays.asList(TRANSCODE_LIST);
+ final String[] callingPackages = mMediaProvider.getSharedPackagesForUidForTranscoding(uid);
+ for (String callingPackage: callingPackages) {
+ if (allowList.contains(callingPackage)) {
+ return false;
+ }
+ if (transcodeList.contains(callingPackage)) {
+ return true;
+ }
+ }
+
+ int supportedUid = SystemProperties.getInt("fuse.sys.transcode_uid", -2);
+ if ((supportedUid == uid) || (supportedUid == -1)) {
+ return true;
+ }
+
+ List<String> supportedPackages =
+ Arrays.asList(SystemProperties.get("fuse.sys.transcode_package").split(","));
+ for (String callingPackage: callingPackages) {
+ if (supportedPackages.contains(callingPackage)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ 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.endsWith(".mp4") &&
+ cameraRelativePath.equalsIgnoreCase(FileUtils.extractRelativePath(path));
+ }
+
+ 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);
+ }
+
+ public boolean isTranscodeFileCached(String path, String transcodePath) {
+ if (SystemProperties.getBoolean("fuse.sys.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, true /* toast */, null /* session */);
+ }
+ return result;
+ }
+ return false;
+ }
+
+ private TranscodingSession enqueueTranscodingSession(String src, String dst, int uid,
+ final CountDownLatch latch) throws FileNotFoundException, MediaTranscodingException {
+ int bitRate = 20000000; // 20Mbps
+ int width = 1920;
+ int height = 1080;
+
+ File file = new File(src);
+ File transcodeFile = new File(dst);
+
+ Uri uri = Uri.fromFile(file);
+ Uri transcodeUri = Uri.fromFile(transcodeFile);
+
+ // TODO: Get MediaFormat from database
+ MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC,
+ width, height);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
+
+ TranscodingRequest request =
+ new TranscodingRequest.Builder()
+ .setClientUid(uid)
+ .setSourceUri(uri)
+ .setDestinationUri(transcodeUri)
+ .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+ .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
+ .setVideoTrackFormat(format)
+ .build();
+ TranscodingSession session = mMediaTranscodeManager.enqueueRequest(request,
+ ForegroundThread.getExecutor(),
+ s -> finishTranscodingResult(uid, src, s, latch));
+
+ logEvent("Transcoding start: " + src + ". Uid: " + uid, true /* toast */, session);
+ return session;
+ }
+
+ private boolean waitTranscodingResult(int uid, String src, TranscodingSession session,
+ CountDownLatch latch) {
+ try {
+ int timeout = getTranscodeTimeoutSeconds(src);
+
+ String waitStartLog = "Transcoding wait start: " + src + ". Uid: " + uid + ". Timeout: "
+ + timeout + "s";
+ logEvent(waitStartLog, false /* toast */, session);
+
+ boolean latchResult = latch.await(timeout, TimeUnit.SECONDS);
+ boolean transcodeResult = session.getResult() == TranscodingSession.RESULT_SUCCESS;
+
+ String waitEndLog = "Transcoding wait end: " + src + ". Uid: " + uid + ". Timeout: "
+ + !latchResult + ". Success: " + transcodeResult;
+ logEvent(waitEndLog, false /* toast */, session);
+
+ return transcodeResult;
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ Log.w(TAG, "Transcoding latch interrupted." + session);
+ return false;
+ }
+ }
+
+ private int getTranscodeTimeoutSeconds(String file) {
+ double sizeMb = new File(file).length() / (1024 * 1024);
+ return (int) (sizeMb * TRANSCODING_TIMEOUT_COEFFICIENT);
+ }
+
+ private void finishTranscodingResult(int uid, String src, TranscodingSession session,
+ CountDownLatch latch) {
+ synchronized (mTranscodingSessions) {
+ latch.countDown();
+ session.cancel();
+ mTranscodingSessions.remove(src);
+ mTranscodingLatches.remove(session.getSessionId());
+ }
+
+ logEvent("Transcoding end: " + src + ". Uid: " + uid, true /* toast */, 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);
+ return qb.update(getDatabaseHelperForUri(uri), values, TRANSCODE_WHERE_CLAUSE,
+ selectionArgs) == 1;
+ }
+
+ public boolean deleteCachedTranscodeFile(long rowId) {
+ return new File(getTranscodeDirectory(), 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 void logEvent(String event, boolean toast, @Nullable TranscodingSession session) {
+ Log.d(TAG, event + (session == null ? "" : session));
+
+ if (toast && SystemProperties.getBoolean("fuse.sys.transcode_show_toast", false)) {
+ mUiHandler.post(() ->
+ Toast.makeText(mContext, event, Toast.LENGTH_SHORT).show());
+ }
+ }
+}
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/scan/ModernMediaScanner.java b/src/com/android/providers/media/scan/ModernMediaScanner.java
index adb9941..9e678f1 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;
@@ -93,7 +94,6 @@
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;
@@ -166,10 +166,12 @@
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)$)|"
+ + "(?:\\.transcode$)|"
+ + "(?:(?:Movies|Music|Pictures)/.thumbnails$))");
private static final Pattern PATTERN_YEAR = Pattern.compile("([1-9][0-9][0-9][0-9])");
@@ -1201,6 +1203,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()) {
@@ -1223,6 +1226,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
diff --git a/src/com/android/providers/media/util/FileUtils.java b/src/com/android/providers/media/util/FileUtils.java
index 3ddec4a..cff4fa7 100644
--- a/src/com/android/providers/media/util/FileUtils.java
+++ b/src/com/android/providers/media/util/FileUtils.java
@@ -856,9 +856,9 @@
}
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(
@@ -899,7 +899,7 @@
* 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]+/)?Android/(?:data|media|obb)/([^/]+)(/?.*)?");
/**
* Regex that matches Android/obb or Android/data path.
@@ -923,11 +923,10 @@
};
/**
- * Regex that matches paths for {@link MediaColumns#RELATIVE_PATH}; it
- * captures both top-level paths and sandboxed paths.
+ * 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.
diff --git a/src/com/android/providers/media/util/PermissionUtils.java b/src/com/android/providers/media/util/PermissionUtils.java
index adbe0e2..5021224 100644
--- a/src/com/android/providers/media/util/PermissionUtils.java
+++ b/src/com/android/providers/media/util/PermissionUtils.java
@@ -23,6 +23,7 @@
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.MODE_ALLOWED;
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 +43,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.
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/src/com/android/providers/media/DatabaseHelperTest.java b/tests/src/com/android/providers/media/DatabaseHelperTest.java
index b2b7825..a9c77b6 100644
--- a/tests/src/com/android/providers/media/DatabaseHelperTest.java
+++ b/tests/src/com/android/providers/media/DatabaseHelperTest.java
@@ -75,7 +75,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 +234,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 +284,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 +346,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 +395,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)) {
@@ -525,6 +512,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 +935,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/MediaProviderTest.java b/tests/src/com/android/providers/media/MediaProviderTest.java
index 76faa16..30df5dc 100644
--- a/tests/src/com/android/providers/media/MediaProviderTest.java
+++ b/tests/src/com/android/providers/media/MediaProviderTest.java
@@ -434,8 +434,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
@@ -832,36 +830,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 +903,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);
diff --git a/tests/src/com/android/providers/media/scan/ModernMediaScannerTest.java b/tests/src/com/android/providers/media/scan/ModernMediaScannerTest.java
index 1c86ed8..9b7f0d2 100644
--- a/tests/src/com/android/providers/media/scan/ModernMediaScannerTest.java
+++ b/tests/src/com/android/providers/media/scan/ModernMediaScannerTest.java
@@ -383,15 +383,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 +403,9 @@
new File(prefix + "/Movies/.thumbnails/meow"));
assertShouldScanPathAndIsPathHidden(false, false,
new File(prefix + "/Music/.thumbnails/meow"));
+
+ assertShouldScanPathAndIsPathHidden(false, false,
+ new File(prefix + "/.transcode/meow"));
}
}
@@ -457,26 +456,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 + "/.transcode"));
}
}
@@ -492,9 +489,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"));
@@ -1035,4 +1030,21 @@
assertEquals(0, cursor.getCount());
}
}
+
+ @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);
+ }
+ }
}
diff --git a/tests/transcode/Android.bp b/tests/transcode/Android.bp
new file mode 100644
index 0000000..7326386
--- /dev/null
+++ b/tests/transcode/Android.bp
@@ -0,0 +1,30 @@
+android_test {
+ name: "MediaProviderTranscodeTests",
+ test_suites: [
+ "device-tests",
+ "mts",
+ ],
+ compile_multilib: "both",
+
+ manifest: "AndroidManifest.xml",
+
+ srcs: [
+ "src/**/*.java",
+ ],
+
+ libs: [
+ "android.test.base",
+ "android.test.mock",
+ "android.test.runner",
+ ],
+
+ static_libs: [
+ "androidx.test.rules",
+ "cts-install-lib",
+ "collector-device-lib-platform",
+ "mockito-target",
+ "truth-prebuilt",
+ ],
+
+ certificate: "media",
+}
diff --git a/tests/transcode/AndroidManifest.xml b/tests/transcode/AndroidManifest.xml
new file mode 100644
index 0000000..99254f4
--- /dev/null
+++ b/tests/transcode/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.providers.media.transcode"
+ android:sharedUserId="android.media">
+
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <application android:label="MediaProvider Transcode Tests">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.providers.media.transcode"
+ android:label="MediaProvider Transcode Tests" />
+
+</manifest>
diff --git a/tests/transcode/AndroidTest.xml b/tests/transcode/AndroidTest.xml
new file mode 100644
index 0000000..97fce01
--- /dev/null
+++ b/tests/transcode/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs Tests for MediaProvder.">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="MediaProviderTranscodeTests.apk" />
+ <option name="install-arg" value="-g" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="framework-base-presubmit" />
+ <option name="test-tag" value="MediaProviderTranscodeTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.providers.media.transcode" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/tests/transcode/res/raw/testvideo_HEVC.mp4 b/tests/transcode/res/raw/testvideo_HEVC.mp4
new file mode 100644
index 0000000..8a3dba2
--- /dev/null
+++ b/tests/transcode/res/raw/testvideo_HEVC.mp4
Binary files differ
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..8c363be
--- /dev/null
+++ b/tests/transcode/src/com/android/providers/media/transcode/TranscodeTest.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.transcode;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Environment;
+import android.system.Os;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Arrays;
+
+@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");
+
+ static final String NONCE = String.valueOf(System.nanoTime());
+ private static final String HEVC_FILE_NAME = "TranscodeTestHEVC_" + NONCE + ".mp4";
+
+ @Before
+ public void setUp() throws Exception {
+ TranscodeTestUtils.pollForExternalStorageState();
+ TranscodeTestUtils.enableSeamlessTranscoding();
+ }
+
+ @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() throws Exception {
+ File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+ try {
+ TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+ long size = modernFile.length();
+
+ FileInputStream fisOriginal = new FileInputStream(modernFile);
+
+ TranscodeTestUtils.setLegacy(Os.getuid());
+
+ FileInputStream fisTranscoded = new FileInputStream(modernFile);
+
+ assertFileContent(fisOriginal, fisTranscoded, size, false);
+
+ fisOriginal.close();
+ fisTranscoded.close();
+ } finally {
+ TranscodeTestUtils.unsetLegacy();
+ modernFile.delete();
+ }
+ }
+
+ /**
+ * Tests that same transcoded file is used for multiple open() from same app
+ * @throws Exception
+ */
+ @Test
+ public void testSameTranscodedFile() throws Exception {
+ File modernFile = new File(DIR_CAMERA, HEVC_FILE_NAME);
+ try {
+ TranscodeTestUtils.stageHEVCVideoFile(modernFile);
+ long size = modernFile.length();
+
+ TranscodeTestUtils.setLegacy(Os.getuid());
+
+ FileInputStream fisTranscoded1 = new FileInputStream(modernFile);
+ FileInputStream fisTranscoded2 = new FileInputStream(modernFile);
+
+ assertFileContent(fisTranscoded1, fisTranscoded2, size, true);
+
+ fisTranscoded1.close();
+ fisTranscoded2.close();
+ } finally {
+ TranscodeTestUtils.unsetLegacy();
+ modernFile.delete();
+ }
+ }
+
+ private void assertFileContent(FileInputStream fisOriginal, FileInputStream fisTranscoded,
+ long fileSize, boolean assertSame) throws IOException {
+ final int readBytesLen = 10;
+ byte[] original = new byte[readBytesLen];
+ byte[] transcoded = new byte[readBytesLen];
+ long ind = 0;
+ final int seekLen = 1024;
+ boolean isSame = true;
+
+ while (isSame && ind < fileSize) {
+ assertEquals(readBytesLen, fisOriginal.read(original));
+ assertEquals(readBytesLen, fisTranscoded.read(transcoded));
+
+ isSame = Arrays.equals(original, transcoded);
+
+ ind += seekLen;
+ fisOriginal.skip(seekLen);
+ fisTranscoded.skip(seekLen);
+ }
+ assertEquals(assertSame, isSame);
+ }
+}
diff --git a/tests/transcode/src/com/android/providers/media/transcode/TranscodeTestUtils.java b/tests/transcode/src/com/android/providers/media/transcode/TranscodeTestUtils.java
new file mode 100644
index 0000000..9a8b881
--- /dev/null
+++ b/tests/transcode/src/com/android/providers/media/transcode/TranscodeTestUtils.java
@@ -0,0 +1,137 @@
+/*
+ * 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 org.junit.Assert.assertTrue;
+
+import android.app.UiAutomation;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.provider.MediaStore;
+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.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Supplier;
+
+public class TranscodeTestUtils {
+ private static final String TAG = "TranscodeTestUtils";
+
+ private static final long POLLING_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(20);
+ private static final long POLLING_SLEEP_MILLIS = 100;
+
+ public static void stageHEVCVideoFile(File videoFile) throws IOException {
+ if (!videoFile.getParentFile().exists()) {
+ assertTrue(videoFile.getParentFile().mkdirs());
+ }
+ try (InputStream in =
+ getContext().getResources().openRawResource(R.raw.testvideo_HEVC);
+ FileOutputStream out = new FileOutputStream(videoFile)) {
+ FileUtils.copy(in, out);
+ // Sync file to disk to ensure file is fully written to the lower fs before scanning
+ // Otherwise, media provider might try to read the file on the lower fs and not see
+ // the fully written bytes
+ out.getFD().sync();
+ }
+ // MediaProvider treats this app as app with MANAGE_EXTERNAL_STORAGE,
+ // so we have to explicitly insert this file to database.
+ MediaStore.scanFile(getContext().getContentResolver(), videoFile);
+ }
+
+ public static void enableSeamlessTranscoding() throws Exception {
+ executeShellCommand("setprop persist.fuse.sys.transcode true");
+ }
+
+ public static void disableSeamlessTranscoding() throws Exception {
+ executeShellCommand("setprop persist.fuse.sys.transcode false");
+ }
+
+ public static void setLegacy(int uid) throws IOException {
+ final String command = "setprop fuse.sys.transcode_uid " + uid;
+ executeShellCommand(command);
+ }
+
+ public static void setLegacyAll() throws IOException {
+ final String command = "setprop fuse.sys.transcode_uid " + -1;
+ executeShellCommand(command);
+ }
+
+ public static void unsetLegacy() throws IOException {
+ final String command = "setprop fuse.sys.transcode_uid " + -2;
+ executeShellCommand(command);
+ }
+
+ /**
+ * Executes a shell command.
+ */
+ public static String executeShellCommand(String command) throws IOException {
+ 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));
+ }
+ }
+
+ /**
+ * Polls for external storage to be mounted.
+ */
+ public static void pollForExternalStorageState() throws Exception {
+ pollForCondition(
+ () -> Environment.getExternalStorageState(Environment.getExternalStorageDirectory())
+ .equals(Environment.MEDIA_MOUNTED),
+ "Timed out while waiting for ExternalStorageState to be MEDIA_MOUNTED");
+ }
+
+ private static void pollForCondition(Supplier<Boolean> condition, String errorMessage)
+ throws Exception {
+ for (int i = 0; i < POLLING_TIMEOUT_MILLIS / POLLING_SLEEP_MILLIS; i++) {
+ if (condition.get()) {
+ return;
+ }
+ Thread.sleep(POLLING_SLEEP_MILLIS);
+ }
+ throw new TimeoutException(errorMessage);
+ }
+
+}
diff --git a/tools/dialogs/AndroidManifest.xml b/tools/dialogs/AndroidManifest.xml
index 947f1bd..8ee5f2c 100644
--- a/tools/dialogs/AndroidManifest.xml
+++ b/tools/dialogs/AndroidManifest.xml
@@ -1,14 +1,17 @@
-<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"/>
<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>