Add androidx.annotation_annotation dependency. am: 5a8f6e7e8f
Original change: https://android-review.googlesource.com/c/platform/packages/apps/DocumentsUI/+/2962916
Change-Id: Ie202cfa529f0eca0fd771ddf8fd59c6e16a54a15
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/app-perf-tests/Android.bp b/app-perf-tests/Android.bp
index f753ac5..5a4bf72 100644
--- a/app-perf-tests/Android.bp
+++ b/app-perf-tests/Android.bp
@@ -20,7 +20,7 @@
"androidx.legacy_legacy-support-v4",
"collector-device-lib",
"mockito-target",
- "ub-uiautomator",
+ "androidx.test.uiautomator_uiautomator",
],
platform_apis: true,
diff --git a/app-perf-tests/src/com/android/documentsui/FilesAppPerfTest.java b/app-perf-tests/src/com/android/documentsui/FilesAppPerfTest.java
index 8366c89..821198d 100644
--- a/app-perf-tests/src/com/android/documentsui/FilesAppPerfTest.java
+++ b/app-perf-tests/src/com/android/documentsui/FilesAppPerfTest.java
@@ -32,9 +32,10 @@
import android.os.Bundle;
import android.os.SystemClock;
import android.provider.DocumentsContract;
-import android.support.test.uiautomator.UiDevice;
import android.util.Log;
+import androidx.test.uiautomator.UiDevice;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
diff --git a/perf-tests/Android.bp b/perf-tests/Android.bp
index 955b1c0..d98c36c 100644
--- a/perf-tests/Android.bp
+++ b/perf-tests/Android.bp
@@ -28,7 +28,7 @@
"androidx.test.espresso.core",
"mockito-target",
"ub-janktesthelper",
- "ub-uiautomator",
+ "androidx.test.uiautomator_uiautomator",
],
platform_apis: true,
diff --git a/perf-tests/src/com/android/documentsui/FilesJankPerfTest.java b/perf-tests/src/com/android/documentsui/FilesJankPerfTest.java
index 04dfc78..6dd8044 100644
--- a/perf-tests/src/com/android/documentsui/FilesJankPerfTest.java
+++ b/perf-tests/src/com/android/documentsui/FilesJankPerfTest.java
@@ -16,27 +16,24 @@
package com.android.documentsui;
-import static com.android.documentsui.StressProvider.DEFAULT_AUTHORITY;
-import static com.android.documentsui.StressProvider.STRESS_ROOT_0_ID;
import static com.android.documentsui.StressProvider.STRESS_ROOT_2_ID;
import android.app.Activity;
-import android.os.RemoteException;
-import android.test.suitebuilder.annotation.LargeTest;
-
import android.app.UiAutomation;
-import android.content.Intent;
import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.support.test.jank.GfxMonitor;
import android.support.test.jank.JankTest;
import android.support.test.jank.JankTestBase;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.jank.GfxMonitor;
-import android.support.test.uiautomator.UiScrollable;
-import android.util.Log;
+import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiScrollable;
+
+import com.android.documentsui.bots.DirectoryListBot;
import com.android.documentsui.bots.SidebarBot;
import com.android.documentsui.files.FilesActivity;
-import com.android.documentsui.bots.DirectoryListBot;
@LargeTest
public class FilesJankPerfTest extends JankTestBase {
diff --git a/perfetto_config.pbtx b/perfetto_config.pbtx
new file mode 100644
index 0000000..7c5bfa1
--- /dev/null
+++ b/perfetto_config.pbtx
@@ -0,0 +1,222 @@
+buffers: {
+ size_kb: 63488
+ fill_policy: DISCARD
+}
+buffers: {
+ size_kb: 2048
+ fill_policy: DISCARD
+}
+# Max duration: 1 min
+duration_ms: 60000
+
+data_sources: {
+ config {
+ name: "linux.ftrace"
+
+ # See: https://perfetto.dev/docs/data-sources/atrace#traceconfig
+ ftrace_config {
+ ftrace_events: "ftrace/print"
+
+ # Trace all ContentProvider commands and SQLite queries.
+ # See: https://source.corp.google.com/android-internal/frameworks/base/core/java/android/os/Trace.java?q=TRACE_TAG_DATABASE
+ # See: https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/database/sqlite/SQLiteConnection.java
+ # Uncomment to enable.
+ # Note: on a userdebug build it will add a trace for every SQLite command for every
+ # application, which makes traces very-very "noisy".
+ # atrace_categories: "database"
+
+ # Trace Binder IPC transactions.
+ # Uncomment to enable.
+ # Note: on a userdebug build it will add a trace for every Binder transaction for every
+ # application, which makes traces very-very "noisy".
+ # atrace_categories: "binder_driver"
+
+ # ActivityManager, WindowManager, Graphics, View System.
+ # Uncomment to enable.
+ # Note: on a userdebug build it will traces from corresponding category for every
+ # application, which makes traces very-very "noisy".
+ # atrace_categories: "wm"
+ # atrace_categories: "am"
+ atrace_categories: "gfx"
+ atrace_categories: "view"
+
+ # Trace DocumentUI "custom" events.
+ atrace_apps: "com.android.documentsui"
+ atrace_apps: "com.google.android.documentsui"
+
+ # Trace other Providers.
+
+ # authorities="com.android.documentsui.archives" - DocsUI
+ atrace_apps: "com.android.documentsui"
+ atrace_apps: "com.google.android.documentsui"
+
+ # authorities="com.android.externalstorage.documents" - ExternalStorageProvider
+ atrace_apps: "com.android.externalstorage"
+
+ # authorities="com.android.mtp.documents" - Mtp
+ atrace_apps: "com.android.mtp"
+
+ # authorities="com.android.pixellogger.documents" - PixelLogger
+ atrace_apps: "com.android.pixellogger"
+
+ # authorities="com.android.providers.downloads" - DownloadSProvider
+ atrace_apps: "com.android.providers.downloads.documents"
+
+ # authorities="com.android.providers.media.documents" - MediaProvider
+ atrace_apps: "com.android.providers.media.module"
+ atrace_apps: "com.google.android.providers.media.module"
+
+ # authorities="com.android.shell.documents" - Shell (BugreportStorageProvider)
+ # atrace_apps: "android.uid.shell"
+
+ # authorities="com.google.android.apps.docs.storage" - Google Docs
+ atrace_apps: "com.google.android.apps.docs"
+
+ # Google Photos.
+ # atrace_apps: "com.google.android.apps.photos"
+
+ # Trace all apps' events.
+ # Uncomment to enable.
+ # atrace_apps: "*"
+ }
+ }
+}
+
+# This is for getting Thread-to-Process associations and full process names.
+data_sources: {
+ config {
+ name: "linux.process_stats"
+ }
+}
+
+# "Expected Timeline" and "Actual Timeline" jor each process
+# See https://perfetto.dev/docs/data-sources/frametimeline#traceconfig
+data_sources {
+ config {
+ name: "android.surfaceflinger.frametimeline"
+ }
+}
+
+# Memory Profiling
+# See https://perfetto.dev/docs/data-sources/memory-counters
+
+# Android Logcat
+data_sources: {
+ config {
+ name: "android.log"
+ android_log_config {
+ min_prio: PRIO_VERBOSE # Default: PRIO_DEBUG
+
+ log_ids: LID_EVENTS
+ log_ids: LID_CRASH
+ log_ids: LID_KERNEL
+ log_ids: LID_DEFAULT
+ log_ids: LID_RADIO
+ log_ids: LID_SECURITY
+ log_ids: LID_STATS
+ log_ids: LID_SYSTEM
+
+ # If filter_tags non-empty ignores all log messages whose tag doesn't match one of the
+ # specified values.
+ # filter_tags: "AbstractActionHandler"
+ # filter_tags: "ActionModeController"
+ # filter_tags: "Archive"
+ # filter_tags: "ArchiveFileTestRule"
+ # filter_tags: "ArchiveHandle"
+ # filter_tags: "ArchivesProvider"
+ # filter_tags: "CancelFromNotificationUiTest"
+ # filter_tags: "ClipStorage"
+ # filter_tags: "CommandInterceptor"
+ # filter_tags: "CompressJob"
+ # filter_tags: "ConfirmFragment"
+ # filter_tags: "ContentLock"
+ # filter_tags: "CopyJob"
+ # filter_tags: "DebugHelper"
+ # filter_tags: "DeleteJob"
+ # filter_tags: "DirectoryFragment"
+ # filter_tags: "DirectoryLoader"
+ # filter_tags: "DirectoryResult"
+ # filter_tags: "DocumentAccess"
+ # filter_tags: "DocumentClipper"
+ # filter_tags: "DocumentInfo"
+ # filter_tags: "DocumentStack"
+ # filter_tags: "Documents"
+ # filter_tags: "DocumentsApplication"
+ # filter_tags: "DocumentsSwipeRefreshLayout"
+ # filter_tags: "DragStartListener"
+ # filter_tags: "DrawerController"
+ # filter_tags: "EjectRootTask"
+ # filter_tags: "FileCopyUiTest"
+ # filter_tags: "FileDeleteUiTest"
+ # filter_tags: "FileOperationService"
+ # filter_tags: "FileOperations"
+ # filter_tags: "FilesActivity"
+ # filter_tags: "FilesAppPerfTest"
+ # filter_tags: "FocusManager"
+ # filter_tags: "GetRootDocumentTask"
+ # filter_tags: "HeaderItem"
+ # filter_tags: "HeaderMessage"
+ # filter_tags: "IconHelper"
+ # filter_tags: "ItemDragListener"
+ # filter_tags: "Job"
+ # filter_tags: "JumboUrisSupplier"
+ # filter_tags: "LastAccessedProvider"
+ # filter_tags: "LastAccessedStorage"
+ # filter_tags: "LauncherActivity"
+ # filter_tags: "ListDocumentHolder"
+ # filter_tags: "LoadDocStackTask"
+ # filter_tags: "LoadRootTask"
+ # filter_tags: "Loader"
+ # filter_tags: "ManagerActionHandler"
+ # filter_tags: "MenuManager"
+ # filter_tags: "MetadataLoader"
+ # filter_tags: "Metrics"
+ # filter_tags: "Model"
+ # filter_tags: "ModelBackedDocuments"
+ # filter_tags: "MoveJob"
+ # filter_tags: "MultiRootDocsLoader"
+ # filter_tags: "NavigationViewManager"
+ # filter_tags: "OperationDialogFragment"
+ # filter_tags: "PermissionsTest"
+ # filter_tags: "PickActivity"
+ # filter_tags: "PickCountRecordProvider"
+ # filter_tags: "PickCountRecordStorage"
+ # filter_tags: "PickFragment"
+ # filter_tags: "PickerActionHandler"
+ # filter_tags: "PreBootReceiver"
+ # filter_tags: "ProfileTabsController"
+ # filter_tags: "ProvidersCache"
+ # filter_tags: "QuickViewIntentBuilder"
+ # filter_tags: "ReadableArchive"
+ # filter_tags: "RefreshTask"
+ # filter_tags: "ResolvedResourcesJob"
+ # filter_tags: "RootCursorWrapper"
+ # filter_tags: "RootInfo"
+ # filter_tags: "RootItem"
+ # filter_tags: "RootUiTest"
+ # filter_tags: "RootsDragHost"
+ # filter_tags: "RootsFragment"
+ # filter_tags: "RootsListBot"
+ # filter_tags: "SaveFragment"
+ # filter_tags: "ScaleHelper"
+ # filter_tags: "ScopedAccessMetrics"
+ # filter_tags: "SearchFragment"
+ # filter_tags: "SearchHistoryManager"
+ # filter_tags: "SearchManager"
+ # filter_tags: "SectioningDocumentsAdapterWrapper"
+ # filter_tags: "SelectionMetadata"
+ # filter_tags: "SharedInputHandler"
+ # filter_tags: "SortModel"
+ # filter_tags: "SpacerItem"
+ # filter_tags: "State"
+ # filter_tags: "StubProvider"
+ # filter_tags: "TestContextResolver"
+ # filter_tags: "TestNotificationService"
+ # filter_tags: "ThemeOverlayManager"
+ # filter_tags: "ThumbnailLoader"
+ # filter_tags: "UserIdManager"
+ # filter_tags: "WriteableArchive"
+ # filter_tags: "dirlist.DragHost"
+ }
+ }
+}
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 6c39b8e..e68194b 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -27,7 +27,7 @@
<string name="title_open" msgid="3165686459158020921">"ክፈት ከ"</string>
<string name="title_save" msgid="4384490653102710025">"አስቀምጥ ወደ"</string>
<string name="menu_create_dir" msgid="2413624798689091042">"አዲስ አቃፊ"</string>
- <string name="menu_grid" msgid="1453636521731880680">"የፍርግርግ እይታ"</string>
+ <string name="menu_grid" msgid="1453636521731880680">"የፍርግርግ ዕይታ"</string>
<string name="menu_list" msgid="6714267452146410402">"የዝርዝር ዕይታ"</string>
<string name="menu_search" msgid="1876699106790719849">"ፈልግ"</string>
<string name="menu_settings" msgid="6520844520117939047">"የማከማቻ ቅንብሮች"</string>
@@ -74,11 +74,11 @@
<string name="sort_dimension_size" msgid="2190547351159472884">"መጠን"</string>
<string name="sort_dimension_date" msgid="4231005651895254033">"የተቀየረበት ጊዜ"</string>
<string name="sort_dimension_name_ascending" msgid="3715725253270614742">"የፋይል ስም (ሀ እስከ ፐ)"</string>
- <string name="sort_dimension_file_type_ascending" msgid="3466973933402894292">"አይነት (ሀ እስከ ፐ)"</string>
+ <string name="sort_dimension_file_type_ascending" msgid="3466973933402894292">"ዓይነት (ሀ እስከ ፐ)"</string>
<string name="sort_dimension_size_ascending" msgid="2617359017800057762">"መጠን (ትንሹ መጀመሪያ)"</string>
<string name="sort_dimension_date_ascending" msgid="7920068979386920559">"ተቀይሯል (የድሮው መጀመሪያ)"</string>
<string name="sort_dimension_name_descending" msgid="1514525521100423342">"የፋይል ስም (ፐ እስከ ሀ)"</string>
- <string name="sort_dimension_file_type_descending" msgid="3344796059784511100">"አይነት (ፐ እስከ ሀ)"</string>
+ <string name="sort_dimension_file_type_descending" msgid="3344796059784511100">"ዓይነት (ፐ እስከ ሀ)"</string>
<string name="sort_dimension_size_descending" msgid="2367030017597348938">"መጠን (ትልቁ መጀመሪያ)"</string>
<string name="sort_dimension_date_descending" msgid="5466839955763127113">"ተቀይሯል (አዲሱ መጀመሪያ)"</string>
<string name="sort_dimension_dialog_title" msgid="3048641573962982157">"ደርድር በ"</string>
@@ -264,7 +264,7 @@
<string name="chip_title_from_this_week" msgid="4961536405220379672">"በዚህ ሳምንት"</string>
<string name="input_hint_new_folder" msgid="479672411219725677">"የአቃፊ ስም"</string>
<string name="input_hint_rename" msgid="8390711744270994452">"አዲስ ስም"</string>
- <string name="preview_file" msgid="4056622696305432343">"<xliff:g id="FILENAME">%1$s</xliff:g> ፋይሉን በቅድመ እይታ ይመልከቱ"</string>
+ <string name="preview_file" msgid="4056622696305432343">"<xliff:g id="FILENAME">%1$s</xliff:g> ፋይሉን በቅድመ ዕይታ ይመልከቱ"</string>
<string name="preview_work_file" msgid="4495643735563487273">"የሥራ ፋይሉን <xliff:g id="FILENAME">%1$s</xliff:g> በቅድመ ዕይታ አሳይ"</string>
<string name="apps_row_title" msgid="3340490016663092925">"ፋይሎችን በሌሎች መተግበሪያዎች ውስጥ ያስሱ"</string>
<string name="anonymous_application" msgid="7633027057951625862">"ስም-አልባ"</string>
diff --git a/res/values-b+sr+Latn/inspector_strings.xml b/res/values-b+sr+Latn/inspector_strings.xml
index 5bc9b8c..528cbd0 100644
--- a/res/values-b+sr+Latn/inspector_strings.xml
+++ b/res/values-b+sr+Latn/inspector_strings.xml
@@ -33,7 +33,7 @@
<string name="metadata_camera" msgid="2363009732801281319">"Kamera"</string>
<string name="metadata_camera_format" msgid="1494489751904311612">"<xliff:g id="MAKE">%1$s</xliff:g> <xliff:g id="MODEL">%2$s</xliff:g>"</string>
<string name="metadata_aperture" msgid="6538741952698935357">"Otvor blende"</string>
- <string name="metadata_shutter_speed" msgid="8204739885103326131">"Brzina okidača"</string>
+ <string name="metadata_shutter_speed" msgid="8204739885103326131">"Ekspozicija"</string>
<string name="metadata_duration" msgid="3115494422055472715">"Trajanje"</string>
<string name="metadata_date_time" msgid="1090351199248114406">"Snimljeno"</string>
<string name="metadata_focal_length" msgid="3440735161407699893">"Žižna daljina"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index c3de830..bd7d89d 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -272,7 +272,7 @@
<string name="open_tree_dialog_title" msgid="6339509533852318569">"Vil du give <xliff:g id="APPNAME">%1$s</xliff:g> adgang til filerne i <xliff:g id="DIRECTORY">%2$s</xliff:g>?"</string>
<string name="open_tree_dialog_message" msgid="4120695398430659628">"Dette giver <xliff:g id="APPNAME">%1$s</xliff:g> adgang til aktuelt og fremtidigt indhold, som gemmes i <xliff:g id="DIRECTORY">%2$s</xliff:g>."</string>
<string name="directory_blocked_header_title" msgid="1164584889578740066">"Denne mappe kan ikke bruges"</string>
- <string name="directory_blocked_header_subtitle" msgid="2829150911849033408">"For at beskytte dine personlige oplysninger skal du vælge en anden mappe"</string>
+ <string name="directory_blocked_header_subtitle" msgid="2829150911849033408">"For at beskytte dit privatliv skal du vælge en anden mappe"</string>
<string name="create_new_folder_button" msgid="8859613309559794890">"Opret ny mappe"</string>
<string name="search_bar_hint" msgid="4517366509897977321">"Søg på denne telefon"</string>
<string name="delete_search_history" msgid="2202015025607694515">"Slet søgehistorikken <xliff:g id="TEXT">%1$s</xliff:g>"</string>
diff --git a/res/values-eu/inspector_strings.xml b/res/values-eu/inspector_strings.xml
index cf901d7..7e8f506 100644
--- a/res/values-eu/inspector_strings.xml
+++ b/res/values-eu/inspector_strings.xml
@@ -33,7 +33,7 @@
<string name="metadata_camera" msgid="2363009732801281319">"Kamera"</string>
<string name="metadata_camera_format" msgid="1494489751904311612">"<xliff:g id="MAKE">%1$s</xliff:g> <xliff:g id="MODEL">%2$s</xliff:g>"</string>
<string name="metadata_aperture" msgid="6538741952698935357">"Irekidura"</string>
- <string name="metadata_shutter_speed" msgid="8204739885103326131">"Kliskagailuaren abiadura"</string>
+ <string name="metadata_shutter_speed" msgid="8204739885103326131">"Obturadorearen abiadura"</string>
<string name="metadata_duration" msgid="3115494422055472715">"Iraupena"</string>
<string name="metadata_date_time" msgid="1090351199248114406">"Data"</string>
<string name="metadata_focal_length" msgid="3440735161407699893">"Foku-distantzia"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 7133225..37b58da 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -92,7 +92,7 @@
<string name="save_error" msgid="8631128801982095782">"Ezin izan da gorde dokumentua"</string>
<string name="create_error" msgid="3092144450044861994">"Ezin izan da sortu karpeta"</string>
<string name="query_error" msgid="6625421453613879336">"Une honetan ezin da kargatu edukia"</string>
- <string name="quiet_mode_error_title" msgid="554319751414657910">"Pausatu egin dira laneko aplikazioak"</string>
+ <string name="quiet_mode_error_title" msgid="554319751414657910">"Pausatuta daude laneko aplikazioak"</string>
<string name="quiet_mode_button" msgid="8051436551926677305">"Aktibatu laneko aplikazioak"</string>
<string name="cant_select_work_files_error_title" msgid="6688716319549644354">"Ezin dira hautatu laneko fitxategiak"</string>
<string name="cant_select_work_files_error_message" msgid="683480676150690641">"IKT saileko administratzaileak ez dizu ematen baimenik laneko fitxategiak aplikazio pertsonal batetik atzitzeko"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index d0961ba..f40b13d 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -77,8 +77,8 @@
<string name="sort_dimension_file_type_ascending" msgid="3466973933402894292">"نوع (بهترتیب حروف الفبا)"</string>
<string name="sort_dimension_size_ascending" msgid="2617359017800057762">"اندازه (ابتدا کوچکترین)"</string>
<string name="sort_dimension_date_ascending" msgid="7920068979386920559">"اصلاحشده (ابتدا قدیمیترین)"</string>
- <string name="sort_dimension_name_descending" msgid="1514525521100423342">"نام فایل (Z تا A)"</string>
- <string name="sort_dimension_file_type_descending" msgid="3344796059784511100">"نوع (Z تا A)"</string>
+ <string name="sort_dimension_name_descending" msgid="1514525521100423342">"نام فایل (ی تا الف)"</string>
+ <string name="sort_dimension_file_type_descending" msgid="3344796059784511100">"نوع (ی تا الف)"</string>
<string name="sort_dimension_size_descending" msgid="2367030017597348938">"اندازه (ابتدا بزرگترین)"</string>
<string name="sort_dimension_date_descending" msgid="5466839955763127113">"اصلاحشده (ابتدا جدیدترین)"</string>
<string name="sort_dimension_dialog_title" msgid="3048641573962982157">"مرتب کردن براساس"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 7544057..cad5d3e 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -255,7 +255,7 @@
<string name="name_conflict" msgid="28407269328862986">"Un fichier du même nom existe déjà."</string>
<string name="authentication_required" msgid="8030880723643436099">"Pour afficher ce répertoire, connectez-vous à <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="cant_display_content" msgid="8633226333229417237">"Impossible d\'afficher le contenu"</string>
- <string name="sign_in" msgid="6253762676723505592">"Connexion"</string>
+ <string name="sign_in" msgid="6253762676723505592">"Se connecter"</string>
<string name="new_archive_file_name" msgid="1604650338077249838">"archive<xliff:g id="EXTENSION">%s</xliff:g>"</string>
<string name="overwrite_file_confirmation_message" msgid="2496109652768222716">"Remplacer <xliff:g id="NAME">%1$s</xliff:g>?"</string>
<string name="continue_in_background" msgid="1974214559047793331">"Continuer en arrière-plan"</string>
diff --git a/res/values-it/inspector_strings.xml b/res/values-it/inspector_strings.xml
index a853094..7e81318 100644
--- a/res/values-it/inspector_strings.xml
+++ b/res/values-it/inspector_strings.xml
@@ -33,7 +33,7 @@
<string name="metadata_camera" msgid="2363009732801281319">"Fotocamera"</string>
<string name="metadata_camera_format" msgid="1494489751904311612">"<xliff:g id="MAKE">%1$s</xliff:g> <xliff:g id="MODEL">%2$s</xliff:g>"</string>
<string name="metadata_aperture" msgid="6538741952698935357">"Apertura"</string>
- <string name="metadata_shutter_speed" msgid="8204739885103326131">"Velocità otturatore"</string>
+ <string name="metadata_shutter_speed" msgid="8204739885103326131">"Tempo di esposizione"</string>
<string name="metadata_duration" msgid="3115494422055472715">"Durata"</string>
<string name="metadata_date_time" msgid="1090351199248114406">"Data/ora"</string>
<string name="metadata_focal_length" msgid="3440735161407699893">"Lunghezza focale"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 560e73c..a3b3998 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -298,8 +298,8 @@
<string name="create_new_folder_button" msgid="8859613309559794890">"Crea nuova cartella"</string>
<string name="search_bar_hint" msgid="4517366509897977321">"Cerca su questo telefono"</string>
<string name="delete_search_history" msgid="2202015025607694515">"Elimina la cronologia delle ricerche <xliff:g id="TEXT">%1$s</xliff:g>"</string>
- <string name="personal_tab" msgid="3878576287868528503">"Personali"</string>
- <string name="work_tab" msgid="7265359366883747413">"Di lavoro"</string>
+ <string name="personal_tab" msgid="3878576287868528503">"Personale"</string>
+ <string name="work_tab" msgid="7265359366883747413">"Lavoro"</string>
<string name="a11y_work" msgid="7504431382825242153">"Lavoro"</string>
<string name="drag_from_another_app" msgid="8310249276199969905">"Impossibile spostare file da un\'altra app."</string>
<string name="grid_mode_showing" msgid="2803166871485028508">"Visualizzazione in modalità griglia."</string>
diff --git a/res/values-iw/inspector_strings.xml b/res/values-iw/inspector_strings.xml
index 22543c9..2b62b16 100644
--- a/res/values-iw/inspector_strings.xml
+++ b/res/values-iw/inspector_strings.xml
@@ -33,7 +33,7 @@
<string name="metadata_camera" msgid="2363009732801281319">"מצלמה"</string>
<string name="metadata_camera_format" msgid="1494489751904311612">"<xliff:g id="MAKE">%1$s</xliff:g> <xliff:g id="MODEL">%2$s</xliff:g>"</string>
<string name="metadata_aperture" msgid="6538741952698935357">"צמצם"</string>
- <string name="metadata_shutter_speed" msgid="8204739885103326131">"מהירות תריס"</string>
+ <string name="metadata_shutter_speed" msgid="8204739885103326131">"מהירות התריס"</string>
<string name="metadata_duration" msgid="3115494422055472715">"משך"</string>
<string name="metadata_date_time" msgid="1090351199248114406">"תאריך צילום:"</string>
<string name="metadata_focal_length" msgid="3440735161407699893">"אורך מוקד"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index f61bbb8..1f94cad 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -92,7 +92,7 @@
<string name="save_error" msgid="8631128801982095782">"ドキュメントを保存できませんでした"</string>
<string name="create_error" msgid="3092144450044861994">"フォルダを作成できませんでした"</string>
<string name="query_error" msgid="6625421453613879336">"現在、コンテンツを読み込むことができません"</string>
- <string name="quiet_mode_error_title" msgid="554319751414657910">"仕事用アプリは一時停止されています"</string>
+ <string name="quiet_mode_error_title" msgid="554319751414657910">"仕事用アプリ一時停止中"</string>
<string name="quiet_mode_button" msgid="8051436551926677305">"仕事用アプリをオンにする"</string>
<string name="cant_select_work_files_error_title" msgid="6688716319549644354">"仕事用ファイルは選択できません"</string>
<string name="cant_select_work_files_error_message" msgid="683480676150690641">"IT 管理者は、個人用アプリから仕事用ファイルにアクセスすることを許可していません"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index df4cab1..b654b6d 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -202,8 +202,8 @@
<string name="rename_error" msgid="6700093173508118635">"ಡಾಕ್ಯುಮೆಂಟ್ ಮರುಹೆಸರಿಸಲು ವಿಫಲವಾಗಿದೆ"</string>
<string name="menu_eject_root" msgid="9215040039374893613">"ಇಜೆಕ್ಟ್"</string>
<string name="notification_copy_files_converted_title" msgid="6916768494891833365">"ಕೆಲವು ಫೈಲ್ಗಳನ್ನು ಪರಿವರ್ತಿಸಲಾಗಿದೆ"</string>
- <string name="open_external_dialog_request" msgid="8173558471322861268">"<xliff:g id="STORAGE"><i>^3</i></xliff:g> ರಲ್ಲಿ <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ಡೈರೆಕ್ಟರಿಗೆ <xliff:g id="APPNAME"><b>^1</b></xliff:g> ಪ್ರವೇಶ ನೀಡುವುದೇ?"</string>
- <string name="open_external_dialog_request_primary_volume" msgid="2240992164087948176">"<xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ಡೈರೆಕ್ಟರಿಗೆ <xliff:g id="APPNAME"><b>^1</b></xliff:g> ಗೆ ಪ್ರವೇಶ ನೀಡುವುದೇ?"</string>
+ <string name="open_external_dialog_request" msgid="8173558471322861268">"<xliff:g id="STORAGE"><i>^3</i></xliff:g> ರಲ್ಲಿ <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ಡೈರೆಕ್ಟರಿಗೆ <xliff:g id="APPNAME"><b>^1</b></xliff:g> ಆ್ಯಕ್ಸೆಸ್ ನೀಡುವುದೇ?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="2240992164087948176">"<xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ಡೈರೆಕ್ಟರಿಗೆ <xliff:g id="APPNAME"><b>^1</b></xliff:g> ಗೆ ಆ್ಯಕ್ಸೆಸ್ ನೀಡುವುದೇ?"</string>
<string name="open_external_dialog_root_request" msgid="6776729293982633">"<xliff:g id="STORAGE"><i>^2</i></xliff:g> ಸಂಗ್ರಹಣೆಯಲ್ಲಿನ ಪೋಟೋಗಳು ಮತ್ತು ವೀಡಿಯೊಗಳು ಸೇರಿದಂತೆ ನಿಮ್ಮ ಡೇಟಾವನ್ನು ಪ್ರವೇಶಿಸಲು <xliff:g id="APPNAME"><b>^1</b></xliff:g> ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುವುದೇ?"</string>
<string name="allow" msgid="1275746941353040309">"ಅನುಮತಿಸಿ"</string>
<string name="deny" msgid="5127201668078153379">"ನಿರಾಕರಿಸಿ"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index ce3e7df..5a3ee30 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -56,7 +56,7 @@
<string name="menu_paste_from_clipboard" msgid="360947260414135827">"Залепи"</string>
<string name="menu_paste_into_folder" msgid="8000644546983240101">"Залепи во папка"</string>
<string name="menu_advanced_show" msgid="7558626506462906726">"Прикажи внатрешен капацитет"</string>
- <string name="menu_advanced_hide" msgid="6488381508009246334">"Сокриј внатр. меморија"</string>
+ <string name="menu_advanced_hide" msgid="6488381508009246334">"Скриј внатр. меморија"</string>
<string name="button_select" msgid="240863497069321364">"Избери"</string>
<string name="button_copy" msgid="8219059853840996027">"Копирај"</string>
<string name="button_compress" msgid="8951561310857223966">"Компримирај"</string>
@@ -88,7 +88,7 @@
<string name="sort_direction_descending" msgid="1729187589765894076">"Опаѓачко"</string>
<string name="open_external_app" msgid="7107920381038980086">"Отворете <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
<string name="drawer_open" msgid="8071673398187261741">"Прикажи корени"</string>
- <string name="drawer_close" msgid="4263880768630848848">"Сокриј корени"</string>
+ <string name="drawer_close" msgid="4263880768630848848">"Скриј корени"</string>
<string name="save_error" msgid="8631128801982095782">"Документот не успеа да се зачува"</string>
<string name="create_error" msgid="3092144450044861994">"Папката не успеа да се создаде"</string>
<string name="query_error" msgid="6625421453613879336">"Во моментов не може да се вчита содржината."</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 72ac0e0..7b9cab9 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -103,7 +103,7 @@
<string name="cant_save_to_personal_error_title" msgid="858327493694069780">"व्यक्तिगत प्रोफाइलमा सुरक्षित गर्न सकिएन"</string>
<string name="cant_save_to_personal_error_message" msgid="6991758723736381751">"तपाईंका IT एडमिनले तपाईंलाई आफ्नो व्यक्तिगत प्रोफाइलमा रहेका एपहरूमा कार्यालयका फाइलहरू सुरक्षित गर्ने अनुमति दिनुभएको छैन"</string>
<string name="cross_profile_action_not_allowed_title" msgid="6611281348716476478">"यो कार्य गर्ने अनुमति छैन"</string>
- <string name="cross_profile_action_not_allowed_message" msgid="7331275433061690947">"थप जानकारी प्राप्त गर्न आफ्ना IT प्रशासकसँग सम्पर्क गर्नुहोस्"</string>
+ <string name="cross_profile_action_not_allowed_message" msgid="7331275433061690947">"थप जानकारी प्राप्त गर्न आफ्ना IT एड्मिनलाई सम्पर्क गर्नुहोस्"</string>
<string name="root_recent" msgid="1080156975424341623">"हालको"</string>
<string name="root_available_bytes" msgid="8269870862691408864">"<xliff:g id="SIZE">%1$s</xliff:g> खाली छ"</string>
<string name="root_type_service" msgid="6521366147466512289">"भण्डारण सम्बन्धी सेवाहरू"</string>
@@ -152,8 +152,8 @@
<string name="delete_preparing" msgid="7339349837842802508">"तयार पार्दै..."</string>
<string name="delete_progress" msgid="2627631054702306423">"<xliff:g id="COUNT_0">%1$d</xliff:g> / <xliff:g id="TOTALCOUNT">%2$d</xliff:g>"</string>
<plurals name="copy_error_notification_title" formatted="false" msgid="3188432450429390963">
- <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> वस्तुहरूलाई प्रतिलिपि गर्न सकिएन</item>
- <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> वस्तुलाई प्रतिलिपि गर्न सकिएन</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> वस्तुहरूलाई कपी गर्न सकिएन</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> वस्तुलाई कपी गर्न सकिएन</item>
</plurals>
<plurals name="compress_error_notification_title" formatted="false" msgid="3043630066678213644">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> वटा फाइल कम्प्रेस गर्न सकिएन</item>
@@ -194,8 +194,8 @@
<item quantity="one">यो फाइललाई अर्को ढाँचामा रूपान्तरण गरिएको छ: <xliff:g id="LIST_0">%1$s</xliff:g></item>
</plurals>
<plurals name="clipboard_files_clipped" formatted="false" msgid="4847061634862926902">
- <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> वस्तुहरूलाई क्लिपबोर्डमा प्रतिलिपि गरियो।</item>
- <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> वस्तुलाई क्लिपबोर्डमा प्रतिलिपि गरियो।</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> वटा फाइल क्लिपबोर्डमा कपी गरिए।</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> फाइल क्लिपबोर्डमा कपी गरियो।</item>
</plurals>
<string name="file_operation_rejected" msgid="4301554203329008794">"फाइल सम्बन्धी कार्य समर्थित छैन।"</string>
<string name="file_operation_error" msgid="2234357335716533795">"फाइल सम्बन्धी कार्य असफल भयो।"</string>
diff --git a/res/values-or/inspector_strings.xml b/res/values-or/inspector_strings.xml
index 99f5573..f0d68e1 100644
--- a/res/values-or/inspector_strings.xml
+++ b/res/values-or/inspector_strings.xml
@@ -30,7 +30,7 @@
<string name="metadata_coordinates" msgid="6897383227370702734">"ନିର୍ଦ୍ଦେଶାଙ୍କ"</string>
<string name="metadata_coordinates_format" msgid="1402724596764547761">"<xliff:g id="LATITUDE">%1$,.3f</xliff:g>, <xliff:g id="LONGITUDE">%2$,.3f</xliff:g>"</string>
<string name="metadata_altitude" msgid="8063792127436794294">"ଉଚ୍ଚତା"</string>
- <string name="metadata_camera" msgid="2363009732801281319">"କ୍ୟାମେରା"</string>
+ <string name="metadata_camera" msgid="2363009732801281319">"କେମେରା"</string>
<string name="metadata_camera_format" msgid="1494489751904311612">"<xliff:g id="MAKE">%1$s</xliff:g> <xliff:g id="MODEL">%2$s</xliff:g>"</string>
<string name="metadata_aperture" msgid="6538741952698935357">"ଆପାର୍ଚର୍"</string>
<string name="metadata_shutter_speed" msgid="8204739885103326131">"ଶଟର୍ ସ୍ପୀଡ୍"</string>
@@ -42,6 +42,6 @@
<string name="metadata_iso_format" msgid="4153285204012694861">"ISO <xliff:g id="ISO_SPEED">%1$d</xliff:g>"</string>
<string name="metadata_artist" msgid="8972421485694988540">"କଳାକାର"</string>
<string name="metadata_composer" msgid="4696926808308256056">"କମ୍ପୋଜର୍"</string>
- <string name="metadata_album" msgid="1661699531214720236">"ଆଲବମ୍"</string>
+ <string name="metadata_album" msgid="1661699531214720236">"ଆଲବମ"</string>
<string name="metadata_address" msgid="1849921023707744640">"ଲୋକେସନ"</string>
</resources>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 50c3d00..fa895c7 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -34,7 +34,7 @@
<string name="menu_open" msgid="9092138100049759315">"ଖୋଲନ୍ତୁ"</string>
<string name="menu_open_with" msgid="5507647065467520229">"ଏଥିରେ ଖୋଲନ୍ତୁ"</string>
<string name="menu_open_in_new_window" msgid="6686563636123311276">"ନୂଆ ୱିଣ୍ଡୋରେ ଖୋଲନ୍ତୁ"</string>
- <string name="menu_save" msgid="5195367497138965168">"ସେଭ୍ କରନ୍ତୁ"</string>
+ <string name="menu_save" msgid="5195367497138965168">"ସେଭ କରନ୍ତୁ"</string>
<string name="menu_share" msgid="4307140947108068356">"ସେୟାର୍ କରନ୍ତୁ"</string>
<string name="menu_delete" msgid="1022254131543256626">"ଡିଲିଟ୍ କରନ୍ତୁ"</string>
<string name="menu_select_all" msgid="7600576812185570403">"ସବୁ ଚୟନ କରନ୍ତୁ"</string>
diff --git a/res/values-pt-rBR/strings.xml b/res/values-pt-rBR/strings.xml
index d751a55..96030ed 100644
--- a/res/values-pt-rBR/strings.xml
+++ b/res/values-pt-rBR/strings.xml
@@ -253,7 +253,7 @@
<string name="images_shortcut_label" msgid="2545168016070493574">"Imagens"</string>
<string name="archive_loading_failed" msgid="7243436722828766996">"Não foi possível abrir o arquivo para navegação, pois ele está corrompido ou tem um formato incompatível."</string>
<string name="name_conflict" msgid="28407269328862986">"Já existe um arquivo com esse nome."</string>
- <string name="authentication_required" msgid="8030880723643436099">"Para ver esse diretório, faça login em <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="authentication_required" msgid="8030880723643436099">"Para acessar esse diretório, faça login em <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="cant_display_content" msgid="8633226333229417237">"Não é possível exibir conteúdos"</string>
<string name="sign_in" msgid="6253762676723505592">"Fazer login"</string>
<string name="new_archive_file_name" msgid="1604650338077249838">"arquivo<xliff:g id="EXTENSION">%s</xliff:g>"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 7a69fd6..9eb8f12 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -218,9 +218,9 @@
<string name="rename_error" msgid="6700093173508118635">"Falha ao mudar o nome do documento"</string>
<string name="menu_eject_root" msgid="9215040039374893613">"Ejetar"</string>
<string name="notification_copy_files_converted_title" msgid="6916768494891833365">"Alguns ficheiros foram convertidos"</string>
- <string name="open_external_dialog_request" msgid="8173558471322861268">"Pretende conceder a <xliff:g id="APPNAME"><b>^1</b></xliff:g> acesso ao diretório <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> no(a) <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
- <string name="open_external_dialog_request_primary_volume" msgid="2240992164087948176">"Pretende conceder a <xliff:g id="APPNAME"><b>^1</b></xliff:g> acesso ao diretório <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
- <string name="open_external_dialog_root_request" msgid="6776729293982633">"Pretende conceder a <xliff:g id="APPNAME"><b>^1</b></xliff:g> acesso aos seus dados, incluindo fotos e vídeos, no(a) <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request" msgid="8173558471322861268">"Quer conceder a <xliff:g id="APPNAME"><b>^1</b></xliff:g> acesso ao diretório <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> no(a) <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="2240992164087948176">"Quer conceder a <xliff:g id="APPNAME"><b>^1</b></xliff:g> acesso ao diretório <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="6776729293982633">"Quer conceder a <xliff:g id="APPNAME"><b>^1</b></xliff:g> acesso aos seus dados, incluindo fotos e vídeos, no(a) <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="allow" msgid="1275746941353040309">"Permitir"</string>
<string name="deny" msgid="5127201668078153379">"Recusar"</string>
<plurals name="elements_selected" formatted="false" msgid="4448165978637163692">
@@ -257,7 +257,7 @@
<string name="cant_display_content" msgid="8633226333229417237">"Não é possível apresentar conteúdos"</string>
<string name="sign_in" msgid="6253762676723505592">"Iniciar sessão"</string>
<string name="new_archive_file_name" msgid="1604650338077249838">"arquivo<xliff:g id="EXTENSION">%s</xliff:g>"</string>
- <string name="overwrite_file_confirmation_message" msgid="2496109652768222716">"Pretende substituir <xliff:g id="NAME">%1$s</xliff:g>?"</string>
+ <string name="overwrite_file_confirmation_message" msgid="2496109652768222716">"Quer substituir <xliff:g id="NAME">%1$s</xliff:g>?"</string>
<string name="continue_in_background" msgid="1974214559047793331">"Continuar em segundo plano"</string>
<plurals name="selected_count" formatted="false" msgid="7555250236512981129">
<item quantity="many"><xliff:g id="COUNT_1">%1$d</xliff:g> selecionados</item>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index d751a55..96030ed 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -253,7 +253,7 @@
<string name="images_shortcut_label" msgid="2545168016070493574">"Imagens"</string>
<string name="archive_loading_failed" msgid="7243436722828766996">"Não foi possível abrir o arquivo para navegação, pois ele está corrompido ou tem um formato incompatível."</string>
<string name="name_conflict" msgid="28407269328862986">"Já existe um arquivo com esse nome."</string>
- <string name="authentication_required" msgid="8030880723643436099">"Para ver esse diretório, faça login em <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="authentication_required" msgid="8030880723643436099">"Para acessar esse diretório, faça login em <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="cant_display_content" msgid="8633226333229417237">"Não é possível exibir conteúdos"</string>
<string name="sign_in" msgid="6253762676723505592">"Fazer login"</string>
<string name="new_archive_file_name" msgid="1604650338077249838">"arquivo<xliff:g id="EXTENSION">%s</xliff:g>"</string>
diff --git a/res/values-sk/inspector_strings.xml b/res/values-sk/inspector_strings.xml
index de234a5..8b4634e 100644
--- a/res/values-sk/inspector_strings.xml
+++ b/res/values-sk/inspector_strings.xml
@@ -33,7 +33,7 @@
<string name="metadata_camera" msgid="2363009732801281319">"Fotoaparát"</string>
<string name="metadata_camera_format" msgid="1494489751904311612">"<xliff:g id="MAKE">%1$s</xliff:g> <xliff:g id="MODEL">%2$s</xliff:g>"</string>
<string name="metadata_aperture" msgid="6538741952698935357">"Clona"</string>
- <string name="metadata_shutter_speed" msgid="8204739885103326131">"Rýchlosť uzávierky"</string>
+ <string name="metadata_shutter_speed" msgid="8204739885103326131">"Expozičný čas"</string>
<string name="metadata_duration" msgid="3115494422055472715">"Trvanie"</string>
<string name="metadata_date_time" msgid="1090351199248114406">"Nasnímané"</string>
<string name="metadata_focal_length" msgid="3440735161407699893">"Ohnisková vzdialenosť"</string>
diff --git a/res/values-sq/inspector_strings.xml b/res/values-sq/inspector_strings.xml
index 6d2bc10..6c4166c 100644
--- a/res/values-sq/inspector_strings.xml
+++ b/res/values-sq/inspector_strings.xml
@@ -19,7 +19,7 @@
<string name="inspector_title" msgid="1924760928091740238">"Informacione"</string>
<string name="inspector_load_error" msgid="7522190243413249291">"Informacioni i skedarit nuk mund të ngarkohej"</string>
<string name="inspector_debug_section" msgid="2576052661505700421">"Informacioni i korrigjimit (vetëm zhvilluesit)"</string>
- <string name="inspector_debug_metadata_section" msgid="5875140675600744846">"Të dhënat meta të papërpunuara: <xliff:g id="METADATATYPE">%1$s</xliff:g>"</string>
+ <string name="inspector_debug_metadata_section" msgid="5875140675600744846">"Metadatat e papërpunuara: <xliff:g id="METADATATYPE">%1$s</xliff:g>"</string>
<string name="inspector_metadata_section" msgid="6077622515328240575">"Detajet e medias"</string>
<string name="handler_app_file_opens_with" msgid="5272329600389613550">"Ky lloj skedari hapet me"</string>
<string name="handler_app_belongs_to" msgid="5444507329303253553">"Ky skedar ofrohet nga"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 96f01f2..30d115d 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -117,7 +117,7 @@
<string name="toast_failed_delete" msgid="3453846588205817591">"E pamundur të fshihen disa dokumente"</string>
<string name="toast_share_over_limit" msgid="5805442886537093015">"Nuk mund të ndahen më shumë se <xliff:g id="COUNT">%1$d</xliff:g> skedarë"</string>
<string name="toast_action_not_allowed" msgid="1329382474450572415">"Veprimi nuk lejohet"</string>
- <string name="share_via" msgid="8725082736005677161">"Shpërndaj nëpërmjet"</string>
+ <string name="share_via" msgid="8725082736005677161">"Ndaj nëpërmjet"</string>
<string name="copy_notification_title" msgid="52256435625098456">"Po kopjon skedarët"</string>
<string name="compress_notification_title" msgid="6830195148113751021">"Skedarët po ngjishen"</string>
<string name="extract_notification_title" msgid="5067393961754430469">"Po nxjerr skedarët"</string>
diff --git a/res/values-sr/inspector_strings.xml b/res/values-sr/inspector_strings.xml
index 8e4e682..2bf1962 100644
--- a/res/values-sr/inspector_strings.xml
+++ b/res/values-sr/inspector_strings.xml
@@ -33,7 +33,7 @@
<string name="metadata_camera" msgid="2363009732801281319">"Камера"</string>
<string name="metadata_camera_format" msgid="1494489751904311612">"<xliff:g id="MAKE">%1$s</xliff:g> <xliff:g id="MODEL">%2$s</xliff:g>"</string>
<string name="metadata_aperture" msgid="6538741952698935357">"Отвор бленде"</string>
- <string name="metadata_shutter_speed" msgid="8204739885103326131">"Брзина окидача"</string>
+ <string name="metadata_shutter_speed" msgid="8204739885103326131">"Експозиција"</string>
<string name="metadata_duration" msgid="3115494422055472715">"Трајање"</string>
<string name="metadata_date_time" msgid="1090351199248114406">"Снимљено"</string>
<string name="metadata_focal_length" msgid="3440735161407699893">"Жижна даљина"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index b238e78..5ec185c 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -45,7 +45,7 @@
<string name="menu_move" msgid="2310760789561129882">"இங்கு நகர்த்து…"</string>
<string name="menu_compress" msgid="37539111904724188">"அளவைக் குறை"</string>
<string name="menu_extract" msgid="8171946945982532262">"இங்கு பிரி…"</string>
- <string name="menu_rename" msgid="1883113442688817554">"மறுபெயரிடு"</string>
+ <string name="menu_rename" msgid="1883113442688817554">"பெயர் மாற்று"</string>
<string name="menu_inspect" msgid="7279855349299446224">"தகவலைப் பெறு"</string>
<string name="menu_show_hidden_files" msgid="5140676344684492769">"மறைக்கப்பட்ட ஃபைல்களைக் காட்டு"</string>
<string name="menu_hide_hidden_files" msgid="5654495713350153702">"மறைக்கப்பட்ட ஃபைல்களைக் காட்டாதே"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index eafaf58..b8879c8 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -33,7 +33,7 @@
<string name="menu_settings" msgid="6520844520117939047">"స్టోరేజ్ సెట్టింగ్లు"</string>
<string name="menu_open" msgid="9092138100049759315">"తెరువు"</string>
<string name="menu_open_with" msgid="5507647065467520229">"దీనితో తెరువు"</string>
- <string name="menu_open_in_new_window" msgid="6686563636123311276">"కొత్త విండోలో తెరువు"</string>
+ <string name="menu_open_in_new_window" msgid="6686563636123311276">"కొత్త విండోలో తెరవండి"</string>
<string name="menu_save" msgid="5195367497138965168">"సేవ్ చేయండి"</string>
<string name="menu_share" msgid="4307140947108068356">"షేర్ చేయి"</string>
<string name="menu_delete" msgid="1022254131543256626">"తొలగించండి"</string>
@@ -103,7 +103,7 @@
<string name="cant_save_to_personal_error_title" msgid="858327493694069780">"వ్యక్తిగత ప్రొఫైల్లో సేవ్ చేయడం సాధ్యం కాదు"</string>
<string name="cant_save_to_personal_error_message" msgid="6991758723736381751">"మీ వ్యక్తిగత ప్రొఫైల్లో కార్యాలయ ఫైళ్లను సేవ్ చేయడానికి మీ IT అడ్మిన్ అనుమతించరు"</string>
<string name="cross_profile_action_not_allowed_title" msgid="6611281348716476478">"ఈ చర్య అనుమతించబడదు"</string>
- <string name="cross_profile_action_not_allowed_message" msgid="7331275433061690947">"మరింత తెలుసుకోవడానికి, మీ IT అడ్మిన్ను సంప్రదించండి"</string>
+ <string name="cross_profile_action_not_allowed_message" msgid="7331275433061690947">"మరింత తెలుసుకోవడానికి, మీ IT అడ్మిన్ను కాంటాక్ట్ చేయండి"</string>
<string name="root_recent" msgid="1080156975424341623">"ఇటీవలివి"</string>
<string name="root_available_bytes" msgid="8269870862691408864">"<xliff:g id="SIZE">%1$s</xliff:g> ఖాళీగా ఉంది"</string>
<string name="root_type_service" msgid="6521366147466512289">"స్టోరేజ్ పరికరాలు"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index e4e7b6a..a01e970 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -18,7 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="missing_rename_error" msgid="9098952207447981103">"คุณต้องเปลี่ยนชื่อนี้"</string>
<string name="add_folder_name_error" msgid="6014615052491406810">"คุณต้องป้อนชื่อโฟลเดอร์"</string>
- <string name="files_label" msgid="771781190045103748">"ไฟล์"</string>
+ <string name="files_label" msgid="771781190045103748">"Files"</string>
<string name="downloads_label" msgid="5462789470049501103">"ดาวน์โหลด"</string>
<!-- no translation found for app_label (8089292432455111409) -->
<skip />
diff --git a/res/values-vi/inspector_strings.xml b/res/values-vi/inspector_strings.xml
index 568d300..048af2b 100644
--- a/res/values-vi/inspector_strings.xml
+++ b/res/values-vi/inspector_strings.xml
@@ -33,7 +33,7 @@
<string name="metadata_camera" msgid="2363009732801281319">"Máy ảnh"</string>
<string name="metadata_camera_format" msgid="1494489751904311612">"<xliff:g id="MAKE">%1$s</xliff:g> <xliff:g id="MODEL">%2$s</xliff:g>"</string>
<string name="metadata_aperture" msgid="6538741952698935357">"Khẩu độ"</string>
- <string name="metadata_shutter_speed" msgid="8204739885103326131">"Tốc độ chụp"</string>
+ <string name="metadata_shutter_speed" msgid="8204739885103326131">"Tốc độ màn trập"</string>
<string name="metadata_duration" msgid="3115494422055472715">"Thời lượng"</string>
<string name="metadata_date_time" msgid="1090351199248114406">"Chụp vào"</string>
<string name="metadata_focal_length" msgid="3440735161407699893">"Tiêu cự"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index cbb702c..b52f20a 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -18,7 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="missing_rename_error" msgid="9098952207447981103">"Bạn cần đổi tên này"</string>
<string name="add_folder_name_error" msgid="6014615052491406810">"Bạn cần thêm tên thư mục"</string>
- <string name="files_label" msgid="771781190045103748">"Tệp"</string>
+ <string name="files_label" msgid="771781190045103748">"Files"</string>
<string name="downloads_label" msgid="5462789470049501103">"Tệp đã tải xuống"</string>
<!-- no translation found for app_label (8089292432455111409) -->
<skip />
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 3b893e6..7cd2401 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -16,8 +16,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="missing_rename_error" msgid="9098952207447981103">"您需要重新命名"</string>
- <string name="add_folder_name_error" msgid="6014615052491406810">"您需要新增資料夾名稱"</string>
+ <string name="missing_rename_error" msgid="9098952207447981103">"你需要重新命名"</string>
+ <string name="add_folder_name_error" msgid="6014615052491406810">"你需要新增資料夾名稱"</string>
<string name="files_label" msgid="771781190045103748">"檔案"</string>
<string name="downloads_label" msgid="5462789470049501103">"下載"</string>
<!-- no translation found for app_label (8089292432455111409) -->
@@ -95,15 +95,15 @@
<string name="quiet_mode_error_title" msgid="554319751414657910">"已暫停工作應用程式"</string>
<string name="quiet_mode_button" msgid="8051436551926677305">"開啟工作應用程式"</string>
<string name="cant_select_work_files_error_title" msgid="6688716319549644354">"無法選取工作檔案"</string>
- <string name="cant_select_work_files_error_message" msgid="683480676150690641">"IT 管理員不允許您透過個人應用程式存取工作檔案"</string>
+ <string name="cant_select_work_files_error_message" msgid="683480676150690641">"IT 管理員不允許你透過個人應用程式存取工作檔案"</string>
<string name="cant_select_personal_files_error_title" msgid="3200697170148617742">"無法選取個人檔案"</string>
- <string name="cant_select_personal_files_error_message" msgid="4105905035459118209">"IT 管理員不允許您透過工作應用程式存取個人檔案"</string>
+ <string name="cant_select_personal_files_error_message" msgid="4105905035459118209">"IT 管理員不允許你透過工作應用程式存取個人檔案"</string>
<string name="cant_save_to_work_error_title" msgid="1351323070040641358">"無法儲存至工作設定檔"</string>
- <string name="cant_save_to_work_error_message" msgid="4975583233814059890">"IT 管理員不允許您將個人檔案儲存至工作設定檔"</string>
+ <string name="cant_save_to_work_error_message" msgid="4975583233814059890">"IT 管理員不允許你將個人檔案儲存至工作設定檔"</string>
<string name="cant_save_to_personal_error_title" msgid="858327493694069780">"無法儲存至個人設定檔"</string>
- <string name="cant_save_to_personal_error_message" msgid="6991758723736381751">"IT 管理員不允許您將工作檔案儲存至個人設定檔"</string>
+ <string name="cant_save_to_personal_error_message" msgid="6991758723736381751">"IT 管理員不允許你將工作檔案儲存至個人設定檔"</string>
<string name="cross_profile_action_not_allowed_title" msgid="6611281348716476478">"不允許執行這項操作"</string>
- <string name="cross_profile_action_not_allowed_message" msgid="7331275433061690947">"如需瞭解詳情,請聯絡 IT 管理員。"</string>
+ <string name="cross_profile_action_not_allowed_message" msgid="7331275433061690947">"詳情請聯絡 IT 管理員。"</string>
<string name="root_recent" msgid="1080156975424341623">"最近項目"</string>
<string name="root_available_bytes" msgid="8269870862691408864">"可用空間:<xliff:g id="SIZE">%1$s</xliff:g>"</string>
<string name="root_type_service" msgid="6521366147466512289">"儲存空間服務"</string>
@@ -272,14 +272,14 @@
<string name="open_tree_dialog_title" msgid="6339509533852318569">"要允許「<xliff:g id="APPNAME">%1$s</xliff:g>」存取「<xliff:g id="DIRECTORY">%2$s</xliff:g>」中的檔案嗎?"</string>
<string name="open_tree_dialog_message" msgid="4120695398430659628">"這樣會允許「<xliff:g id="APPNAME">%1$s</xliff:g>」在今後存取儲存在「<xliff:g id="DIRECTORY">%2$s</xliff:g>」的內容。"</string>
<string name="directory_blocked_header_title" msgid="1164584889578740066">"無法使用此資料夾"</string>
- <string name="directory_blocked_header_subtitle" msgid="2829150911849033408">"為保護您的私隱,請選擇其他資料夾"</string>
+ <string name="directory_blocked_header_subtitle" msgid="2829150911849033408">"為保護你的私隱,請選擇其他資料夾"</string>
<string name="create_new_folder_button" msgid="8859613309559794890">"建立新資料夾"</string>
<string name="search_bar_hint" msgid="4517366509897977321">"在這部手機中搜尋"</string>
<string name="delete_search_history" msgid="2202015025607694515">"刪除搜尋記錄 <xliff:g id="TEXT">%1$s</xliff:g>"</string>
<string name="personal_tab" msgid="3878576287868528503">"個人"</string>
<string name="work_tab" msgid="7265359366883747413">"工作"</string>
<string name="a11y_work" msgid="7504431382825242153">"工作"</string>
- <string name="drag_from_another_app" msgid="8310249276199969905">"您無法移動其他應用程式的檔案。"</string>
+ <string name="drag_from_another_app" msgid="8310249276199969905">"你無法移動其他應用程式的檔案。"</string>
<string name="grid_mode_showing" msgid="2803166871485028508">"用格狀模式顯示緊。"</string>
<string name="list_mode_showing" msgid="1225413902295895166">"用清單模式顯示緊。"</string>
</resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 5b8ff7c..3015e20 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -92,7 +92,7 @@
<string name="save_error" msgid="8631128801982095782">"Yehlulekile ukulondoloza idokhumenti"</string>
<string name="create_error" msgid="3092144450044861994">"Yehlulekile ukudala ifolda"</string>
<string name="query_error" msgid="6625421453613879336">"Ayikwazanga ukulayisha okuqukethwe okwamanje"</string>
- <string name="quiet_mode_error_title" msgid="554319751414657910">"Izinhlelo zokusebenza zomsebenzi ziphunyuziwe"</string>
+ <string name="quiet_mode_error_title" msgid="554319751414657910">"Ama-app omsebenzi amisiwe"</string>
<string name="quiet_mode_button" msgid="8051436551926677305">"Vula ama-app omsebenzi"</string>
<string name="cant_select_work_files_error_title" msgid="6688716319549644354">"Ayikwazi ukukhetha amafayela omsebenzi"</string>
<string name="cant_select_work_files_error_message" msgid="683480676150690641">"Umphathi wakho we-IT akakuvumeli ukuba ufinyelele amafayela omsebenzi avela kuhlelo lokusebenza lomuntu siqu"</string>
diff --git a/src/com/android/documentsui/AbstractActionHandler.java b/src/com/android/documentsui/AbstractActionHandler.java
index a310ace..4407a62 100644
--- a/src/com/android/documentsui/AbstractActionHandler.java
+++ b/src/com/android/documentsui/AbstractActionHandler.java
@@ -779,32 +779,48 @@
}
protected final boolean launchToDocument(Uri uri) {
- // We don't support launching to a document in an archive.
- if (!Providers.isArchiveUri(uri)) {
- loadDocument(uri, UserId.DEFAULT_USER, this::onStackLoaded);
- return true;
+ if (DEBUG) {
+ Log.d(TAG, "launchToDocument() uri=" + uri);
}
- return false;
+ // We don't support launching to a document in an archive.
+ if (Providers.isArchiveUri(uri)) {
+ return false;
+ }
+
+ loadDocument(uri, UserId.DEFAULT_USER, this::onStackToLaunchToLoaded);
+ return true;
}
- private void onStackLoaded(@Nullable DocumentStack stack) {
- if (stack != null) {
- if (!stack.peek().isDirectory()) {
- // Requested document is not a directory. Pop it so that we can launch into its
- // parent.
- stack.pop();
- }
- mState.stack.reset(stack);
- mActivity.refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
+ /**
+ * Invoked <b>only</b> once, when the initial stack (that is the stack we are going to
+ * "launch to") is loaded.
+ *
+ * @see #launchToDocument(Uri)
+ */
+ private void onStackToLaunchToLoaded(@Nullable DocumentStack stack) {
+ if (DEBUG) {
+ Log.d(TAG, "onLaunchStackLoaded() stack=" + stack);
+ }
- Metrics.logLaunchAtLocation(mState, stack.getRoot().getUri());
- } else {
+ if (stack == null) {
Log.w(TAG, "Failed to launch into the given uri. Launch to default location.");
launchToDefaultLocation();
Metrics.logLaunchAtLocation(mState, null);
+ return;
}
+
+ // Make sure the document at the top of the stack is a directory (if it isn't - just pop
+ // one off).
+ if (!stack.peek().isDirectory()) {
+ stack.pop();
+ }
+
+ mState.stack.reset(stack);
+ mActivity.refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
+
+ Metrics.logLaunchAtLocation(mState, stack.getRoot().getUri());
}
private void onRootLoaded(@Nullable RootInfo root) {
diff --git a/src/com/android/documentsui/BaseActivity.java b/src/com/android/documentsui/BaseActivity.java
index c6cbc19..f08294e 100644
--- a/src/com/android/documentsui/BaseActivity.java
+++ b/src/com/android/documentsui/BaseActivity.java
@@ -75,7 +75,6 @@
import com.android.documentsui.sorting.SortController;
import com.android.documentsui.sorting.SortModel;
-import com.android.documentsui.util.VersionUtils;
import com.google.android.material.appbar.AppBarLayout;
import java.util.ArrayList;
@@ -504,50 +503,39 @@
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- onBackPressed();
- return true;
-
- case R.id.option_menu_create_dir:
- getInjector().actions.showCreateDirectoryDialog();
- return true;
-
- case R.id.option_menu_search:
- // SearchViewManager listens for this directly.
- return false;
-
- case R.id.option_menu_select_all:
- getInjector().actions.selectAllFiles();
- return true;
-
- case R.id.option_menu_debug:
- getInjector().actions.showDebugMessage();
- return true;
-
- case R.id.option_menu_sort:
- getInjector().actions.showSortDialog();
- return true;
-
- case R.id.option_menu_launcher:
- getInjector().actions.switchLauncherIcon();
- return true;
-
- case R.id.option_menu_show_hidden_files:
- onClickedShowHiddenFiles();
- return true;
-
- case R.id.sub_menu_grid:
- setViewMode(State.MODE_GRID);
- return true;
-
- case R.id.sub_menu_list:
- setViewMode(State.MODE_LIST);
- return true;
-
- default:
- return super.onOptionsItemSelected(item);
+ final int id = item.getItemId();
+ if (id == android.R.id.home) {
+ onBackPressed();
+ return true;
+ } else if (id == R.id.option_menu_create_dir) {
+ getInjector().actions.showCreateDirectoryDialog();
+ return true;
+ } else if (id == R.id.option_menu_search) {
+ // SearchViewManager listens for this directly.
+ return false;
+ } else if (id == R.id.option_menu_select_all) {
+ getInjector().actions.selectAllFiles();
+ return true;
+ } else if (id == R.id.option_menu_debug) {
+ getInjector().actions.showDebugMessage();
+ return true;
+ } else if (id == R.id.option_menu_sort) {
+ getInjector().actions.showSortDialog();
+ return true;
+ } else if (id == R.id.option_menu_launcher) {
+ getInjector().actions.switchLauncherIcon();
+ return true;
+ } else if (id == R.id.option_menu_show_hidden_files) {
+ onClickedShowHiddenFiles();
+ return true;
+ } else if (id == R.id.sub_menu_grid) {
+ setViewMode(MODE_GRID);
+ return true;
+ } else if (id == R.id.sub_menu_list) {
+ setViewMode(State.MODE_LIST);
+ return true;
}
+ return super.onOptionsItemSelected(item);
}
protected final @Nullable DirectoryFragment getDirectoryFragment() {
diff --git a/src/com/android/documentsui/dirlist/DirectoryFragment.java b/src/com/android/documentsui/dirlist/DirectoryFragment.java
index a5306bd..4444a9c 100644
--- a/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -110,8 +110,8 @@
import com.android.documentsui.services.FileOperations;
import com.android.documentsui.sorting.SortDimension;
import com.android.documentsui.sorting.SortModel;
-
import com.android.documentsui.util.VersionUtils;
+
import com.google.common.base.Objects;
import java.io.IOException;
@@ -833,127 +833,99 @@
MutableSelection<String> selection = new MutableSelection<>();
mSelectionMgr.copySelection(selection);
- switch (item.getItemId()) {
- case R.id.action_menu_select:
- case R.id.dir_menu_open:
- openDocuments(selection);
- mActionModeController.finishActionMode();
- return true;
-
- case R.id.action_menu_open_with:
- case R.id.dir_menu_open_with:
- showChooserForDoc(selection);
- return true;
-
- case R.id.dir_menu_open_in_new_window:
- mActions.openSelectedInNewWindow();
- return true;
-
- case R.id.action_menu_share:
- case R.id.dir_menu_share:
- mActions.shareSelectedDocuments();
- return true;
-
- case R.id.action_menu_delete:
- case R.id.dir_menu_delete:
- // deleteDocuments will end action mode if the documents are deleted.
- // It won't end action mode if user cancels the delete.
- mActions.showDeleteDialog();
- return true;
-
- case R.id.action_menu_copy_to:
- transferDocuments(selection, null, FileOperationService.OPERATION_COPY);
- // TODO: Only finish selection mode if copy-to is not canceled.
- // Need to plum down into handling the way we do with deleteDocuments.
- mActionModeController.finishActionMode();
- return true;
-
- case R.id.action_menu_compress:
- transferDocuments(selection, mState.stack,
- FileOperationService.OPERATION_COMPRESS);
- // TODO: Only finish selection mode if compress is not canceled.
- // Need to plum down into handling the way we do with deleteDocuments.
- mActionModeController.finishActionMode();
- return true;
+ final int id = item.getItemId();
+ if (id == R.id.action_menu_select || id == R.id.dir_menu_open) {
+ openDocuments(selection);
+ mActionModeController.finishActionMode();
+ return true;
+ } else if (id == R.id.action_menu_open_with || id == R.id.dir_menu_open_with) {
+ showChooserForDoc(selection);
+ return true;
+ } else if (id == R.id.dir_menu_open_in_new_window) {
+ mActions.openSelectedInNewWindow();
+ return true;
+ } else if (id == R.id.action_menu_share || id == R.id.dir_menu_share) {
+ mActions.shareSelectedDocuments();
+ return true;
+ } else if (id == R.id.action_menu_delete || id == R.id.dir_menu_delete) {
+ // deleteDocuments will end action mode if the documents are deleted.
+ // It won't end action mode if user cancels the delete.
+ mActions.showDeleteDialog();
+ return true;
+ } else if (id == R.id.action_menu_copy_to) {
+ transferDocuments(selection, null, FileOperationService.OPERATION_COPY);
+ // TODO: Only finish selection mode if copy-to is not canceled.
+ // Need to plum down into handling the way we do with deleteDocuments.
+ mActionModeController.finishActionMode();
+ return true;
+ } else if (id == R.id.action_menu_compress) {
+ transferDocuments(selection, mState.stack,
+ FileOperationService.OPERATION_COMPRESS);
+ // TODO: Only finish selection mode if compress is not canceled.
+ // Need to plum down into handling the way we do with deleteDocuments.
+ mActionModeController.finishActionMode();
+ return true;
// TODO: Implement extract (to the current directory).
- case R.id.action_menu_extract_to:
- transferDocuments(selection, null, FileOperationService.OPERATION_EXTRACT);
- // TODO: Only finish selection mode if compress-to is not canceled.
- // Need to plum down into handling the way we do with deleteDocuments.
- mActionModeController.finishActionMode();
+ } else if (id == R.id.action_menu_extract_to) {
+ transferDocuments(selection, null, FileOperationService.OPERATION_EXTRACT);
+ // TODO: Only finish selection mode if compress-to is not canceled.
+ // Need to plum down into handling the way we do with deleteDocuments.
+ mActionModeController.finishActionMode();
+ return true;
+ } else if (id == R.id.action_menu_move_to) {
+ if (mModel.hasDocuments(selection, DocumentFilters.NOT_MOVABLE)) {
+ mInjector.dialogs.showOperationUnsupported();
return true;
+ }
+ // Exit selection mode first, so we avoid deselecting deleted documents.
+ mActionModeController.finishActionMode();
+ transferDocuments(selection, null, FileOperationService.OPERATION_MOVE);
+ return true;
+ } else if (id == R.id.action_menu_inspect || id == R.id.dir_menu_inspect) {
+ mActionModeController.finishActionMode();
+ assert selection.size() <= 1;
+ DocumentInfo doc = selection.isEmpty()
+ ? mActivity.getCurrentDirectory()
+ : mModel.getDocuments(selection).get(0);
- case R.id.action_menu_move_to:
- if (mModel.hasDocuments(selection, DocumentFilters.NOT_MOVABLE)) {
- mInjector.dialogs.showOperationUnsupported();
- return true;
- }
- // Exit selection mode first, so we avoid deselecting deleted documents.
- mActionModeController.finishActionMode();
- transferDocuments(selection, null, FileOperationService.OPERATION_MOVE);
- return true;
-
- case R.id.action_menu_inspect:
- case R.id.dir_menu_inspect:
- mActionModeController.finishActionMode();
- assert selection.size() <= 1;
- DocumentInfo doc = selection.isEmpty()
- ? mActivity.getCurrentDirectory()
- : mModel.getDocuments(selection).get(0);
-
- mActions.showInspector(doc);
- return true;
-
- case R.id.dir_menu_cut_to_clipboard:
- mActions.cutToClipboard();
- return true;
-
- case R.id.dir_menu_copy_to_clipboard:
- mActions.copyToClipboard();
- return true;
-
- case R.id.dir_menu_paste_from_clipboard:
- pasteFromClipboard();
- return true;
-
- case R.id.dir_menu_paste_into_folder:
- pasteIntoFolder();
- return true;
-
- case R.id.action_menu_select_all:
- case R.id.dir_menu_select_all:
- mActions.selectAllFiles();
- return true;
-
- case R.id.action_menu_deselect_all:
- case R.id.dir_menu_deselect_all:
- mActions.deselectAllFiles();
- return true;
-
- case R.id.action_menu_rename:
- case R.id.dir_menu_rename:
- renameDocuments(selection);
- return true;
-
- case R.id.dir_menu_create_dir:
- mActions.showCreateDirectoryDialog();
- return true;
-
- case R.id.dir_menu_view_in_owner:
- mActions.viewInOwner();
- return true;
-
- case R.id.action_menu_sort:
- mActions.showSortDialog();
- return true;
-
- default:
- if (DEBUG) {
- Log.d(TAG, "Unhandled menu item selected: " + item);
- }
- return false;
+ mActions.showInspector(doc);
+ return true;
+ } else if (id == R.id.dir_menu_cut_to_clipboard) {
+ mActions.cutToClipboard();
+ return true;
+ } else if (id == R.id.dir_menu_copy_to_clipboard) {
+ mActions.copyToClipboard();
+ return true;
+ } else if (id == R.id.dir_menu_paste_from_clipboard) {
+ pasteFromClipboard();
+ return true;
+ } else if (id == R.id.dir_menu_paste_into_folder) {
+ pasteIntoFolder();
+ return true;
+ } else if (id == R.id.action_menu_select_all || id == R.id.dir_menu_select_all) {
+ mActions.selectAllFiles();
+ return true;
+ } else if (id == R.id.action_menu_deselect_all || id == R.id.dir_menu_deselect_all) {
+ mActions.deselectAllFiles();
+ return true;
+ } else if (id == R.id.action_menu_rename || id == R.id.dir_menu_rename) {
+ renameDocuments(selection);
+ return true;
+ } else if (id == R.id.dir_menu_create_dir) {
+ mActions.showCreateDirectoryDialog();
+ return true;
+ } else if (id == R.id.dir_menu_view_in_owner) {
+ mActions.viewInOwner();
+ return true;
+ } else if (id == R.id.action_menu_sort) {
+ mActions.showSortDialog();
+ return true;
}
+ if (DEBUG) {
+ Log.d(TAG, "Unhandled menu item selected: " + item);
+ }
+ return false;
}
private boolean onAccessibilityClick(View child) {
diff --git a/src/com/android/documentsui/files/FilesActivity.java b/src/com/android/documentsui/files/FilesActivity.java
index 7c09811..c426a70 100644
--- a/src/com/android/documentsui/files/FilesActivity.java
+++ b/src/com/android/documentsui/files/FilesActivity.java
@@ -303,25 +303,20 @@
@Override
public boolean onOptionsItemSelected(MenuItem item) {
DirectoryFragment dir;
- switch (item.getItemId()) {
- case R.id.option_menu_create_dir:
- assert(canCreateDirectory());
- mInjector.actions.showCreateDirectoryDialog();
- break;
- case R.id.option_menu_new_window:
- mInjector.actions.openInNewWindow(mState.stack);
- break;
- case R.id.option_menu_settings:
- mInjector.actions.openSettings(getCurrentRoot());
- break;
- case R.id.option_menu_select_all:
- mInjector.actions.selectAllFiles();
- break;
- case R.id.option_menu_inspect:
- mInjector.actions.showInspector(getCurrentDirectory());
- break;
- default:
- return super.onOptionsItemSelected(item);
+ final int id = item.getItemId();
+ if (id == R.id.option_menu_create_dir) {
+ assert (canCreateDirectory());
+ mInjector.actions.showCreateDirectoryDialog();
+ } else if (id == R.id.option_menu_new_window) {
+ mInjector.actions.openInNewWindow(mState.stack);
+ } else if (id == R.id.option_menu_settings) {
+ mInjector.actions.openSettings(getCurrentRoot());
+ } else if (id == R.id.option_menu_select_all) {
+ mInjector.actions.selectAllFiles();
+ } else if (id == R.id.option_menu_inspect) {
+ mInjector.actions.showInspector(getCurrentDirectory());
+ } else {
+ return super.onOptionsItemSelected(item);
}
return true;
}
diff --git a/src/com/android/documentsui/picker/ActionHandler.java b/src/com/android/documentsui/picker/ActionHandler.java
index cc349a8..1fc35b3 100644
--- a/src/com/android/documentsui/picker/ActionHandler.java
+++ b/src/com/android/documentsui/picker/ActionHandler.java
@@ -16,6 +16,9 @@
package com.android.documentsui.picker;
+import static android.provider.DocumentsContract.isDocumentUri;
+import static android.provider.DocumentsContract.isRootUri;
+
import static com.android.documentsui.base.SharedMinimal.DEBUG;
import static com.android.documentsui.base.State.ACTION_CREATE;
import static com.android.documentsui.base.State.ACTION_GET_CONTENT;
@@ -23,6 +26,8 @@
import static com.android.documentsui.base.State.ACTION_OPEN_TREE;
import static com.android.documentsui.base.State.ACTION_PICK_COPY_DESTINATION;
+import static java.util.regex.Pattern.CASE_INSENSITIVE;
+
import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.ComponentName;
@@ -35,6 +40,8 @@
import android.provider.Settings;
import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
@@ -52,6 +59,7 @@
import com.android.documentsui.base.DocumentStack;
import com.android.documentsui.base.Features;
import com.android.documentsui.base.Lookup;
+import com.android.documentsui.base.Providers;
import com.android.documentsui.base.RootInfo;
import com.android.documentsui.base.Shared;
import com.android.documentsui.base.State;
@@ -61,14 +69,12 @@
import com.android.documentsui.queries.SearchViewManager;
import com.android.documentsui.roots.ProvidersAccess;
import com.android.documentsui.services.FileOperationService;
+import com.android.documentsui.util.FileUtils;
-import com.android.documentsui.util.VersionUtils;
+import java.io.IOException;
import java.util.Arrays;
-import java.util.Locale;
import java.util.concurrent.Executor;
-
import java.util.regex.Pattern;
-import javax.annotation.Nullable;
/**
* Provides {@link PickActivity} action specializations to fragments.
@@ -77,12 +83,24 @@
private static final String TAG = "PickerActionHandler";
+ /**
+ * Used to prevent applications from using {@link Intent.ACTION_OPEN_DOCUMENT_TREE} and
+ * the {@link Intent.ACTION_OPEN_DOCUMENT} actions to request that the user select individual
+ * files from "/Android/data", "/Android/obb", "/Android/sandbox" directories and all their
+ * subdirectories (on the external storage), in accordance with the SAF privacy restrictions
+ * introduced in Android 11 (R).
+ *
+ * <p>
+ * See <a href="https://developer.android.com/about/versions/11/privacy/storage#file-access">
+ * Storage updates in Android 11</a>.
+ */
+ private static final Pattern PATTERN_RESTRICTED_INITIAL_PATH =
+ Pattern.compile("^/Android/(?:data|obb|sandbox).*", CASE_INSENSITIVE);
+
private final Features mFeatures;
private final ActivityConfig mConfig;
private final LastAccessedStorage mLastAccessed;
private final UserIdManager mUserIdManager;
- private final static Pattern PATTERN_BLOCK_PATH = Pattern.compile(
- ".*:android\\/(?:data|obb|sandbox)$");
private UpdatePickResultTask mUpdatePickResultTask;
@@ -160,25 +178,84 @@
}
private boolean launchToInitialUri(Intent intent) {
- Uri uri = intent.getParcelableExtra(DocumentsContract.EXTRA_INITIAL_URI);
- if (uri != null) {
- // In android S and above if path contains Android/data, Android/obb
- // or Android/sandbox redirect to the root for which
- // FLAG_DIR_BLOCKS_OPEN_DOCUMENT_TREE is already set
- if(Shared.shouldRestrictStorageAccessFramework(mActivity)
- && (PATTERN_BLOCK_PATH.matcher(uri.getPath().toLowerCase(Locale.ROOT)).matches())){
- loadDeviceRoot();
- return true;
- }
- if (DocumentsContract.isRootUri(mActivity, uri)) {
- loadRoot(uri, UserId.DEFAULT_USER);
- return true;
- } else if (DocumentsContract.isDocumentUri(mActivity, uri)) {
- return launchToDocument(uri);
- }
+ final Uri initialUri = intent.getParcelableExtra(DocumentsContract.EXTRA_INITIAL_URI);
+ if (initialUri == null) {
+ return false;
}
- return false;
+ final boolean isRoot = isRootUri(mActivity, initialUri);
+ final boolean isDocument = !isRoot && isDocumentUri(mActivity, initialUri);
+
+ if (!isRoot && !isDocument) {
+ // Neither a root nor a document.
+ return false;
+ }
+
+ if (isRoot) {
+ loadRoot(initialUri, UserId.DEFAULT_USER);
+ return true;
+ }
+ // From here onwards: isDoc == true.
+
+ if (shouldPreemptivelyRestrictRequestedInitialUri(initialUri)) {
+ Log.w(TAG, "Requested initial URI - " + initialUri + " - is restricted: "
+ + "loading device root instead.");
+ return false;
+ }
+
+ return launchToDocument(initialUri);
+ }
+
+ /**
+ * Starting with Android 11 (R, API Level 30) applications are no longer allowed to use the
+ * {@link Intent#ACTION_OPEN_DOCUMENT} and {@link Intent#ACTION_OPEN_DOCUMENT_TREE} to request
+ * that the user select individual files from "Android/data/", "Android/obb/",
+ * "Android/sandbox/" directories and all their subdirectories on "external storage".
+ * <p>
+ * See <a href="https://developer.android.com/about/versions/11/privacy/storage#file-access">
+ * Storage updates in Android 11</a>.
+ * <p>
+ * Ideally, this should be handled on the {@code ExternalStorageProvider} side, but as of
+ * Android 14 (U) FRC, {@code ExternalStorageProvider} "hides" only "Android/data/",
+ * "Android/obb/" and "Android/sandbox/" directories, but NOT their subdirectories.
+ */
+ private boolean shouldPreemptivelyRestrictRequestedInitialUri(@NonNull Uri uri) {
+ // Not restricting SAF access for the calling app.
+ if (!Shared.shouldRestrictStorageAccessFramework(mActivity)) {
+ return false;
+ }
+
+ // We only need to restrict some locations on the "external" storage.
+ if (!Providers.AUTHORITY_STORAGE.equals(uri.getAuthority())) {
+ return false;
+ }
+
+ // TODO(b/283962634): in the future this will have to be platform-version specific.
+ // For example, if the fix on the ExternalStorageProvider side makes it to the Android 15,
+ // we would change this to check if the platform version >= 15.
+ // In the upcoming Android 14 release, however, ExternalStorageProvider does NOT yet
+ // implement this logic.
+ final boolean externalProviderImplementsSafRestrictions = false;
+ if (externalProviderImplementsSafRestrictions) {
+ return false;
+ }
+
+ // External Storage Provider's docId format is "root:path/to/file"
+ // The getPathFromStorageDocId() turns that into "/path/to/file"
+ // Note the missing leading "/" in the path part of the docId, while the path returned by
+ // the getPathFromStorageDocId() start with "/".
+ final String docId = DocumentsContract.getDocumentId(uri);
+ final String filePath;
+ try {
+ filePath = FileUtils.getPathFromStorageDocId(docId);
+ } catch (IOException e) {
+ Log.w(TAG, "Could not get canonical file path from docId '" + docId + "'");
+ return true;
+ }
+
+ // Check if the app is asking for /Android/data, /Android/obb, /Android/sandbox or any of
+ // their subdirectories (on the external storage).
+ return PATTERN_RESTRICTED_INITIAL_PATH.matcher(filePath).matches();
}
private void initLoadLastAccessedStack() {
diff --git a/src/com/android/documentsui/queries/SearchFragment.java b/src/com/android/documentsui/queries/SearchFragment.java
index 92cd91a..890d2b2 100644
--- a/src/com/android/documentsui/queries/SearchFragment.java
+++ b/src/com/android/documentsui/queries/SearchFragment.java
@@ -163,9 +163,9 @@
updateDirectoryVisibility(View.VISIBLE);
- FragmentTransaction ft = getParentFragmentManager().beginTransaction();
- ft.remove(this);
- ft.commitNow();
+ getParentFragmentManager().beginTransaction()
+ .remove(this)
+ .commitAllowingStateLoss();
}
private void updateDirectoryVisibility(int visibility) {
diff --git a/src/com/android/documentsui/sidebar/RootsFragment.java b/src/com/android/documentsui/sidebar/RootsFragment.java
index 5bf3f1a..49710e1 100644
--- a/src/com/android/documentsui/sidebar/RootsFragment.java
+++ b/src/com/android/documentsui/sidebar/RootsFragment.java
@@ -611,26 +611,25 @@
return false;
}
final RootItem rootItem = (RootItem) mAdapter.getItem(adapterMenuInfo.position);
- switch (item.getItemId()) {
- case R.id.root_menu_eject_root:
- final View ejectIcon = adapterMenuInfo.targetView.findViewById(R.id.action_icon);
- ejectClicked(ejectIcon, rootItem.root, mActionHandler);
- return true;
- case R.id.root_menu_open_in_new_window:
- mActionHandler.openInNewWindow(new DocumentStack(rootItem.root));
- return true;
- case R.id.root_menu_paste_into_folder:
- mActionHandler.pasteIntoFolder(rootItem.root);
- return true;
- case R.id.root_menu_settings:
- mActionHandler.openSettings(rootItem.root);
- return true;
- default:
- if (DEBUG) {
- Log.d(TAG, "Unhandled menu item selected: " + item);
- }
- return false;
+ final int id = item.getItemId();
+ if (id == R.id.root_menu_eject_root) {
+ final View ejectIcon = adapterMenuInfo.targetView.findViewById(R.id.action_icon);
+ ejectClicked(ejectIcon, rootItem.root, mActionHandler);
+ return true;
+ } else if (id == R.id.root_menu_open_in_new_window) {
+ mActionHandler.openInNewWindow(new DocumentStack(rootItem.root));
+ return true;
+ } else if (id == R.id.root_menu_paste_into_folder) {
+ mActionHandler.pasteIntoFolder(rootItem.root);
+ return true;
+ } else if (id == R.id.root_menu_settings) {
+ mActionHandler.openSettings(rootItem.root);
+ return true;
}
+ if (DEBUG) {
+ Log.d(TAG, "Unhandled menu item selected: " + item);
+ }
+ return false;
}
private void getRootDocument(RootItem rootItem, RootUpdater updater) {
diff --git a/src/com/android/documentsui/sorting/SortController.java b/src/com/android/documentsui/sorting/SortController.java
index ccfc3f1..4fc2844 100644
--- a/src/com/android/documentsui/sorting/SortController.java
+++ b/src/com/android/documentsui/sorting/SortController.java
@@ -16,11 +16,11 @@
package com.android.documentsui.sorting;
+import android.view.View;
+
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
-import android.view.View;
-
import com.android.documentsui.BaseActivity;
import com.android.documentsui.Injector;
import com.android.documentsui.MetricConsts;
@@ -67,19 +67,15 @@
final Injector<?> injector = ((BaseActivity)activity).getInjector();
sortModel.setMetricRecorder((SortDimension dimension) -> {
int sortType = MetricConsts.USER_ACTION_UNKNOWN;
- switch (dimension.getId()) {
- case SortModel.SORT_DIMENSION_ID_TITLE:
- sortType = MetricConsts.USER_ACTION_SORT_NAME;
- break;
- case SortModel.SORT_DIMENSION_ID_SIZE:
- sortType = MetricConsts.USER_ACTION_SORT_SIZE;
- break;
- case SortModel.SORT_DIMENSION_ID_DATE:
- sortType = MetricConsts.USER_ACTION_SORT_DATE;
- break;
- case SortModel.SORT_DIMENSION_ID_FILE_TYPE:
- sortType = MetricConsts.USER_ACTION_SORT_TYPE;
- break;
+ final int id = dimension.getId();
+ if (id == SortModel.SORT_DIMENSION_ID_TITLE) {
+ sortType = MetricConsts.USER_ACTION_SORT_NAME;
+ } else if (id == SortModel.SORT_DIMENSION_ID_SIZE) {
+ sortType = MetricConsts.USER_ACTION_SORT_SIZE;
+ } else if (id == SortModel.SORT_DIMENSION_ID_DATE) {
+ sortType = MetricConsts.USER_ACTION_SORT_DATE;
+ } else if (id == SortModel.SORT_DIMENSION_ID_FILE_TYPE) {
+ sortType = MetricConsts.USER_ACTION_SORT_TYPE;
}
Metrics.logUserAction(sortType);
diff --git a/src/com/android/documentsui/sorting/SortListFragment.java b/src/com/android/documentsui/sorting/SortListFragment.java
index 7c33c9d..8d4032c 100644
--- a/src/com/android/documentsui/sorting/SortListFragment.java
+++ b/src/com/android/documentsui/sorting/SortListFragment.java
@@ -20,7 +20,6 @@
import com.android.documentsui.R;
import com.android.documentsui.sorting.SortDimension.SortDirection;
-import com.android.documentsui.sorting.SortModel.SortDimensionId;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.bottomsheet.BottomSheetDialog;
@@ -66,18 +65,15 @@
for (int i = 0; i < mModel.getSize(); ++i) {
SortDimension dimension = mModel.getDimensionAt(i);
if (dimension.getSortCapability() != SortDimension.SORT_CAPABILITY_NONE) {
- switch (dimension.getId()) {
- case SortModel.SORT_DIMENSION_ID_TITLE:
- case SortModel.SORT_DIMENSION_ID_FILE_TYPE:
- addBothDirectionDimension(dimension, true);
- break;
- case SortModel.SORT_DIMENSION_ID_DATE:
- case SortModel.SORT_DIMENSION_ID_SIZE:
- addBothDirectionDimension(dimension, false);
- break;
- default:
- mSortingList.add(new SortItem(dimension));
- break;
+ final int id = dimension.getId();
+ if (id == SortModel.SORT_DIMENSION_ID_TITLE
+ || id == SortModel.SORT_DIMENSION_ID_FILE_TYPE) {
+ addBothDirectionDimension(dimension, true);
+ } else if (id == SortModel.SORT_DIMENSION_ID_DATE
+ || id == SortModel.SORT_DIMENSION_ID_SIZE) {
+ addBothDirectionDimension(dimension, false);
+ } else {
+ mSortingList.add(new SortItem(dimension));
}
}
}
@@ -96,22 +92,21 @@
public static @StringRes int getSheetLabelId(SortDimension dimension, @SortDirection int direction) {
boolean isAscending = direction == SortDimension.SORT_DIRECTION_ASCENDING;
- switch (dimension.getId()) {
- case SortModel.SORT_DIMENSION_ID_TITLE:
- return isAscending ? R.string.sort_dimension_name_ascending :
- R.string.sort_dimension_name_descending;
- case SortModel.SORT_DIMENSION_ID_DATE:
- return isAscending ? R.string.sort_dimension_date_ascending :
- R.string.sort_dimension_date_descending;
- case SortModel.SORT_DIMENSION_ID_FILE_TYPE:
- return isAscending ? R.string.sort_dimension_file_type_ascending :
- R.string.sort_dimension_file_type_descending;
- case SortModel.SORT_DIMENSION_ID_SIZE:
- return isAscending ? R.string.sort_dimension_size_ascending :
- R.string.sort_dimension_size_descending;
- default:
- return dimension.getLabelId();
+ final int id = dimension.getId();
+ if (id == SortModel.SORT_DIMENSION_ID_TITLE) {
+ return isAscending ? R.string.sort_dimension_name_ascending :
+ R.string.sort_dimension_name_descending;
+ } else if (id == SortModel.SORT_DIMENSION_ID_DATE) {
+ return isAscending ? R.string.sort_dimension_date_ascending :
+ R.string.sort_dimension_date_descending;
+ } else if (id == SortModel.SORT_DIMENSION_ID_FILE_TYPE) {
+ return isAscending ? R.string.sort_dimension_file_type_ascending :
+ R.string.sort_dimension_file_type_descending;
+ } else if (id == SortModel.SORT_DIMENSION_ID_SIZE) {
+ return isAscending ? R.string.sort_dimension_size_ascending :
+ R.string.sort_dimension_size_descending;
}
+ return dimension.getLabelId();
}
@Override
@@ -170,7 +165,7 @@
private static class SortItem {
- @SortDimensionId final int id;
+ final int id;
@SortDirection final int direction;
@StringRes final int labelId;
@@ -180,7 +175,7 @@
labelId = dimension.getLabelId();
}
- SortItem(@SortDimensionId int id, @SortDirection int direction, @StringRes int labelId) {
+ SortItem(int id, @SortDirection int direction, @StringRes int labelId) {
this.id = id;
this.direction = direction;
this.labelId = labelId;
diff --git a/src/com/android/documentsui/sorting/SortModel.java b/src/com/android/documentsui/sorting/SortModel.java
index 4e544a9..9bf5b96 100644
--- a/src/com/android/documentsui/sorting/SortModel.java
+++ b/src/com/android/documentsui/sorting/SortModel.java
@@ -47,16 +47,6 @@
* Sort model that contains all columns and their sorting state.
*/
public class SortModel implements Parcelable {
- @IntDef({
- SORT_DIMENSION_ID_UNKNOWN,
- SORT_DIMENSION_ID_TITLE,
- SORT_DIMENSION_ID_SUMMARY,
- SORT_DIMENSION_ID_SIZE,
- SORT_DIMENSION_ID_FILE_TYPE,
- SORT_DIMENSION_ID_DATE
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface SortDimensionId {}
public static final int SORT_DIMENSION_ID_UNKNOWN = 0;
public static final int SORT_DIMENSION_ID_TITLE = android.R.id.title;
public static final int SORT_DIMENSION_ID_SUMMARY = android.R.id.summary;
@@ -239,32 +229,28 @@
// should only be called when R.bool.feature_content_paging is true
final int id = getSortedDimensionId();
- switch (id) {
- case SORT_DIMENSION_ID_UNKNOWN:
- return;
- case SortModel.SORT_DIMENSION_ID_TITLE:
- queryArgs.putStringArray(
- ContentResolver.QUERY_ARG_SORT_COLUMNS,
- new String[]{ Document.COLUMN_DISPLAY_NAME });
- break;
- case SortModel.SORT_DIMENSION_ID_DATE:
- queryArgs.putStringArray(
- ContentResolver.QUERY_ARG_SORT_COLUMNS,
- new String[]{ Document.COLUMN_LAST_MODIFIED });
- break;
- case SortModel.SORT_DIMENSION_ID_SIZE:
- queryArgs.putStringArray(
- ContentResolver.QUERY_ARG_SORT_COLUMNS,
- new String[]{ Document.COLUMN_SIZE });
- break;
- case SortModel.SORT_DIMENSION_ID_FILE_TYPE:
- // Unfortunately sorting by mime type is pretty much guaranteed different from
- // sorting by user-friendly type, so there is no point to guide the provider to sort
- // in a particular order.
- return;
- default:
- throw new IllegalStateException(
- "Unexpected sort dimension id: " + id);
+ if (id == SORT_DIMENSION_ID_UNKNOWN) {
+ return;
+ } else if (id == SortModel.SORT_DIMENSION_ID_TITLE) {
+ queryArgs.putStringArray(
+ ContentResolver.QUERY_ARG_SORT_COLUMNS,
+ new String[]{Document.COLUMN_DISPLAY_NAME});
+ } else if (id == SortModel.SORT_DIMENSION_ID_DATE) {
+ queryArgs.putStringArray(
+ ContentResolver.QUERY_ARG_SORT_COLUMNS,
+ new String[]{Document.COLUMN_LAST_MODIFIED});
+ } else if (id == SortModel.SORT_DIMENSION_ID_SIZE) {
+ queryArgs.putStringArray(
+ ContentResolver.QUERY_ARG_SORT_COLUMNS,
+ new String[]{Document.COLUMN_SIZE});
+ } else if (id == SortModel.SORT_DIMENSION_ID_FILE_TYPE) {
+ // Unfortunately sorting by mime type is pretty much guaranteed different from
+ // sorting by user-friendly type, so there is no point to guide the provider to sort
+ // in a particular order.
+ return;
+ } else {
+ throw new IllegalStateException(
+ "Unexpected sort dimension id: " + id);
}
final SortDimension dimension = getDimensionById(id);
@@ -296,26 +282,22 @@
final int id = getSortedDimensionId();
final String columnName;
- switch (id) {
- case SORT_DIMENSION_ID_UNKNOWN:
- return null;
- case SortModel.SORT_DIMENSION_ID_TITLE:
- columnName = Document.COLUMN_DISPLAY_NAME;
- break;
- case SortModel.SORT_DIMENSION_ID_DATE:
- columnName = Document.COLUMN_LAST_MODIFIED;
- break;
- case SortModel.SORT_DIMENSION_ID_SIZE:
- columnName = Document.COLUMN_SIZE;
- break;
- case SortModel.SORT_DIMENSION_ID_FILE_TYPE:
- // Unfortunately sorting by mime type is pretty much guaranteed different from
- // sorting by user-friendly type, so there is no point to guide the provider to sort
- // in a particular order.
- return null;
- default:
- throw new IllegalStateException(
- "Unexpected sort dimension id: " + id);
+ if (id == SORT_DIMENSION_ID_UNKNOWN) {
+ return null;
+ } else if (id == SortModel.SORT_DIMENSION_ID_TITLE) {
+ columnName = Document.COLUMN_DISPLAY_NAME;
+ } else if (id == SortModel.SORT_DIMENSION_ID_DATE) {
+ columnName = Document.COLUMN_LAST_MODIFIED;
+ } else if (id == SortModel.SORT_DIMENSION_ID_SIZE) {
+ columnName = Document.COLUMN_SIZE;
+ } else if (id == SortModel.SORT_DIMENSION_ID_FILE_TYPE) {
+ // Unfortunately sorting by mime type is pretty much guaranteed different from
+ // sorting by user-friendly type, so there is no point to guide the provider to sort
+ // in a particular order.
+ return null;
+ } else {
+ throw new IllegalStateException(
+ "Unexpected sort dimension id: " + id);
}
final SortDimension dimension = getDimensionById(id);
diff --git a/src/com/android/documentsui/sorting/SortingCursorWrapper.java b/src/com/android/documentsui/sorting/SortingCursorWrapper.java
index d8c686c..1e359a3 100644
--- a/src/com/android/documentsui/sorting/SortingCursorWrapper.java
+++ b/src/com/android/documentsui/sorting/SortingCursorWrapper.java
@@ -27,7 +27,6 @@
import com.android.documentsui.base.Lookup;
import com.android.documentsui.base.Shared;
-import com.android.documentsui.sorting.SortModel.SortDimensionId;
/**
* Cursor wrapper that presents a sorted view of the underlying cursor. Handles
@@ -49,16 +48,13 @@
long[] longValues = null;
String[] ids = new String[count];
- final @SortDimensionId int id = dimension.getId();
- switch (id) {
- case SortModel.SORT_DIMENSION_ID_TITLE:
- case SortModel.SORT_DIMENSION_ID_FILE_TYPE:
- stringValues = new String[count];
- break;
- case SortModel.SORT_DIMENSION_ID_DATE:
- case SortModel.SORT_DIMENSION_ID_SIZE:
- longValues = new long[count];
- break;
+ final int id = dimension.getId();
+ if (id == SortModel.SORT_DIMENSION_ID_TITLE
+ || id == SortModel.SORT_DIMENSION_ID_FILE_TYPE) {
+ stringValues = new String[count];
+ } else if (id == SortModel.SORT_DIMENSION_ID_DATE
+ || id == SortModel.SORT_DIMENSION_ID_SIZE) {
+ longValues = new long[count];
}
cursor.moveToPosition(-1);
@@ -70,34 +66,26 @@
isDirs[i] = Document.MIME_TYPE_DIR.equals(mimeType);
ids[i] = getCursorString(mCursor, Document.COLUMN_DOCUMENT_ID);
- switch(id) {
- case SortModel.SORT_DIMENSION_ID_TITLE:
- final String displayName = getCursorString(
- mCursor, Document.COLUMN_DISPLAY_NAME);
- stringValues[i] = displayName;
- break;
- case SortModel.SORT_DIMENSION_ID_FILE_TYPE:
- stringValues[i] = fileTypeLookup.lookup(mimeType);
- break;
- case SortModel.SORT_DIMENSION_ID_DATE:
- longValues[i] = getLastModified(mCursor);
- break;
- case SortModel.SORT_DIMENSION_ID_SIZE:
- longValues[i] = getCursorLong(mCursor, Document.COLUMN_SIZE);
- break;
+ if (id == SortModel.SORT_DIMENSION_ID_TITLE) {
+ final String displayName = getCursorString(
+ mCursor, Document.COLUMN_DISPLAY_NAME);
+ stringValues[i] = displayName;
+ } else if (id == SortModel.SORT_DIMENSION_ID_FILE_TYPE) {
+ stringValues[i] = fileTypeLookup.lookup(mimeType);
+ } else if (id == SortModel.SORT_DIMENSION_ID_DATE) {
+ longValues[i] = getLastModified(mCursor);
+ } else if (id == SortModel.SORT_DIMENSION_ID_SIZE) {
+ longValues[i] = getCursorLong(mCursor, Document.COLUMN_SIZE);
}
}
- switch (id) {
- case SortModel.SORT_DIMENSION_ID_TITLE:
- case SortModel.SORT_DIMENSION_ID_FILE_TYPE:
- binarySort(stringValues, isDirs, mPosition, ids, dimension.getSortDirection());
- break;
- case SortModel.SORT_DIMENSION_ID_DATE:
- case SortModel.SORT_DIMENSION_ID_SIZE:
- binarySort(longValues, isDirs, mPosition, ids, dimension.getSortDirection());
- break;
+ if (id == SortModel.SORT_DIMENSION_ID_TITLE
+ || id == SortModel.SORT_DIMENSION_ID_FILE_TYPE) {
+ binarySort(stringValues, isDirs, mPosition, ids, dimension.getSortDirection());
+ } else if (id == SortModel.SORT_DIMENSION_ID_DATE
+ || id == SortModel.SORT_DIMENSION_ID_SIZE) {
+ binarySort(longValues, isDirs, mPosition, ids, dimension.getSortDirection());
}
}
diff --git a/src/com/android/documentsui/sorting/TableHeaderController.java b/src/com/android/documentsui/sorting/TableHeaderController.java
index f3853c0..549478c 100644
--- a/src/com/android/documentsui/sorting/TableHeaderController.java
+++ b/src/com/android/documentsui/sorting/TableHeaderController.java
@@ -19,7 +19,6 @@
import android.view.View;
import com.android.documentsui.R;
-import com.android.documentsui.sorting.SortModel.SortDimensionId;
import javax.annotation.Nullable;
@@ -78,7 +77,7 @@
mModel.removeListener(mModelListener);
}
- private void bindCell(HeaderCell cell, @SortDimensionId int id) {
+ private void bindCell(HeaderCell cell, int id) {
assert(cell != null);
SortDimension dimension = mModel.getDimensionById(id);
diff --git a/src/com/android/documentsui/util/FileUtils.java b/src/com/android/documentsui/util/FileUtils.java
new file mode 100644
index 0000000..40f7fba
--- /dev/null
+++ b/src/com/android/documentsui/util/FileUtils.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.documentsui.util;
+
+import androidx.annotation.NonNull;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Objects;
+
+public class FileUtils {
+
+ /**
+ * Returns the canonical pathname string of the provided abstract pathname.
+ *
+ * @return The canonical pathname string denoting the same file or directory as this abstract
+ * pathname.
+ * @see File#getCanonicalPath()
+ */
+ @NonNull
+ public static String getCanonicalPath(@NonNull String path) throws IOException {
+ Objects.requireNonNull(path);
+ return new File(path).getCanonicalPath();
+ }
+
+ /**
+ * This is basically a very slightly tweaked fork of
+ * {@link com.android.externalstorage.ExternalStorageProvider#getPathFromDocId(String)}.
+ * The difference between this fork and the "original" method is that here we do not strip
+ * the leading and trailing "/"s (because we don't worry about those).
+ *
+ * @return canonicalized file path.
+ */
+ public static String getPathFromStorageDocId(String docId) throws IOException {
+ // Remove the root tag from the docId, e.g. "primary:", which should leave with the file
+ // path.
+ final String docIdPath = docId.substring(docId.indexOf(':', 1) + 1);
+
+ return getCanonicalPath(docIdPath);
+ }
+
+ private FileUtils() {
+ }
+}
diff --git a/tests/Android.bp b/tests/Android.bp
index 7a5d631..65a4bbd 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -95,7 +95,7 @@
"androidx.test.ext.truth",
"guava",
"mockito-target",
- "ub-uiautomator",
+ "androidx.test.uiautomator_uiautomator",
],
jarjar_rules: "jarjar-rules.txt",
diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml
index 7874118..c5a00a2 100644
--- a/tests/AndroidTest.xml
+++ b/tests/AndroidTest.xml
@@ -18,6 +18,14 @@
<option name="test-file-name" value="DocumentsUITests.apk" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <!-- Disable keyguard -->
+ <!-- Though keyguard is disabled globally in cts-preconditions.xml, this will ensure that
+ the test gets the same treatment when running in other test suites (e.g. MTS), as well
+ as when running locally (e.g. via atest) -->
+ <option name="run-command" value="locksettings set-disabled true" />
+ </target_preparer>
+
<option name="test-suite-tag" value="apct" />
<option name="test-tag" value="DocumentsUITests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/tests/common/com/android/documentsui/TestActivity.java b/tests/common/com/android/documentsui/TestActivity.java
index 2506bdf..eb321fa 100644
--- a/tests/common/com/android/documentsui/TestActivity.java
+++ b/tests/common/com/android/documentsui/TestActivity.java
@@ -263,6 +263,17 @@
public final void finish() {
finishedHandler.accept(null);
}
+
+ @Override
+ public boolean isInMultiWindowMode() {
+ // We are seeing this causing NPEs on older platform versions of some OEM, e.g. b/297710004.
+ // Hence we'll wrap this in a try-catch.
+ try {
+ return super.isInMultiWindowMode();
+ } catch (Exception e) {
+ return false;
+ }
+ }
}
// Trick Mockito into finding our Addons methods correctly. W/o this
diff --git a/tests/common/com/android/documentsui/bots/Bots.java b/tests/common/com/android/documentsui/bots/Bots.java
index 8a3d347..8cc00ac 100644
--- a/tests/common/com/android/documentsui/bots/Bots.java
+++ b/tests/common/com/android/documentsui/bots/Bots.java
@@ -20,15 +20,15 @@
import android.app.UiAutomation;
import android.content.Context;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.BySelector;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.UiSelector;
-import android.support.test.uiautomator.Until;
import androidx.test.InstrumentationRegistry;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.UiSelector;
+import androidx.test.uiautomator.Until;
/**
* Handy collection of bots for working with Files app.
diff --git a/tests/common/com/android/documentsui/bots/BreadBot.java b/tests/common/com/android/documentsui/bots/BreadBot.java
index d022f52..7c1a81b 100644
--- a/tests/common/com/android/documentsui/bots/BreadBot.java
+++ b/tests/common/com/android/documentsui/bots/BreadBot.java
@@ -17,9 +17,10 @@
package com.android.documentsui.bots;
import android.content.Context;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject2;
+
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
import junit.framework.Assert;
diff --git a/tests/common/com/android/documentsui/bots/DirectoryListBot.java b/tests/common/com/android/documentsui/bots/DirectoryListBot.java
index 9fbc39f..3e9fa30 100644
--- a/tests/common/com/android/documentsui/bots/DirectoryListBot.java
+++ b/tests/common/com/android/documentsui/bots/DirectoryListBot.java
@@ -27,21 +27,22 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.os.SystemClock;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.BySelector;
-import android.support.test.uiautomator.Configurator;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.UiObjectNotFoundException;
-import android.support.test.uiautomator.UiScrollable;
-import android.support.test.uiautomator.UiSelector;
-import android.support.test.uiautomator.Until;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.Configurator;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.UiObjectNotFoundException;
+import androidx.test.uiautomator.UiScrollable;
+import androidx.test.uiautomator.UiSelector;
+import androidx.test.uiautomator.Until;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
diff --git a/tests/common/com/android/documentsui/bots/GestureBot.java b/tests/common/com/android/documentsui/bots/GestureBot.java
index 9a0fb6e..4c7f7c7 100644
--- a/tests/common/com/android/documentsui/bots/GestureBot.java
+++ b/tests/common/com/android/documentsui/bots/GestureBot.java
@@ -21,16 +21,17 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.os.SystemClock;
-import android.support.test.uiautomator.Configurator;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiObjectNotFoundException;
-import android.support.test.uiautomator.UiSelector;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
+import androidx.test.uiautomator.Configurator;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject;
+import androidx.test.uiautomator.UiObjectNotFoundException;
+import androidx.test.uiautomator.UiSelector;
+
/**
* A test helper class that provides support for controlling directory list
* and making assertions against the state of it.
diff --git a/tests/common/com/android/documentsui/bots/InspectorBot.java b/tests/common/com/android/documentsui/bots/InspectorBot.java
index 252fde4..7e459db 100644
--- a/tests/common/com/android/documentsui/bots/InspectorBot.java
+++ b/tests/common/com/android/documentsui/bots/InspectorBot.java
@@ -20,12 +20,13 @@
import android.app.Activity;
import android.content.Context;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiSelector;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiSelector;
+
import com.android.documentsui.R;
import com.android.documentsui.inspector.DetailsView;
import com.android.documentsui.inspector.KeyValueRow;
diff --git a/tests/common/com/android/documentsui/bots/KeyboardBot.java b/tests/common/com/android/documentsui/bots/KeyboardBot.java
index 6167acf..7cd3eed 100644
--- a/tests/common/com/android/documentsui/bots/KeyboardBot.java
+++ b/tests/common/com/android/documentsui/bots/KeyboardBot.java
@@ -21,10 +21,11 @@
import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom;
import android.content.Context;
-import android.support.test.uiautomator.UiDevice;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
+import androidx.test.uiautomator.UiDevice;
+
/**
* A test helper class that provides support for keyboard manipulation.
*/
diff --git a/tests/common/com/android/documentsui/bots/MenuBot.java b/tests/common/com/android/documentsui/bots/MenuBot.java
index 511c51e..58e82e4 100644
--- a/tests/common/com/android/documentsui/bots/MenuBot.java
+++ b/tests/common/com/android/documentsui/bots/MenuBot.java
@@ -16,13 +16,14 @@
package com.android.documentsui.bots;
-import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
import android.content.Context;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObjectNotFoundException;
+
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObjectNotFoundException;
import java.util.Map;
diff --git a/tests/common/com/android/documentsui/bots/NotificationsBot.java b/tests/common/com/android/documentsui/bots/NotificationsBot.java
index 5ab872d..5200ddb 100644
--- a/tests/common/com/android/documentsui/bots/NotificationsBot.java
+++ b/tests/common/com/android/documentsui/bots/NotificationsBot.java
@@ -19,9 +19,9 @@
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
-import android.support.test.uiautomator.UiDevice;
import androidx.test.InstrumentationRegistry;
+import androidx.test.uiautomator.UiDevice;
import com.android.documentsui.services.TestNotificationService;
diff --git a/tests/common/com/android/documentsui/bots/SearchBot.java b/tests/common/com/android/documentsui/bots/SearchBot.java
index d14fd13..51f9394 100644
--- a/tests/common/com/android/documentsui/bots/SearchBot.java
+++ b/tests/common/com/android/documentsui/bots/SearchBot.java
@@ -31,13 +31,13 @@
import static org.hamcrest.CoreMatchers.anyOf;
import android.content.Context;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiObjectNotFoundException;
-import android.support.test.uiautomator.UiSelector;
import android.view.View;
import androidx.recyclerview.R;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject;
+import androidx.test.uiautomator.UiObjectNotFoundException;
+import androidx.test.uiautomator.UiSelector;
import org.hamcrest.Matcher;
diff --git a/tests/common/com/android/documentsui/bots/SidebarBot.java b/tests/common/com/android/documentsui/bots/SidebarBot.java
index 4e96126..01f311a7 100644
--- a/tests/common/com/android/documentsui/bots/SidebarBot.java
+++ b/tests/common/com/android/documentsui/bots/SidebarBot.java
@@ -22,14 +22,15 @@
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import android.content.Context;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiObjectNotFoundException;
-import android.support.test.uiautomator.UiScrollable;
-import android.support.test.uiautomator.UiSelector;
import android.util.Log;
import android.view.View;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject;
+import androidx.test.uiautomator.UiObjectNotFoundException;
+import androidx.test.uiautomator.UiScrollable;
+import androidx.test.uiautomator.UiSelector;
+
import com.android.documentsui.R;
import junit.framework.Assert;
diff --git a/tests/common/com/android/documentsui/bots/SortBot.java b/tests/common/com/android/documentsui/bots/SortBot.java
index 6b6f817..0a77de4 100644
--- a/tests/common/com/android/documentsui/bots/SortBot.java
+++ b/tests/common/com/android/documentsui/bots/SortBot.java
@@ -33,19 +33,18 @@
import static org.hamcrest.Matchers.allOf;
import android.content.Context;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject2;
import android.view.View;
import androidx.annotation.StringRes;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
import com.android.documentsui.R;
import com.android.documentsui.sorting.SortDimension;
import com.android.documentsui.sorting.SortDimension.SortDirection;
import com.android.documentsui.sorting.SortListFragment;
import com.android.documentsui.sorting.SortModel;
-import com.android.documentsui.sorting.SortModel.SortDimensionId;
import org.hamcrest.Matcher;
@@ -67,7 +66,7 @@
mUiBot = uiBot;
}
- public void sortBy(@SortDimensionId int id, @SortDirection int direction) {
+ public void sortBy(int id, @SortDirection int direction) {
assert(direction != SortDimension.SORT_DIRECTION_NONE);
final @StringRes int labelId = mSortModel.getDimensionById(id).getLabelId();
@@ -102,7 +101,7 @@
assertTrue(Matchers.present(mColumnBot.MATCHER));
}
- private boolean sortByMenu(@SortDimensionId int id, @SortDirection int direction) {
+ private boolean sortByMenu(int id, @SortDirection int direction) {
assert(direction != SortDimension.SORT_DIRECTION_NONE);
clickMenuSort();
diff --git a/tests/common/com/android/documentsui/bots/UiBot.java b/tests/common/com/android/documentsui/bots/UiBot.java
index 36256ff..f30cb93 100644
--- a/tests/common/com/android/documentsui/bots/UiBot.java
+++ b/tests/common/com/android/documentsui/bots/UiBot.java
@@ -35,13 +35,6 @@
import static org.hamcrest.Matchers.endsWith;
import android.content.Context;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.UiObjectNotFoundException;
-import android.support.test.uiautomator.UiSelector;
-import android.support.test.uiautomator.Until;
import android.util.TypedValue;
import android.view.View;
@@ -51,6 +44,13 @@
import androidx.test.espresso.action.ViewActions;
import androidx.test.espresso.matcher.BoundedMatcher;
import androidx.test.espresso.matcher.ViewMatchers;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.UiObjectNotFoundException;
+import androidx.test.uiautomator.UiSelector;
+import androidx.test.uiautomator.Until;
import com.android.documentsui.R;
diff --git a/tests/functional/com/android/documentsui/ActionCreateDocumentUiTest.java b/tests/functional/com/android/documentsui/ActionCreateDocumentUiTest.java
index 1a355b9..6939e3d 100644
--- a/tests/functional/com/android/documentsui/ActionCreateDocumentUiTest.java
+++ b/tests/functional/com/android/documentsui/ActionCreateDocumentUiTest.java
@@ -16,28 +16,30 @@
package com.android.documentsui;
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static android.app.Activity.RESULT_OK;
+import static android.content.Intent.ACTION_CREATE_DOCUMENT;
+import static android.content.Intent.CATEGORY_DEFAULT;
+import static android.content.Intent.CATEGORY_OPENABLE;
+import static android.content.Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION;
+import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
+import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
import static com.android.documentsui.base.Providers.AUTHORITY_STORAGE;
import static com.google.common.truth.Truth.assertThat;
-import android.app.Activity;
import android.app.Instrumentation;
-import android.app.UiAutomation;
-import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.provider.DocumentsContract;
-import android.support.test.uiautomator.UiDevice;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
-import com.android.documentsui.bots.Bots;
import com.android.documentsui.picker.PickActivity;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -48,62 +50,54 @@
@LargeTest
@RunWith(AndroidJUnit4.class)
-public class ActionCreateDocumentUiTest {
+public class ActionCreateDocumentUiTest extends DocumentsUiTestBase {
@Rule
public final ActivityTestRule<PickActivity> mRule =
new ActivityTestRule<>(PickActivity.class, false, false);
- private Context mTargetContext;
- private Context mContext;
- private Bots mBots;
- private UiDevice mDevice;
-
@Before
- public void setup() {
- UiAutomation automation = getInstrumentation().getUiAutomation();
+ public void setup() throws Exception {
+ super.setUp();
+ }
- mDevice = UiDevice.getInstance(getInstrumentation());
- mTargetContext = getInstrumentation().getTargetContext();
- mContext = getInstrumentation().getContext();
- mBots = new Bots(mDevice, automation, mTargetContext, 5000);
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
}
@Test
public void testActionCreate_TextFile() throws Exception {
- Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
- intent.addCategory(Intent.CATEGORY_DEFAULT);
- intent.addCategory(Intent.CATEGORY_OPENABLE);
+ final Intent intent = new Intent(ACTION_CREATE_DOCUMENT);
+ intent.addCategory(CATEGORY_DEFAULT);
+ intent.addCategory(CATEGORY_OPENABLE);
intent.setType("*/*");
-
- Uri hintUri = DocumentsContract.buildRootUri(AUTHORITY_STORAGE, "primary");
- intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, hintUri);
+ intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI,
+ DocumentsContract.buildRootUri(AUTHORITY_STORAGE, "primary"));
mRule.launchActivity(intent);
- String fileName = UUID.randomUUID().toString() + ".txt";
+ final String fileName = UUID.randomUUID() + ".txt";
- mBots.main.setDialogText(fileName);
- mBots.main.clickSaveButton();
- mDevice.waitForIdle();
+ bots.main.setDialogText(fileName);
+ bots.main.clickSaveButton();
+ device.waitForIdle();
- Instrumentation.ActivityResult activityResult = mRule.getActivityResult();
+ final Instrumentation.ActivityResult activityResult = mRule.getActivityResult();
+ assertThat(activityResult.getResultCode()).isEqualTo(RESULT_OK);
- Intent result = activityResult.getResultData();
- Uri uri = result.getData();
- int flags = result.getFlags();
+ final Intent resultData = activityResult.getResultData();
+ final Uri uri = resultData.getData();
- assertThat(activityResult.getResultCode()).isEqualTo(Activity.RESULT_OK);
assertThat(uri.getAuthority()).isEqualTo(AUTHORITY_STORAGE);
assertThat(uri.getPath()).contains(fileName);
- int expectedFlags =
- Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION;
+ assertThat(resultData.getFlags()).isEqualTo(FLAG_GRANT_READ_URI_PERMISSION
+ | FLAG_GRANT_WRITE_URI_PERMISSION
+ | FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
- assertThat(flags).isEqualTo(expectedFlags);
- assertThat(DocumentsContract.deleteDocument(mContext.getContentResolver(), uri)).isTrue();
+ final boolean deletedSuccessfully =
+ DocumentsContract.deleteDocument(context.getContentResolver(), uri);
+ assertThat(deletedSuccessfully).isTrue();
}
-
}
\ No newline at end of file
diff --git a/tests/functional/com/android/documentsui/ActivityTest.java b/tests/functional/com/android/documentsui/ActivityTest.java
index ec4e6cb..bda672b 100644
--- a/tests/functional/com/android/documentsui/ActivityTest.java
+++ b/tests/functional/com/android/documentsui/ActivityTest.java
@@ -16,6 +16,8 @@
package com.android.documentsui;
+import static java.util.Objects.requireNonNull;
+
import android.app.Activity;
import android.app.UiAutomation;
import android.app.UiModeManager;
@@ -28,19 +30,22 @@
import android.os.RemoteException;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
-import android.support.test.uiautomator.Configurator;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObjectNotFoundException;
import android.test.ActivityInstrumentationTestCase2;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import androidx.test.uiautomator.Configurator;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObjectNotFoundException;
+
import com.android.documentsui.base.Features;
import com.android.documentsui.base.RootInfo;
import com.android.documentsui.base.UserId;
import com.android.documentsui.bots.Bots;
import com.android.documentsui.files.FilesActivity;
+import java.io.IOException;
+
import javax.annotation.Nullable;
/**
@@ -77,6 +82,9 @@
protected ContentProviderClient mClient;
protected UiModeManager mUiModeManager;
+ private String initialScreenOffTimeoutValue = null;
+ private String initialSleepTimeoutValue = null;
+
public ActivityTest(Class<T> activityClass) {
super(activityClass);
}
@@ -128,6 +136,9 @@
device.setOrientationNatural();
device.pressKeyCode(KeyEvent.KEYCODE_WAKEUP);
device.pressKeyCode(KeyEvent.KEYCODE_MENU);
+
+ disableScreenOffAndSleepTimeouts();
+
setupTestingRoots();
launchActivity();
@@ -147,6 +158,7 @@
public void tearDown() throws Exception {
device.unfreezeRotation();
mDocsHelper.cleanUp();
+ restoreScreenOffAndSleepTimeouts();
super.tearDown();
}
@@ -210,4 +222,27 @@
device.waitForIdle(NIGHT_MODE_CHANGE_WAIT_TIME);
}
}
+
+ private void disableScreenOffAndSleepTimeouts() throws IOException {
+ initialScreenOffTimeoutValue = device.executeShellCommand(
+ "settings get system screen_off_timeout");
+ initialSleepTimeoutValue = device.executeShellCommand(
+ "settings get secure sleep_timeout");
+ device.executeShellCommand("settings put system screen_off_timeout -1");
+ device.executeShellCommand("settings put secure sleep_timeout -1");
+ }
+
+ private void restoreScreenOffAndSleepTimeouts() throws IOException {
+ requireNonNull(initialScreenOffTimeoutValue);
+ requireNonNull(initialSleepTimeoutValue);
+ try {
+ device.executeShellCommand(
+ "settings put system screen_off_timeout " + initialScreenOffTimeoutValue);
+ device.executeShellCommand(
+ "settings put secure sleep_timeout " + initialSleepTimeoutValue);
+ } finally {
+ initialScreenOffTimeoutValue = null;
+ initialSleepTimeoutValue = null;
+ }
+ }
}
diff --git a/tests/functional/com/android/documentsui/DialogUiTest.java b/tests/functional/com/android/documentsui/DialogUiTest.java
index c9c9a77..eec7ef1 100644
--- a/tests/functional/com/android/documentsui/DialogUiTest.java
+++ b/tests/functional/com/android/documentsui/DialogUiTest.java
@@ -29,7 +29,6 @@
import android.graphics.Paint;
import android.os.Build;
import android.os.ParcelFileDescriptor;
-import android.support.test.uiautomator.UiDevice;
import android.view.KeyEvent;
import androidx.fragment.app.FragmentManager;
@@ -37,6 +36,7 @@
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.UiDevice;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.dirlist.RenameDocumentFragment;
diff --git a/tests/functional/com/android/documentsui/DocumentsUiTestBase.java b/tests/functional/com/android/documentsui/DocumentsUiTestBase.java
new file mode 100644
index 0000000..75e03bc
--- /dev/null
+++ b/tests/functional/com/android/documentsui/DocumentsUiTestBase.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.documentsui;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static java.util.Objects.requireNonNull;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.view.KeyEvent;
+
+import androidx.test.uiautomator.UiDevice;
+
+import com.android.documentsui.bots.Bots;
+
+import java.io.IOException;
+
+
+/** Base class for instrumentation tests for DocumentsUI Activities. */
+class DocumentsUiTestBase {
+ private static final int BOTS_TIMEOUT = 5000; // 5 seconds
+
+ protected Context targetContext;
+ protected Context context;
+ protected UiDevice device;
+ protected Bots bots;
+
+ private String initialScreenOffTimeoutValue = null;
+ private String initialSleepTimeoutValue = null;
+
+ protected void setUp() throws Exception {
+ final Instrumentation instrumentation = getInstrumentation();
+ targetContext = instrumentation.getTargetContext();
+ context = instrumentation.getContext();
+ device = UiDevice.getInstance(instrumentation);
+
+ disableScreenOffAndSleepTimeouts();
+
+ device.setOrientationNatural();
+
+ // "Wake-up" the device and navigate to the home page.
+ device.pressKeyCode(KeyEvent.KEYCODE_WAKEUP);
+ device.pressKeyCode(KeyEvent.KEYCODE_MENU);
+
+ bots = new Bots(device, instrumentation.getUiAutomation(), targetContext, BOTS_TIMEOUT);
+ }
+
+ protected void tearDown() throws Exception {
+ restoreScreenOffAndSleepTimeouts();
+ }
+
+ private void disableScreenOffAndSleepTimeouts() throws IOException {
+ initialScreenOffTimeoutValue = device.executeShellCommand(
+ "settings get system screen_off_timeout");
+ initialSleepTimeoutValue = device.executeShellCommand(
+ "settings get secure sleep_timeout");
+ device.executeShellCommand("settings put system screen_off_timeout -1");
+ device.executeShellCommand("settings put secure sleep_timeout -1");
+ }
+
+ private void restoreScreenOffAndSleepTimeouts() throws IOException {
+ requireNonNull(initialScreenOffTimeoutValue);
+ requireNonNull(initialSleepTimeoutValue);
+ try {
+ device.executeShellCommand(
+ "settings put system screen_off_timeout " + initialScreenOffTimeoutValue);
+ device.executeShellCommand(
+ "settings put secure sleep_timeout " + initialSleepTimeoutValue);
+ } finally {
+ initialScreenOffTimeoutValue = null;
+ initialSleepTimeoutValue = null;
+ }
+ }
+}
diff --git a/tests/functional/com/android/documentsui/FileCopyUiTest.java b/tests/functional/com/android/documentsui/FileCopyUiTest.java
index eba4809..1f35868 100644
--- a/tests/functional/com/android/documentsui/FileCopyUiTest.java
+++ b/tests/functional/com/android/documentsui/FileCopyUiTest.java
@@ -33,11 +33,11 @@
import android.os.SystemClock;
import android.provider.MediaStore;
import android.provider.Settings;
-import android.support.test.uiautomator.UiObjectNotFoundException;
import android.text.TextUtils;
import android.util.Log;
import androidx.test.filters.LargeTest;
+import androidx.test.uiautomator.UiObjectNotFoundException;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.RootInfo;
diff --git a/tests/functional/com/android/documentsui/IntegratedDownloadsUiTest.java b/tests/functional/com/android/documentsui/IntegratedDownloadsUiTest.java
index 9302e52..aa55be9 100644
--- a/tests/functional/com/android/documentsui/IntegratedDownloadsUiTest.java
+++ b/tests/functional/com/android/documentsui/IntegratedDownloadsUiTest.java
@@ -20,12 +20,12 @@
import android.app.DownloadManager.Request;
import android.content.Context;
import android.net.Uri;
-import android.support.test.uiautomator.Configurator;
-import android.support.test.uiautomator.UiObject;
import android.view.MotionEvent;
import androidx.test.filters.LargeTest;
import androidx.test.filters.Suppress;
+import androidx.test.uiautomator.Configurator;
+import androidx.test.uiautomator.UiObject;
import com.android.documentsui.files.FilesActivity;
diff --git a/tests/functional/com/android/documentsui/RenameDocumentUiTest.java b/tests/functional/com/android/documentsui/RenameDocumentUiTest.java
index 585df5e..e992ac9 100644
--- a/tests/functional/com/android/documentsui/RenameDocumentUiTest.java
+++ b/tests/functional/com/android/documentsui/RenameDocumentUiTest.java
@@ -16,9 +16,8 @@
package com.android.documentsui;
-import android.support.test.uiautomator.UiObjectNotFoundException;
-
import androidx.test.filters.LargeTest;
+import androidx.test.uiautomator.UiObjectNotFoundException;
import com.android.documentsui.files.FilesActivity;
import com.android.documentsui.util.VersionUtils;
diff --git a/tests/unit/com/android/documentsui/RecentsLoaderTests.java b/tests/unit/com/android/documentsui/RecentsLoaderTests.java
index d95e924..915a3bd 100644
--- a/tests/unit/com/android/documentsui/RecentsLoaderTests.java
+++ b/tests/unit/com/android/documentsui/RecentsLoaderTests.java
@@ -56,7 +56,6 @@
private TestEnv mEnv;
private TestActivity mActivity;
private RecentsLoader mLoader;
- private boolean mContentChanged;
@Before
public void setUp() {
@@ -148,12 +147,13 @@
@Test
public void testContentsUpdate_observable() throws Exception {
- CountDownLatch latch = new CountDownLatch(1);
- Runnable callback = () -> {
- latch.countDown();
- mContentChanged = true;
- };
- mLoader.setObserver(new LockingContentObserver(new ContentLock(), callback));
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ // Please be mindful of the fact that the callback will be invoked on the Main (aka UI)
+ // thread, while the test itself is running on another (dedicated) thread.
+ final Runnable onContentChangedCallback = latch::countDown;
+ mLoader.setObserver(new LockingContentObserver(
+ new ContentLock(), onContentChangedCallback));
final DocumentInfo doc = mEnv.model.createFile("freddy.jpg");
doc.lastModified = System.currentTimeMillis();
@@ -162,12 +162,12 @@
mLoader.loadInBackground();
- TestCursor c = (TestCursor) mEnv.mockProviders.get(TestProvidersAccess.HOME.authority)
+ final TestCursor c = (TestCursor) mEnv.mockProviders.get(TestProvidersAccess.HOME.authority)
.queryRecentDocuments(null, null);
c.mockOnChange();
- latch.await(1, TimeUnit.SECONDS);
- assertTrue(mContentChanged);
+ final boolean onContentChangedCallbackInvoked = latch.await(1, TimeUnit.SECONDS);
+ assertTrue(onContentChangedCallbackInvoked);
}
@Test
diff --git a/tests/unit/com/android/documentsui/sorting/SortingCursorWrapperTest.java b/tests/unit/com/android/documentsui/sorting/SortingCursorWrapperTest.java
index e323dc8..72ad1b7 100644
--- a/tests/unit/com/android/documentsui/sorting/SortingCursorWrapperTest.java
+++ b/tests/unit/com/android/documentsui/sorting/SortingCursorWrapperTest.java
@@ -34,7 +34,6 @@
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.Shared;
import com.android.documentsui.roots.RootCursorWrapper;
-import com.android.documentsui.sorting.SortModel.SortDimensionId;
import com.android.documentsui.testing.SortModels;
import com.android.documentsui.testing.TestFileTypeLookup;
@@ -497,7 +496,7 @@
}
private Cursor createSortingCursorWrapper(Cursor c) {
- final @SortDimensionId int id = sortModel.getSortedDimensionId();
+ final int id = sortModel.getSortedDimensionId();
return new SortingCursorWrapper(c, sortModel.getDimensionById(id), fileTypeLookup);
}
}
diff --git a/trace.sh b/trace.sh
new file mode 100755
index 0000000..573d923
--- /dev/null
+++ b/trace.sh
@@ -0,0 +1,3 @@
+$ANDROID_BUILD_TOP/external/perfetto/tools/record_android_trace \
+ -c $ANDROID_BUILD_TOP/packages/apps/DocumentsUI/perfetto_config.pbtx \
+ -o /tmp/perfetto-traces/docsui-$(date +"%d-%m-%Y_%H-%M-%S").perfetto-trace