diff --git a/res/layout/headerless_preference_category.xml b/res/layout/headerless_preference_category.xml
new file mode 100644
index 00000000000..5fdc1a05eaa
--- /dev/null
+++ b/res/layout/headerless_preference_category.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index fb2b1e04247..d7830702bea 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3065,6 +3065,13 @@
Clear cache
Cache
+
+
+ - 1 item
+ - %d items
+
+
+ Clear access
Controls
diff --git a/res/xml/app_storage_settings.xml b/res/xml/app_storage_settings.xml
index 1620164b0b5..3faf9c8bc64 100644
--- a/res/xml/app_storage_settings.xml
+++ b/res/xml/app_storage_settings.xml
@@ -86,4 +86,16 @@
android:selectable="false"
android:layout="@layout/single_button_panel" />
+
+
+
+
+
+
diff --git a/src/com/android/settings/applications/AppStorageSettings.java b/src/com/android/settings/applications/AppStorageSettings.java
index 01780f99a00..71d16675bb2 100644
--- a/src/com/android/settings/applications/AppStorageSettings.java
+++ b/src/com/android/settings/applications/AppStorageSettings.java
@@ -22,9 +22,11 @@ import android.app.AppGlobals;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.UriPermission;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDataObserver;
-import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
@@ -37,6 +39,7 @@ import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceCategory;
import android.text.format.Formatter;
import android.util.Log;
+import android.util.MutableInt;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
@@ -49,9 +52,12 @@ import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.ApplicationsState.Callbacks;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import java.util.TreeMap;
public class AppStorageSettings extends AppInfoWithHeader
implements OnClickListener, Callbacks, DialogInterface.OnClickListener {
@@ -88,6 +94,9 @@ public class AppStorageSettings extends AppInfoWithHeader
private static final String KEY_CLEAR_DATA = "clear_data_button";
private static final String KEY_CLEAR_CACHE = "clear_cache_button";
+ private static final String KEY_URI_CATEGORY = "uri_category";
+ private static final String KEY_CLEAR_URI = "clear_uri_button";
+
private Preference mTotalSize;
private Preference mAppSize;
private Preference mDataSize;
@@ -102,6 +111,11 @@ public class AppStorageSettings extends AppInfoWithHeader
private Preference mStorageUsed;
private Button mChangeStorageButton;
+ // Views related to URI permissions
+ private Button mClearUriButton;
+ private LayoutPreference mClearUri;
+ private PreferenceCategory mUri;
+
private boolean mCanClearData = true;
private boolean mHaveSizes = false;
@@ -166,6 +180,13 @@ public class AppStorageSettings extends AppInfoWithHeader
mClearCacheButton = (Button) ((LayoutPreference) findPreference(KEY_CLEAR_CACHE))
.findViewById(R.id.button);
mClearCacheButton.setText(R.string.clear_cache_btn_text);
+
+ // URI permissions section
+ mUri = (PreferenceCategory) findPreference(KEY_URI_CATEGORY);
+ mClearUri = (LayoutPreference) mUri.findPreference(KEY_CLEAR_URI);
+ mClearUriButton = (Button) mClearUri.findViewById(R.id.button);
+ mClearUriButton.setText(R.string.clear_uri_btn_text);
+ mClearUriButton.setOnClickListener(this);
}
@Override
@@ -189,6 +210,8 @@ public class AppStorageSettings extends AppInfoWithHeader
}
} else if (v == mChangeStorageButton && mDialogBuilder != null && !isMoveInProgress()) {
mDialogBuilder.show();
+ } else if (v == mClearUriButton) {
+ clearUriPermissions();
}
}
@@ -239,7 +262,6 @@ public class AppStorageSettings extends AppInfoWithHeader
}
mClearDataButton.setEnabled(false);
mClearCacheButton.setEnabled(false);
-
} else {
mHaveSizes = true;
long codeSize = mAppEntry.codeSize;
@@ -301,6 +323,7 @@ public class AppStorageSettings extends AppInfoWithHeader
return false;
}
refreshSizeInfo();
+ refreshGrantedUriPermissions();
final VolumeInfo currentVol = getActivity().getPackageManager()
.getPackageCurrentVolume(mAppEntry.info);
@@ -413,6 +436,83 @@ public class AppStorageSettings extends AppInfoWithHeader
}
}
+ private void refreshGrantedUriPermissions() {
+ // Clear UI first (in case the activity has been resumed)
+ removeUriPermissionsFromUi();
+
+ // Gets all URI permissions from am.
+ ActivityManager am = (ActivityManager) getActivity().getSystemService(
+ Context.ACTIVITY_SERVICE);
+ List perms =
+ am.getGrantedUriPermissions(mAppEntry.info.packageName).getList();
+
+ if (perms.isEmpty()) {
+ mClearUriButton.setVisibility(View.GONE);
+ return;
+ }
+
+ PackageManager pm = getActivity().getPackageManager();
+
+ // Group number of URIs by app.
+ Map uriCounters = new TreeMap<>();
+ for (UriPermission perm : perms) {
+ String authority = perm.getUri().getAuthority();
+ ProviderInfo provider = pm.resolveContentProvider(authority, 0);
+ CharSequence app = provider.applicationInfo.loadLabel(pm);
+ MutableInt count = uriCounters.get(app);
+ if (count == null) {
+ uriCounters.put(app, new MutableInt(1));
+ } else {
+ count.value++;
+ }
+ }
+
+ // Dynamically add the preferences, one per app.
+ int order = 0;
+ for (Map.Entry entry : uriCounters.entrySet()) {
+ int numberResources = entry.getValue().value;
+ Preference pref = new Preference(getPrefContext());
+ pref.setTitle(entry.getKey());
+ pref.setSummary(getPrefContext().getResources()
+ .getQuantityString(R.plurals.uri_permissions_text, numberResources,
+ numberResources));
+ pref.setSelectable(false);
+ pref.setLayoutResource(R.layout.horizontal_preference);
+ pref.setOrder(order);
+ Log.v(TAG, "Adding preference '" + pref + "' at order " + order);
+ mUri.addPreference(pref);
+ }
+
+ if (mAppControlRestricted) {
+ mClearUriButton.setEnabled(false);
+ }
+
+ mClearUri.setOrder(order);
+ mClearUriButton.setVisibility(View.VISIBLE);
+
+ }
+
+ private void clearUriPermissions() {
+ // Synchronously revoke the permissions.
+ final ActivityManager am = (ActivityManager) getActivity().getSystemService(
+ Context.ACTIVITY_SERVICE);
+ am.clearGrantedUriPermissions(mAppEntry.info.packageName);
+
+ // Update UI
+ refreshGrantedUriPermissions();
+ }
+
+ private void removeUriPermissionsFromUi() {
+ // Remove all preferences but the clear button.
+ int count = mUri.getPreferenceCount();
+ for (int i = count - 1; i >= 0; i--) {
+ Preference pref = mUri.getPreference(i);
+ if (pref != mClearUri) {
+ mUri.removePreference(pref);
+ }
+ }
+ }
+
@Override
protected AlertDialog createDialog(int id, int errorCode) {
switch (id) {