summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Leif Hendrik Wilden <leifhendrik@google.com> 2018-01-03 11:16:09 -0800
committer Leif Hendrik Wilden <leifhendrik@google.com> 2018-01-03 11:17:07 -0800
commit420534939aeb3b3d155388cf87b71f5432485ed9 (patch)
treec43c7d24f1b0209930aed04618f7be3dce86d80d
parent67aa5151d2d4f6bc09f945d1eaf98c7d06fc76b5 (diff)
Introduce seperate build target for Android TV.
Bug: 68140422 Test: Manually tested that both targets work properly. Change-Id: Icb1ce0eb91c46d0533e89dce0a92cde3b362e6d7
-rw-r--r--Android.mk57
-rw-r--r--AndroidManifest.xml18
-rw-r--r--build_apk.mk28
-rw-r--r--minimal/AndroidManifest.xml51
-rw-r--r--minimal/res/layout/dialog_open_scoped_directory.xml44
-rw-r--r--minimal/res/mipmap-anydpi/ic_app_icon.xml5
-rw-r--r--minimal/res/mipmap-hdpi/ic_launcher_icon_foreground.pngbin0 -> 8196 bytes
-rw-r--r--minimal/res/mipmap-mdpi/ic_launcher_icon_foreground.pngbin0 -> 4594 bytes
-rw-r--r--minimal/res/mipmap-xhdpi/ic_launcher_icon_foreground.pngbin0 -> 11011 bytes
-rw-r--r--minimal/res/mipmap-xxhdpi/ic_launcher_icon_foreground.pngbin0 -> 18176 bytes
-rw-r--r--minimal/res/mipmap-xxxhdpi/ic_launcher_icon_foreground.pngbin0 -> 18291 bytes
-rw-r--r--minimal/res/values/colors.xml19
-rw-r--r--minimal/res/values/drawables.xml20
-rw-r--r--minimal/res/values/strings.xml47
-rw-r--r--src/com/android/documentsui/Metrics.java104
-rw-r--r--src/com/android/documentsui/OpenExternalDirectoryActivity.java30
-rw-r--r--src/com/android/documentsui/PackageReceiver.java9
-rw-r--r--src/com/android/documentsui/ScopedAccessMetrics.java148
-rw-r--r--src/com/android/documentsui/ScopedAccessPackageReceiver.java49
-rw-r--r--src/com/android/documentsui/prefs/LocalPreferences.java65
-rw-r--r--src/com/android/documentsui/prefs/ScopedAccessLocalPreferences.java107
21 files changed, 592 insertions, 209 deletions
diff --git a/Android.mk b/Android.mk
index 3044ea53f..8e6b70987 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1,37 +1,40 @@
LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := optional
-LOCAL_PRIVILEGED_MODULE := true
+########################
+# Complete DocumentsUI app:
+include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES += guava
-
+LOCAL_PACKAGE_NAME := DocumentsUI
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_FULL_MANIFEST_FILE := $(LOCAL_PATH)/AndroidManifest.xml
-LOCAL_STATIC_ANDROID_LIBRARIES := \
- android-support-core-ui \
- android-support-v4 \
- android-support-v7-appcompat \
- android-support-v13 \
- android-support-design \
- android-support-transition \
- android-support-v7-recyclerview
-
-LOCAL_USE_AAPT2 := true
+include $(LOCAL_PATH)/build_apk.mk
-LOCAL_JACK_FLAGS := \
- -D jack.optimization.inner-class.accessors=true
-
-# Only enable asserts on userdebug/eng builds
-ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
-LOCAL_JACK_FLAGS += -D jack.assert.policy=always
-endif
-
-LOCAL_PACKAGE_NAME := DocumentsUI
-LOCAL_CERTIFICATE := platform
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+########################
+# Minimal DocumentsUI app (supports Scoped Directory Access only):
+include $(CLEAR_VARS)
-include $(BUILD_PACKAGE)
+LOCAL_SRC_FILES := \
+ src/com/android/documentsui/OpenExternalDirectoryActivity.java \
+ src/com/android/documentsui/ScopedAccessPackageReceiver.java \
+ src/com/android/documentsui/ScopedAccessMetrics.java \
+ src/com/android/documentsui/archives/Archive.java \
+ src/com/android/documentsui/archives/ArchiveId.java \
+ src/com/android/documentsui/archives/ArchivesProvider.java \
+ src/com/android/documentsui/archives/Loader.java \
+ src/com/android/documentsui/archives/Proxy.java \
+ src/com/android/documentsui/archives/ReadableArchive.java \
+ src/com/android/documentsui/archives/WriteableArchive.java \
+ src/com/android/documentsui/base/Providers.java \
+ src/com/android/documentsui/prefs/ScopedAccessLocalPreferences.java
+
+LOCAL_PACKAGE_NAME := DocumentsUIMinimal
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/minimal/res
+LOCAL_FULL_MANIFEST_FILE := $(LOCAL_PATH)/minimal/AndroidManifest.xml
+
+include $(LOCAL_PATH)/build_apk.mk
+
+# Include makefiles for tests and libraries under the current path
include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 68a3df8ff..9abc7efca 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1,3 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2007-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.documentsui">
diff --git a/build_apk.mk b/build_apk.mk
new file mode 100644
index 000000000..c09517fe9
--- /dev/null
+++ b/build_apk.mk
@@ -0,0 +1,28 @@
+LOCAL_MODULE_TAGS := optional
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_STATIC_JAVA_LIBRARIES += guava
+
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+ android-support-core-ui \
+ android-support-v4 \
+ android-support-v7-appcompat \
+ android-support-v13 \
+ android-support-design \
+ android-support-transition \
+ android-support-v7-recyclerview
+
+LOCAL_USE_AAPT2 := true
+
+LOCAL_JACK_FLAGS := \
+ -D jack.optimization.inner-class.accessors=true
+
+# Only enable asserts on userdebug/eng builds
+ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
+LOCAL_JACK_FLAGS += -D jack.assert.policy=always
+endif
+
+LOCAL_CERTIFICATE := platform
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+
+include $(BUILD_PACKAGE)
diff --git a/minimal/AndroidManifest.xml b/minimal/AndroidManifest.xml
new file mode 100644
index 000000000..b985c98c8
--- /dev/null
+++ b/minimal/AndroidManifest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2007-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.documentsui">
+
+ <uses-permission android:name="android.permission.GET_APP_GRANTED_URI_PERMISSIONS" />
+ <uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
+ <uses-permission android:name="android.permission.CACHE_CONTENT" />
+
+ <application
+ android:label="@string/app_label"
+ android:icon="@drawable/app_icon"
+ android:supportsRtl="true"
+ android:allowBackup="false"
+ android:fullBackupOnly="false">
+
+ <activity
+ android:name=".OpenExternalDirectoryActivity"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar">
+ <intent-filter>
+ <action android:name="android.os.storage.action.OPEN_EXTERNAL_DIRECTORY" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <receiver android:name=".ScopedAccessPackageReceiver">
+ <intent-filter>
+ <action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
+ <action android:name="android.intent.action.PACKAGE_DATA_CLEARED" />
+ <data android:scheme="package" />
+ </intent-filter>
+ </receiver>
+
+ </application>
+</manifest>
diff --git a/minimal/res/layout/dialog_open_scoped_directory.xml b/minimal/res/layout/dialog_open_scoped_directory.xml
new file mode 100644
index 000000000..cb3920604
--- /dev/null
+++ b/minimal/res/layout/dialog_open_scoped_directory.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:theme="@style/Theme.AppCompat.Light.Dialog.Alert"
+ android:orientation="vertical"
+ android:paddingEnd="24dp"
+ android:paddingStart="24dp" >
+
+ <TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingEnd="24dp"
+ android:paddingStart="32dp"
+ android:paddingTop="24dp">
+ </TextView>
+
+ <CheckBox
+ android:id="@+id/do_not_ask_checkbox"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dip"
+ android:text="@string/never_ask_again"
+ android:textColor="?android:attr/textColorSecondary"
+ android:visibility="gone" />
+</LinearLayout>
diff --git a/minimal/res/mipmap-anydpi/ic_app_icon.xml b/minimal/res/mipmap-anydpi/ic_app_icon.xml
new file mode 100644
index 000000000..cd4fa58a8
--- /dev/null
+++ b/minimal/res/mipmap-anydpi/ic_app_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@color/app_icon_background"/>
+ <foreground android:drawable="@mipmap/ic_launcher_icon_foreground"/>
+</adaptive-icon>
diff --git a/minimal/res/mipmap-hdpi/ic_launcher_icon_foreground.png b/minimal/res/mipmap-hdpi/ic_launcher_icon_foreground.png
new file mode 100644
index 000000000..992c44e2d
--- /dev/null
+++ b/minimal/res/mipmap-hdpi/ic_launcher_icon_foreground.png
Binary files differ
diff --git a/minimal/res/mipmap-mdpi/ic_launcher_icon_foreground.png b/minimal/res/mipmap-mdpi/ic_launcher_icon_foreground.png
new file mode 100644
index 000000000..4639ff014
--- /dev/null
+++ b/minimal/res/mipmap-mdpi/ic_launcher_icon_foreground.png
Binary files differ
diff --git a/minimal/res/mipmap-xhdpi/ic_launcher_icon_foreground.png b/minimal/res/mipmap-xhdpi/ic_launcher_icon_foreground.png
new file mode 100644
index 000000000..b99294403
--- /dev/null
+++ b/minimal/res/mipmap-xhdpi/ic_launcher_icon_foreground.png
Binary files differ
diff --git a/minimal/res/mipmap-xxhdpi/ic_launcher_icon_foreground.png b/minimal/res/mipmap-xxhdpi/ic_launcher_icon_foreground.png
new file mode 100644
index 000000000..ae44b2f68
--- /dev/null
+++ b/minimal/res/mipmap-xxhdpi/ic_launcher_icon_foreground.png
Binary files differ
diff --git a/minimal/res/mipmap-xxxhdpi/ic_launcher_icon_foreground.png b/minimal/res/mipmap-xxxhdpi/ic_launcher_icon_foreground.png
new file mode 100644
index 000000000..85150eb3b
--- /dev/null
+++ b/minimal/res/mipmap-xxxhdpi/ic_launcher_icon_foreground.png
Binary files differ
diff --git a/minimal/res/values/colors.xml b/minimal/res/values/colors.xml
new file mode 100644
index 000000000..61a8150e6
--- /dev/null
+++ b/minimal/res/values/colors.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <color name="app_icon_background">#ff4688f2</color>
+</resources>
diff --git a/minimal/res/values/drawables.xml b/minimal/res/values/drawables.xml
new file mode 100644
index 000000000..2e5e77d7a
--- /dev/null
+++ b/minimal/res/values/drawables.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <item name="app_icon" type="drawable">@mipmap/ic_app_icon</item>
+ <item name="launcher_icon" type="drawable">@mipmap/ic_app_icon</item>
+</resources>
diff --git a/minimal/res/values/strings.xml b/minimal/res/values/strings.xml
new file mode 100644
index 000000000..bcffd6b69
--- /dev/null
+++ b/minimal/res/values/strings.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Title of the Files application [CHAR LIMIT=32] -->
+ <string name="files_label">Files</string>
+
+ <!-- Title of the documents application [CHAR LIMIT=32] -->
+ <string name="app_label">@string/files_label</string>
+
+ <!-- Title of the documents application [CHAR LIMIT=32] -->
+ <string name="launcher_label">@string/files_label</string>
+
+ <!-- Text in an alert dialog asking user to grant app access to a given directory in an external storage volume -->
+ <string name="open_external_dialog_request">Grant <xliff:g id="appName" example="System Settings"><b>^1</b></xliff:g>
+ access to <xliff:g id="directory" example="Pictures"><i>^2</i></xliff:g> directory on
+ <xliff:g id="storage" example="SD Card"><i>^3</i></xliff:g>?</string>
+ <!-- Text in an alert dialog asking user to grant app access to a given directory in the internal storage -->
+ <string name="open_external_dialog_request_primary_volume">Grant <xliff:g id="appName" example="System Settings"><b>^1</b></xliff:g>
+ access to <xliff:g id="directory" example="Pictures"><i>^2</i></xliff:g> directory?</string>
+ <!-- Text in an alert dialog asking user to grant app access to all data in an external storage volume -->
+ <string name="open_external_dialog_root_request">Grant <xliff:g id="appName" example="System Settings"><b>^1</b></xliff:g>
+ access to your data, including photos and videos, on <xliff:g id="storage" example="SD Card"><i>^2</i></xliff:g>?</string>
+ <!-- Checkbox that allows user to not be questioned about the directory access request again -->
+ <string name="never_ask_again">Don\'t ask again</string>
+ <!-- Text in the button asking user to allow access to a given directory. -->
+ <string name="allow">Allow</string>
+ <!-- Text in the button asking user to deny access to a given directory. -->
+ <string name="deny">Deny</string>
+
+ <!-- Error message shown when an archive fails to load -->
+ <string name="archive_loading_failed">Unable to open archive for browsing. File is either corrupt, or an unsupported format.</string>
+
+</resources>
diff --git a/src/com/android/documentsui/Metrics.java b/src/com/android/documentsui/Metrics.java
index f421906be..b7f756f5e 100644
--- a/src/com/android/documentsui/Metrics.java
+++ b/src/com/android/documentsui/Metrics.java
@@ -16,14 +16,11 @@
package com.android.documentsui;
-import static android.os.Environment.STANDARD_DIRECTORIES;
import static com.android.documentsui.DocumentsApplication.acquireUnstableProviderOrThrow;
import static com.android.documentsui.base.Shared.DEBUG;
import android.annotation.IntDef;
import android.annotation.Nullable;
-import android.annotation.StringDef;
-import android.app.Activity;
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.Intent;
@@ -45,13 +42,14 @@ import com.android.documentsui.roots.ProvidersAccess;
import com.android.documentsui.services.FileOperationService;
import com.android.documentsui.services.FileOperationService.OpType;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
-/** @hide */
+/**
+ * Methods for logging metrics.
+ */
public final class Metrics {
private static final String TAG = "Metrics";
@@ -689,102 +687,6 @@ public final class Metrics {
logHistogram(context, histogram, getOpCode(operationType, PROVIDER_INTRA));
}
- // Types for logInvalidScopedAccessRequest
- public static final String SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS =
- "docsui_scoped_directory_access_invalid_args";
- public static final String SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY =
- "docsui_scoped_directory_access_invalid_dir";
- public static final String SCOPED_DIRECTORY_ACCESS_ERROR =
- "docsui_scoped_directory_access_error";
-
- @StringDef(value = {
- SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS,
- SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY,
- SCOPED_DIRECTORY_ACCESS_ERROR
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface InvalidScopedAccess{}
-
- public static void logInvalidScopedAccessRequest(Context context,
- @InvalidScopedAccess String type) {
- switch (type) {
- case SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS:
- case SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY:
- case SCOPED_DIRECTORY_ACCESS_ERROR:
- logCount(context, type);
- break;
- default:
- Log.wtf(TAG, "invalid InvalidScopedAccess: " + type);
- }
- }
-
- // Types for logValidScopedAccessRequest
- public static final int SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED = 0;
- public static final int SCOPED_DIRECTORY_ACCESS_GRANTED = 1;
- public static final int SCOPED_DIRECTORY_ACCESS_DENIED = 2;
- public static final int SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST = 3;
- public static final int SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED = 4;
-
- @IntDef(flag = true, value = {
- SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED,
- SCOPED_DIRECTORY_ACCESS_GRANTED,
- SCOPED_DIRECTORY_ACCESS_DENIED,
- SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST,
- SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ScopedAccessGrant {}
-
- public static void logValidScopedAccessRequest(Activity activity, String directory,
- @ScopedAccessGrant int type) {
- int index = -1;
- if (OpenExternalDirectoryActivity.DIRECTORY_ROOT.equals(directory)) {
- index = -2;
- } else {
- for (int i = 0; i < STANDARD_DIRECTORIES.length; i++) {
- if (STANDARD_DIRECTORIES[i].equals(directory)) {
- index = i;
- break;
- }
- }
- }
- final String packageName = activity.getCallingPackage();
- switch (type) {
- case SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED:
- MetricsLogger.action(activity, MetricsEvent
- .ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED_BY_PACKAGE, packageName);
- MetricsLogger.action(activity, MetricsEvent
- .ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED_BY_FOLDER, index);
- break;
- case SCOPED_DIRECTORY_ACCESS_GRANTED:
- MetricsLogger.action(activity, MetricsEvent
- .ACTION_SCOPED_DIRECTORY_ACCESS_GRANTED_BY_PACKAGE, packageName);
- MetricsLogger.action(activity, MetricsEvent
- .ACTION_SCOPED_DIRECTORY_ACCESS_GRANTED_BY_FOLDER, index);
- break;
- case SCOPED_DIRECTORY_ACCESS_DENIED:
- MetricsLogger.action(activity, MetricsEvent
- .ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_BY_PACKAGE, packageName);
- MetricsLogger.action(activity, MetricsEvent
- .ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_BY_FOLDER, index);
- break;
- case SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST:
- MetricsLogger.action(activity, MetricsEvent
- .ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST_BY_PACKAGE, packageName);
- MetricsLogger.action(activity, MetricsEvent
- .ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST_BY_FOLDER, index);
- break;
- case SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED:
- MetricsLogger.action(activity, MetricsEvent
- .ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED_BY_PACKAGE, packageName);
- MetricsLogger.action(activity, MetricsEvent
- .ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED_BY_FOLDER, index);
- break;
- default:
- Log.wtf(TAG, "invalid ScopedAccessGrant: " + type);
- }
- }
-
/**
* Logs the action that was started by user.
* @param context
diff --git a/src/com/android/documentsui/OpenExternalDirectoryActivity.java b/src/com/android/documentsui/OpenExternalDirectoryActivity.java
index 74af224b5..04ac42cec 100644
--- a/src/com/android/documentsui/OpenExternalDirectoryActivity.java
+++ b/src/com/android/documentsui/OpenExternalDirectoryActivity.java
@@ -19,21 +19,21 @@ package com.android.documentsui;
import static android.os.Environment.isStandardDirectory;
import static android.os.storage.StorageVolume.EXTRA_DIRECTORY_NAME;
import static android.os.storage.StorageVolume.EXTRA_STORAGE_VOLUME;
-import static com.android.documentsui.Metrics.SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED;
-import static com.android.documentsui.Metrics.SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED;
-import static com.android.documentsui.Metrics.SCOPED_DIRECTORY_ACCESS_DENIED;
-import static com.android.documentsui.Metrics.SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST;
-import static com.android.documentsui.Metrics.SCOPED_DIRECTORY_ACCESS_ERROR;
-import static com.android.documentsui.Metrics.SCOPED_DIRECTORY_ACCESS_GRANTED;
-import static com.android.documentsui.Metrics.SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS;
-import static com.android.documentsui.Metrics.SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY;
-import static com.android.documentsui.Metrics.logInvalidScopedAccessRequest;
-import static com.android.documentsui.Metrics.logValidScopedAccessRequest;
-import static com.android.documentsui.base.Shared.DEBUG;
-import static com.android.documentsui.prefs.LocalPreferences.PERMISSION_ASK_AGAIN;
-import static com.android.documentsui.prefs.LocalPreferences.PERMISSION_NEVER_ASK;
-import static com.android.documentsui.prefs.LocalPreferences.getScopedAccessPermissionStatus;
-import static com.android.documentsui.prefs.LocalPreferences.setScopedAccessPermissionStatus;
+import static com.android.documentsui.ScopedAccessMetrics.DEBUG;
+import static com.android.documentsui.ScopedAccessMetrics.SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED;
+import static com.android.documentsui.ScopedAccessMetrics.SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED;
+import static com.android.documentsui.ScopedAccessMetrics.SCOPED_DIRECTORY_ACCESS_DENIED;
+import static com.android.documentsui.ScopedAccessMetrics.SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST;
+import static com.android.documentsui.ScopedAccessMetrics.SCOPED_DIRECTORY_ACCESS_ERROR;
+import static com.android.documentsui.ScopedAccessMetrics.SCOPED_DIRECTORY_ACCESS_GRANTED;
+import static com.android.documentsui.ScopedAccessMetrics.SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS;
+import static com.android.documentsui.ScopedAccessMetrics.SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY;
+import static com.android.documentsui.ScopedAccessMetrics.logInvalidScopedAccessRequest;
+import static com.android.documentsui.ScopedAccessMetrics.logValidScopedAccessRequest;
+import static com.android.documentsui.prefs.ScopedAccessLocalPreferences.PERMISSION_ASK_AGAIN;
+import static com.android.documentsui.prefs.ScopedAccessLocalPreferences.PERMISSION_NEVER_ASK;
+import static com.android.documentsui.prefs.ScopedAccessLocalPreferences.getScopedAccessPermissionStatus;
+import static com.android.documentsui.prefs.ScopedAccessLocalPreferences.setScopedAccessPermissionStatus;
import android.annotation.SuppressLint;
import android.app.Activity;
diff --git a/src/com/android/documentsui/PackageReceiver.java b/src/com/android/documentsui/PackageReceiver.java
index 5cb28272b..e917369d9 100644
--- a/src/com/android/documentsui/PackageReceiver.java
+++ b/src/com/android/documentsui/PackageReceiver.java
@@ -23,10 +23,11 @@ import android.content.Intent;
import android.net.Uri;
import com.android.documentsui.picker.LastAccessedProvider;
-import com.android.documentsui.prefs.LocalPreferences;
+import com.android.documentsui.prefs.ScopedAccessLocalPreferences;
/**
- * Clean up {@link LastAccessedProvider} and {@link LocalPreferences} when packages are removed.
+ * Clean up {@link LastAccessedProvider} and {@link ScopedAccessLocalPreferences} when packages
+ * are removed.
*/
public class PackageReceiver extends BroadcastReceiver {
@Override
@@ -44,7 +45,7 @@ public class PackageReceiver extends BroadcastReceiver {
null,
null);
if (packageName != null) {
- LocalPreferences.clearPackagePreferences(context, packageName);
+ ScopedAccessLocalPreferences.clearPackagePreferences(context, packageName);
}
} else if (Intent.ACTION_PACKAGE_DATA_CLEARED.equals(action)) {
if (packageName != null) {
@@ -52,7 +53,7 @@ public class PackageReceiver extends BroadcastReceiver {
LastAccessedProvider.buildLastAccessed(packageName),
LastAccessedProvider.METHOD_PURGE_PACKAGE,
packageName, null);
- LocalPreferences.clearPackagePreferences(context, packageName);
+ ScopedAccessLocalPreferences.clearPackagePreferences(context, packageName);
}
}
}
diff --git a/src/com/android/documentsui/ScopedAccessMetrics.java b/src/com/android/documentsui/ScopedAccessMetrics.java
new file mode 100644
index 000000000..db5fa88a1
--- /dev/null
+++ b/src/com/android/documentsui/ScopedAccessMetrics.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 android.os.Environment.STANDARD_DIRECTORIES;
+
+import android.annotation.IntDef;
+import android.annotation.StringDef;
+import android.app.Activity;
+import android.content.Context;
+import android.os.Build;
+import android.util.Log;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Methods for logging scoped directory access metrics.
+ */
+public final class ScopedAccessMetrics {
+ private static final String TAG = "Metrics";
+
+ public static final boolean DEBUG = Build.IS_DEBUGGABLE;
+
+ // Types for logInvalidScopedAccessRequest
+ public static final String SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS =
+ "docsui_scoped_directory_access_invalid_args";
+ public static final String SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY =
+ "docsui_scoped_directory_access_invalid_dir";
+ public static final String SCOPED_DIRECTORY_ACCESS_ERROR =
+ "docsui_scoped_directory_access_error";
+
+ @StringDef(value = {
+ SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS,
+ SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY,
+ SCOPED_DIRECTORY_ACCESS_ERROR
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface InvalidScopedAccess{}
+
+ public static void logInvalidScopedAccessRequest(Context context,
+ @InvalidScopedAccess String type) {
+ switch (type) {
+ case SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS:
+ case SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY:
+ case SCOPED_DIRECTORY_ACCESS_ERROR:
+ logCount(context, type);
+ break;
+ default:
+ Log.wtf(TAG, "invalid InvalidScopedAccess: " + type);
+ }
+ }
+
+ // Types for logValidScopedAccessRequest
+ public static final int SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED = 0;
+ public static final int SCOPED_DIRECTORY_ACCESS_GRANTED = 1;
+ public static final int SCOPED_DIRECTORY_ACCESS_DENIED = 2;
+ public static final int SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST = 3;
+ public static final int SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED = 4;
+
+ @IntDef(flag = true, value = {
+ SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED,
+ SCOPED_DIRECTORY_ACCESS_GRANTED,
+ SCOPED_DIRECTORY_ACCESS_DENIED,
+ SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST,
+ SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ScopedAccessGrant {}
+
+ public static void logValidScopedAccessRequest(Activity activity, String directory,
+ @ScopedAccessGrant int type) {
+ int index = -1;
+ if (OpenExternalDirectoryActivity.DIRECTORY_ROOT.equals(directory)) {
+ index = -2;
+ } else {
+ for (int i = 0; i < STANDARD_DIRECTORIES.length; i++) {
+ if (STANDARD_DIRECTORIES[i].equals(directory)) {
+ index = i;
+ break;
+ }
+ }
+ }
+ final String packageName = activity.getCallingPackage();
+ switch (type) {
+ case SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED:
+ MetricsLogger.action(activity, MetricsEvent
+ .ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED_BY_PACKAGE, packageName);
+ MetricsLogger.action(activity, MetricsEvent
+ .ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED_BY_FOLDER, index);
+ break;
+ case SCOPED_DIRECTORY_ACCESS_GRANTED:
+ MetricsLogger.action(activity, MetricsEvent
+ .ACTION_SCOPED_DIRECTORY_ACCESS_GRANTED_BY_PACKAGE, packageName);
+ MetricsLogger.action(activity, MetricsEvent
+ .ACTION_SCOPED_DIRECTORY_ACCESS_GRANTED_BY_FOLDER, index);
+ break;
+ case SCOPED_DIRECTORY_ACCESS_DENIED:
+ MetricsLogger.action(activity, MetricsEvent
+ .ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_BY_PACKAGE, packageName);
+ MetricsLogger.action(activity, MetricsEvent
+ .ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_BY_FOLDER, index);
+ break;
+ case SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST:
+ MetricsLogger.action(activity, MetricsEvent
+ .ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST_BY_PACKAGE, packageName);
+ MetricsLogger.action(activity, MetricsEvent
+ .ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST_BY_FOLDER, index);
+ break;
+ case SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED:
+ MetricsLogger.action(activity, MetricsEvent
+ .ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED_BY_PACKAGE, packageName);
+ MetricsLogger.action(activity, MetricsEvent
+ .ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED_BY_FOLDER, index);
+ break;
+ default:
+ Log.wtf(TAG, "invalid ScopedAccessGrant: " + type);
+ }
+ }
+
+ /**
+ * Internal method for making a MetricsLogger.count call. Increments the given counter by 1.
+ *
+ * @param context
+ * @param name The counter to increment.
+ */
+ private static void logCount(Context context, String name) {
+ if (DEBUG) Log.d(TAG, name + ": " + 1);
+ MetricsLogger.count(context, name, 1);
+ }
+}
diff --git a/src/com/android/documentsui/ScopedAccessPackageReceiver.java b/src/com/android/documentsui/ScopedAccessPackageReceiver.java
new file mode 100644
index 000000000..515f1db04
--- /dev/null
+++ b/src/com/android/documentsui/ScopedAccessPackageReceiver.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+
+import com.android.documentsui.prefs.ScopedAccessLocalPreferences;
+
+/**
+ * Clean up {@link ScopedAccessLocalPreferences} when packages are removed.
+ */
+public class ScopedAccessPackageReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final ContentResolver resolver = context.getContentResolver();
+
+ final String action = intent.getAction();
+ final Uri data = intent.getData();
+ final String packageName = data == null ? null : data.getSchemeSpecificPart();
+
+ if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
+ if (packageName != null) {
+ ScopedAccessLocalPreferences.clearPackagePreferences(context, packageName);
+ }
+ } else if (Intent.ACTION_PACKAGE_DATA_CLEARED.equals(action)) {
+ if (packageName != null) {
+ ScopedAccessLocalPreferences.clearPackagePreferences(context, packageName);
+ }
+ }
+ }
+}
diff --git a/src/com/android/documentsui/prefs/LocalPreferences.java b/src/com/android/documentsui/prefs/LocalPreferences.java
index 48a922013..955b19da8 100644
--- a/src/com/android/documentsui/prefs/LocalPreferences.java
+++ b/src/com/android/documentsui/prefs/LocalPreferences.java
@@ -19,20 +19,19 @@ package com.android.documentsui.prefs;
import static com.android.documentsui.base.State.MODE_UNKNOWN;
import android.annotation.IntDef;
-import android.annotation.Nullable;
import android.content.Context;
import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
-import android.os.UserHandle;
import android.preference.PreferenceManager;
import com.android.documentsui.base.RootInfo;
-import com.android.documentsui.base.State;
import com.android.documentsui.base.State.ViewMode;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+/**
+ * Methods for accessing the local preferences.
+ */
public class LocalPreferences {
private static final String ROOT_VIEW_MODE_PREFIX = "rootViewMode-";
@@ -66,64 +65,6 @@ public class LocalPreferences {
@Retention(RetentionPolicy.SOURCE)
public @interface PermissionStatus {}
- /**
- * Clears all preferences associated with a given package.
- *
- * <p>Typically called when a package is removed or when user asked to clear its data.
- */
- public static void clearPackagePreferences(Context context, String packageName) {
- clearScopedAccessPreferences(context, packageName);
- }
-
- /**
- * Methods below are used to keep track of denied user requests on scoped directory access so
- * the dialog is not offered when user checked the 'Do not ask again' box
- *
- * <p>It uses a shared preferences, whose key is:
- * <ol>
- * <li>{@code USER_ID|PACKAGE_NAME|VOLUME_UUID|DIRECTORY} for storage volumes that have a UUID
- * (typically physical volumes like SD cards).
- * <li>{@code USER_ID|PACKAGE_NAME||DIRECTORY} for storage volumes that do not have a UUID
- * (typically the emulated volume used for primary storage
- * </ol>
- */
- public static @PermissionStatus int getScopedAccessPermissionStatus(Context context,
- String packageName, @Nullable String uuid, String directory) {
- final String key = getScopedAccessDenialsKey(packageName, uuid, directory);
- return getPrefs(context).getInt(key, PERMISSION_ASK);
- }
-
- public static void setScopedAccessPermissionStatus(Context context, String packageName,
- @Nullable String uuid, String directory, @PermissionStatus int status) {
- final String key = getScopedAccessDenialsKey(packageName, uuid, directory);
- getPrefs(context).edit().putInt(key, status).apply();
- }
-
- private static void clearScopedAccessPreferences(Context context, String packageName) {
- final String keySubstring = "|" + packageName + "|";
- final SharedPreferences prefs = getPrefs(context);
- Editor editor = null;
- for (final String key : prefs.getAll().keySet()) {
- if (key.contains(keySubstring)) {
- if (editor == null) {
- editor = prefs.edit();
- }
- editor.remove(key);
- }
- }
- if (editor != null) {
- editor.apply();
- }
- }
-
- private static String getScopedAccessDenialsKey(String packageName, String uuid,
- String directory) {
- final int userId = UserHandle.myUserId();
- return uuid == null
- ? userId + "|" + packageName + "||" + directory
- : userId + "|" + packageName + "|" + uuid + "|" + directory;
- }
-
public static boolean shouldBackup(String s) {
return (s != null) ? s.startsWith(ROOT_VIEW_MODE_PREFIX) : false;
}
diff --git a/src/com/android/documentsui/prefs/ScopedAccessLocalPreferences.java b/src/com/android/documentsui/prefs/ScopedAccessLocalPreferences.java
new file mode 100644
index 000000000..f3e9a2591
--- /dev/null
+++ b/src/com/android/documentsui/prefs/ScopedAccessLocalPreferences.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.documentsui.prefs;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.os.UserHandle;
+import android.preference.PreferenceManager;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Methods for accessing the local preferences with regards to scoped directory access.
+ */
+public class ScopedAccessLocalPreferences {
+
+ private static SharedPreferences getPrefs(Context context) {
+ return PreferenceManager.getDefaultSharedPreferences(context);
+ }
+
+ public static final int PERMISSION_ASK = 0;
+ public static final int PERMISSION_ASK_AGAIN = 1;
+ public static final int PERMISSION_NEVER_ASK = -1;
+
+ @IntDef(flag = true, value = {
+ PERMISSION_ASK,
+ PERMISSION_ASK_AGAIN,
+ PERMISSION_NEVER_ASK,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PermissionStatus {}
+
+ /**
+ * Methods below are used to keep track of denied user requests on scoped directory access so
+ * the dialog is not offered when user checked the 'Do not ask again' box
+ *
+ * <p>It uses a shared preferences, whose key is:
+ * <ol>
+ * <li>{@code USER_ID|PACKAGE_NAME|VOLUME_UUID|DIRECTORY} for storage volumes that have a UUID
+ * (typically physical volumes like SD cards).
+ * <li>{@code USER_ID|PACKAGE_NAME||DIRECTORY} for storage volumes that do not have a UUID
+ * (typically the emulated volume used for primary storage
+ * </ol>
+ */
+ public static @PermissionStatus int getScopedAccessPermissionStatus(Context context,
+ String packageName, @Nullable String uuid, String directory) {
+ final String key = getScopedAccessDenialsKey(packageName, uuid, directory);
+ return getPrefs(context).getInt(key, PERMISSION_ASK);
+ }
+
+ public static void setScopedAccessPermissionStatus(Context context, String packageName,
+ @Nullable String uuid, String directory, @PermissionStatus int status) {
+ final String key = getScopedAccessDenialsKey(packageName, uuid, directory);
+ getPrefs(context).edit().putInt(key, status).apply();
+ }
+
+ public static void clearScopedAccessPreferences(Context context, String packageName) {
+ final String keySubstring = "|" + packageName + "|";
+ final SharedPreferences prefs = getPrefs(context);
+ Editor editor = null;
+ for (final String key : prefs.getAll().keySet()) {
+ if (key.contains(keySubstring)) {
+ if (editor == null) {
+ editor = prefs.edit();
+ }
+ editor.remove(key);
+ }
+ }
+ if (editor != null) {
+ editor.apply();
+ }
+ }
+
+ private static String getScopedAccessDenialsKey(String packageName, String uuid,
+ String directory) {
+ final int userId = UserHandle.myUserId();
+ return uuid == null
+ ? userId + "|" + packageName + "||" + directory
+ : userId + "|" + packageName + "|" + uuid + "|" + directory;
+ }
+
+ /**
+ * Clears all preferences associated with a given package.
+ *
+ * <p>Typically called when a package is removed or when user asked to clear its data.
+ */
+ public static void clearPackagePreferences(Context context, String packageName) {
+ ScopedAccessLocalPreferences.clearScopedAccessPreferences(context, packageName);
+ }
+}