diff options
-rw-r--r-- | res/layout/preference_usage.xml | 27 | ||||
-rw-r--r-- | res/layout/title_summary_image_view.xml (renamed from res/layout/summary_image_view.xml) | 4 | ||||
-rw-r--r-- | res/values/dimens.xml | 2 | ||||
-rw-r--r-- | res/values/strings.xml | 16 | ||||
-rw-r--r-- | src/com/android/packageinstaller/permission/ui/handheld/ExpandablePreferenceGroup.java | 10 | ||||
-rw-r--r-- | src/com/android/packageinstaller/permission/ui/handheld/PermissionControlPreference.java | 38 | ||||
-rw-r--r-- | src/com/android/packageinstaller/permission/ui/handheld/PermissionUsageFragment.java | 161 |
7 files changed, 192 insertions, 66 deletions
diff --git a/res/layout/preference_usage.xml b/res/layout/preference_usage.xml index 348dc353..a1eb90f3 100644 --- a/res/layout/preference_usage.xml +++ b/res/layout/preference_usage.xml @@ -38,13 +38,28 @@ android:paddingBottom="16dp" android:orientation="vertical"> - <TextView - android:id="@android:id/title" - android:layout_width="wrap_content" + <LinearLayout + android:layout_width="match_parent" android:layout_height="wrap_content" - android:singleLine="true" - android:textAppearance="@style/Preference_TextAppearanceMaterialSubhead" - android:ellipsize="marquee"/> + android:orientation="horizontal"> + + <LinearLayout + android:id="@+id/title_widget_frame" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:gravity="start|center_vertical" + android:paddingEnd="16dp" + android:orientation="horizontal"/> + + <TextView + android:id="@android:id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="true" + android:textAppearance="@style/Preference_TextAppearanceMaterialSubhead" + android:ellipsize="marquee"/> + + </LinearLayout> <LinearLayout android:layout_width="match_parent" diff --git a/res/layout/summary_image_view.xml b/res/layout/title_summary_image_view.xml index 4caea0fe..8b3a6641 100644 --- a/res/layout/summary_image_view.xml +++ b/res/layout/title_summary_image_view.xml @@ -22,8 +22,8 @@ <com.android.packageinstaller.permission.ui.PreferenceImageView android:id="@+id/icon" - android:layout_width="@dimen/preference_usage_summary_icon_size" - android:layout_height="@dimen/preference_usage_summary_icon_size" + android:layout_width="@dimen/preference_usage_title_summary_icon_size" + android:layout_height="@dimen/preference_usage_title_summary_icon_size" android:layout_marginEnd="4dp" android:tint="?android:attr/colorControlNormal"/> diff --git a/res/values/dimens.xml b/res/values/dimens.xml index c6f2a7e6..49f25574 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -54,7 +54,7 @@ <dimen name="app_permission_divider_margin_top">12dp</dimen> <dimen name="app_permission_divider_margin_bottom">8dp</dimen> - <dimen name="preference_usage_summary_icon_size">16dp</dimen> + <dimen name="preference_usage_title_summary_icon_size">16dp</dimen> <dimen name="grant_permissions_dialog_button_height">56dp</dimen> </resources> diff --git a/res/values/strings.xml b/res/values/strings.xml index 7f1dc5e5..1a11beb2 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -255,14 +255,11 @@ <!-- Title for permission usage [CHAR LIMIT=30] --> <string name="permission_usage_title">Permissions usage</string> - <!-- Summary for showing a single permission access and the time of the last access [CHAR LIMIT=80] --> - <string name="permission_usage_summary_last_access"><xliff:g id="perm" example="Location">%1$s</xliff:g> - Last access <xliff:g id="time" example="2 hours">%2$s</xliff:g> ago</string> - <!-- Summary for showing a single permission access and the number of accesses [CHAR LIMIT=80] --> - <string name="permission_usage_summary_num_accesses"><xliff:g id="perm" example="Location">%1$s</xliff:g> - <xliff:g id="num" example="42">%2$s</xliff:g> accesses</string> + <string name="permission_usage_summary">Last access: <xliff:g id="time" example="12:10 PM">%1$s</xliff:g>\n<xliff:g id="num" example="42">%2$s</xliff:g> accesses</string> <!-- Summary for showing a single permission access and the number of accesses, including those in the background [CHAR LIMIT=80] --> - <string name="permission_usage_summary_num_accesses_background"><xliff:g id="perm" example="Location">%1$s</xliff:g> - <xliff:g id="num" example="42">%2$s</xliff:g> accesses (<xliff:g id="num" example="7">%3$s</xliff:g> in background)</string> + <string name="permission_usage_summary_background">Last access: <xliff:g id="time" example="12:10 PM">%1$s</xliff:g>\n<xliff:g id="num" example="42">%2$s</xliff:g> accesses (<xliff:g id="num" example="7">%3$s</xliff:g> in background)</string> <!-- Description for showing permission accesses with any permission [CHAR LIMIT=30] --> <string name="permission_usage_any_permission">Any permission</string> @@ -282,6 +279,9 @@ <!-- Description for showing permissions accessed in the last 15 minutes [CHAR LIMIT=30] --> <string name="permission_usage_last_15_minutes">Last 15 minutes</string> + <!-- Description for showing permissions accessed in the last minute [CHAR LIMIT=30] --> + <string name="permission_usage_last_minute">Last 1 minute</string> + <!-- Label when no apps have used the requested permissions [CHAR LIMIT=30] --> <string name="no_permission_usages">No permission usages</string> @@ -300,6 +300,9 @@ <!-- Label for the title of the list of permission usages that shows which apps used which permissions[CHAR LIMIT=50] --> <string name="permission_usage_list_title_last_15_minutes">Access in last 15 minutes</string> + <!-- Label for the title of the list of permission usages that shows which apps used which permissions[CHAR LIMIT=50] --> + <string name="permission_usage_list_title_last_minute">Access in last 1 minute</string> + <!-- Label for the title of the permission bar chart showing how often the most common permissions are used [CHAR LIMIT=50] --> <string name="permission_usage_bar_chart_title_any_time">Top permission usage at any time</string> @@ -315,6 +318,9 @@ <!-- Label for the title of the permission bar chart showing how often the most common permissions are used [CHAR LIMIT=50] --> <string name="permission_usage_bar_chart_title_last_15_minutes">Top permission usage in last 15 minutes</string> + <!-- Label for the title of the permission bar chart showing how often the most common permissions are used [CHAR LIMIT=50] --> + <string name="permission_usage_bar_chart_title_last_minute">Top permission usage in last 1 minute</string> + <!-- Label for the bars on the chart that shows how many apps have used various permissions [CHAR LIMIT=10] --> <string name="app_permission_usage_bar_label">Apps</string> diff --git a/src/com/android/packageinstaller/permission/ui/handheld/ExpandablePreferenceGroup.java b/src/com/android/packageinstaller/permission/ui/handheld/ExpandablePreferenceGroup.java index 1dac8508..333df47e 100644 --- a/src/com/android/packageinstaller/permission/ui/handheld/ExpandablePreferenceGroup.java +++ b/src/com/android/packageinstaller/permission/ui/handheld/ExpandablePreferenceGroup.java @@ -87,21 +87,21 @@ public class ExpandablePreferenceGroup extends PreferenceGroup { holder.setDividerAllowedAbove(false); holder.setDividerAllowedBelow(false); + holder.findViewById(R.id.title_widget_frame).setVisibility(View.GONE); + ViewGroup summaryFrame = (ViewGroup) holder.findViewById(R.id.summary_widget_frame); if (mSummaryIcons.isEmpty()) { summaryFrame.setVisibility(View.GONE); } else { summaryFrame.removeAllViews(); - int summaryIconSize = mContext.getResources().getDimensionPixelSize( - R.dimen.preference_usage_summary_icon_size); int numIcons = mSummaryIcons.size(); for (int i = 0; i < numIcons; i++) { LayoutInflater inflater = mContext.getSystemService(LayoutInflater.class); - ViewGroup layoutView = (ViewGroup) inflater.inflate(R.layout.summary_image_view, + ViewGroup group = (ViewGroup) inflater.inflate(R.layout.title_summary_image_view, null); - ImageView imageView = layoutView.requireViewById(R.id.icon); + ImageView imageView = group.requireViewById(R.id.icon); imageView.setImageResource(mSummaryIcons.get(i)); - summaryFrame.addView(layoutView); + summaryFrame.addView(group); } } } diff --git a/src/com/android/packageinstaller/permission/ui/handheld/PermissionControlPreference.java b/src/com/android/packageinstaller/permission/ui/handheld/PermissionControlPreference.java index da80c2a1..97b9cd42 100644 --- a/src/com/android/packageinstaller/permission/ui/handheld/PermissionControlPreference.java +++ b/src/com/android/packageinstaller/permission/ui/handheld/PermissionControlPreference.java @@ -45,6 +45,7 @@ public class PermissionControlPreference extends Preference { private @Nullable Drawable mWidgetIcon; private boolean mUseSmallerIcon; private boolean mEllipsizeEnd; + private @Nullable List<Integer> mTitleIcons; private @Nullable List<Integer> mSummaryIcons; public PermissionControlPreference(@NonNull Context context, @@ -54,6 +55,7 @@ public class PermissionControlPreference extends Preference { mWidgetIcon = null; mUseSmallerIcon = false; mEllipsizeEnd = false; + mTitleIcons = null; mSummaryIcons = null; setOnPreferenceClickListener(preference -> { Intent intent = new Intent(context, AppPermissionActivity.class); @@ -112,6 +114,16 @@ public class PermissionControlPreference extends Preference { } /** + * Sets this preference to show the given icons to the left of its title. + * + * @param titleIcons the icons to show. + */ + public void setTitleIcons(@NonNull List<Integer> titleIcons) { + mTitleIcons = titleIcons; + setLayoutResource(R.layout.preference_usage); + } + + /** * Sets this preference to show the given icons to the left of its summary. * * @param summaryIcons the icons to show. @@ -144,20 +156,26 @@ public class PermissionControlPreference extends Preference { title.setEllipsize(TextUtils.TruncateAt.END); } - if (mSummaryIcons != null && !mSummaryIcons.isEmpty()) { - ViewGroup summaryFrame = (ViewGroup) holder.findViewById(R.id.summary_widget_frame); - summaryFrame.removeAllViews(); - int summaryIconSize = mContext.getResources().getDimensionPixelSize( - R.dimen.preference_usage_summary_icon_size); - int numIcons = mSummaryIcons.size(); + setIcons(holder, mSummaryIcons, R.id.summary_widget_frame); + setIcons(holder, mTitleIcons, R.id.title_widget_frame); + } + + private void setIcons(PreferenceViewHolder holder, @Nullable List<Integer> icons, int frameId) { + ViewGroup frame = (ViewGroup) holder.findViewById(frameId); + if (icons != null && !icons.isEmpty()) { + frame.setVisibility(View.VISIBLE); + frame.removeAllViews(); + int numIcons = icons.size(); for (int i = 0; i < numIcons; i++) { LayoutInflater inflater = mContext.getSystemService(LayoutInflater.class); - ViewGroup layoutView = (ViewGroup) inflater.inflate(R.layout.summary_image_view, + ViewGroup group = (ViewGroup) inflater.inflate(R.layout.title_summary_image_view, null); - ImageView imageView = layoutView.requireViewById(R.id.icon); - imageView.setImageResource(mSummaryIcons.get(i)); - summaryFrame.addView(layoutView); + ImageView imageView = group.requireViewById(R.id.icon); + imageView.setImageResource(icons.get(i)); + frame.addView(group); } + } else if (frame != null) { + frame.setVisibility(View.GONE); } } } diff --git a/src/com/android/packageinstaller/permission/ui/handheld/PermissionUsageFragment.java b/src/com/android/packageinstaller/permission/ui/handheld/PermissionUsageFragment.java index 9485d2d6..2e4aada1 100644 --- a/src/com/android/packageinstaller/permission/ui/handheld/PermissionUsageFragment.java +++ b/src/com/android/packageinstaller/permission/ui/handheld/PermissionUsageFragment.java @@ -28,6 +28,7 @@ import android.content.Context; import android.content.Intent; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.text.format.DateFormat; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; @@ -68,6 +69,7 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Set; /** @@ -81,11 +83,11 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements private static final String LOG_TAG = "PermissionUsageFragment"; @Retention(SOURCE) - @IntDef(value = {SORT_MOST_PERMISSIONS, SORT_MOST_ACCESSES, SORT_RECENT}) + @IntDef(value = {SORT_RECENT, SORT_MOST_PERMISSIONS, SORT_MOST_ACCESSES}) @interface SortOption {} - static final int SORT_MOST_PERMISSIONS = 1; - static final int SORT_MOST_ACCESSES = 2; - static final int SORT_RECENT = 3; + static final int SORT_RECENT = 1; + static final int SORT_MOST_PERMISSIONS = 2; + static final int SORT_MOST_ACCESSES = 3; private static final int MENU_FILTER_BY_PERMISSIONS = MENU_HIDE_SYSTEM + 1; @@ -232,16 +234,20 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements context.getString(R.string.permission_usage_last_15_minutes), R.string.permission_usage_bar_chart_title_last_15_minutes, R.string.permission_usage_list_title_last_15_minutes)); + mFilterAdapterTime.addFilter(new TimeFilterItem(MINUTES.toMillis(1), + context.getString(R.string.permission_usage_last_minute), + R.string.permission_usage_bar_chart_title_last_minute, + R.string.permission_usage_list_title_last_minute)); mFilterSpinnerTime.setSelection(mSavedTimeSpinnerIndex); // Add sort spinner entries. + mSortAdapter.addFilter(new SortItem(context.getString(R.string.sort_spinner_recent), + SORT_RECENT)); mSortAdapter.addFilter( new SortItem(context.getString(R.string.sort_spinner_most_permissions), SORT_MOST_PERMISSIONS)); mSortAdapter.addFilter(new SortItem(context.getString(R.string.sort_spinner_most_accesses), SORT_MOST_ACCESSES)); - mSortAdapter.addFilter(new SortItem(context.getString(R.string.sort_spinner_recent), - SORT_RECENT)); mSortSpinner.setSelection(mSavedSortSpinnerIndex); return root; @@ -378,6 +384,9 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements mHasSystemApps = false; + java.text.DateFormat timeFormat = DateFormat.getTimeFormat(context); + java.text.DateFormat dateFormat = DateFormat.getMediumDateFormat(context); + final int numApps = appPermissionUsages.size(); for (int appNum = 0; appNum < numApps; appNum++) { final AppPermissionUsage appPermissionUsage = appPermissionUsages.get(appNum); @@ -415,23 +424,31 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements if (mFilterGroup != null && !mFilterGroup.equals(groupUsage.getGroup().getName())) { continue; } - // Ignore {READ,WRITE}_EXTERNAL_STORAGE since they're going away. + // STOPSHIP: Ignore {READ,WRITE}_EXTERNAL_STORAGE since they're going away. if (groupUsage.getGroup().getLabel().equals("Storage")) { continue; } if (groupUsage.getAccessCount() > 0) { + String accessTimeString = null; + if (isToday(groupUsage.getLastAccessTime())) { + accessTimeString = timeFormat.format(groupUsage.getLastAccessTime()); + } else { + accessTimeString = dateFormat.format(groupUsage.getLastAccessTime()); + } + permissionPrefs.add(createPermissionUsagePreference(context, - appPermissionUsage, groupUsage, sortOption)); + appPermissionUsage, groupUsage, accessTimeString)); } } - PreferenceGroup parent = category; - if (permissionPrefs.size() > 1) { - // Add a "parent" entry for the app that will expand to the individual entries. - parent = createExpandablePreferenceGroup(context, appPermissionUsage); - category.addPreference(parent); + if (permissionPrefs.isEmpty()) { + continue; } + // Add a "parent" entry for the app that will expand to the individual entries. + PreferenceGroup parent = createExpandablePreferenceGroup(context, appPermissionUsage); + category.addPreference(parent); + final int permissionPrefCount = permissionPrefs.size(); for (int i = 0; i < permissionPrefCount; i++) { final PermissionControlPreference permissionPref = permissionPrefs.get(i); @@ -515,6 +532,10 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements if (groupUsage.getAccessCount() <= 0) { continue; } + // STOPSHIP: Ignore {READ,WRITE}_EXTERNAL_STORAGE since they're going away. + if (groupUsage.getGroup().getLabel().equals("Storage")) { + continue; + } final Integer count = groupToAppCount.get(groupUsage.getGroup().getName()); if (count == null) { groups.add(groupUsage.getGroup()); @@ -571,6 +592,10 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements final int permissionUsageCount = groupUsages.size(); for (int i = 0; i < permissionUsageCount; i++) { final AppPermissionUsage.GroupUsage groupUsage = groupUsages.get(i); + // STOPSHIP: Ignore {READ,WRITE}_EXTERNAL_STORAGE since they're going away. + if (groupUsage.getGroup().getLabel().equals("Storage")) { + continue; + } if (groupUsage.getAccessCount() > 0) { permissionIcons.add(groupUsage.getGroup().getIconResId()); } @@ -587,36 +612,30 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements * @param context the context * @param appPermissionUsage the permission usage for the app * @param groupUsage the permission item to add - * @param sortOption how the entries should be sorted + * @param accessTimeStr the string representing the access time * * @return the Preference */ private PermissionControlPreference createPermissionUsagePreference(@NonNull Context context, @NonNull AppPermissionUsage appPermissionUsage, - @NonNull GroupUsage groupUsage, @SortOption int sortOption) { + @NonNull GroupUsage groupUsage, @NonNull String accessTimeStr) { final PermissionControlPreference pref = new PermissionControlPreference(context, groupUsage.getGroup()); - pref.setTitle(appPermissionUsage.getApp().getLabel()); final AppPermissionGroup group = groupUsage.getGroup(); - if (sortOption == SORT_MOST_ACCESSES) { - if (groupUsage.getBackgroundAccessCount() == 0) { - pref.setSummary( - context.getString(R.string.permission_usage_summary_num_accesses, - group.getLabel(), groupUsage.getForegroundAccessCount())); - } else { - pref.setSummary( - context.getString( - R.string.permission_usage_summary_num_accesses_background, - group.getLabel(), groupUsage.getAccessCount(), - groupUsage.getBackgroundAccessCount())); - } + pref.setTitle(group.getLabel()); + if (groupUsage.getBackgroundAccessCount() == 0) { + pref.setSummary( + context.getString(R.string.permission_usage_summary, + accessTimeStr, groupUsage.getForegroundAccessCount())); } else { - pref.setSummary(context.getString(R.string.permission_usage_summary_last_access, - group.getLabel(), Utils.getTimeDiffStr(context, System.currentTimeMillis() - - groupUsage.getLastAccessTime()))); + pref.setSummary( + context.getString( + R.string.permission_usage_summary_background, + accessTimeStr, groupUsage.getAccessCount(), + groupUsage.getBackgroundAccessCount())); } - pref.setSummaryIcons(Collections.singletonList(group.getIconResId())); + pref.setTitleIcons(Collections.singletonList(group.getIconResId())); pref.setKey(group.getApp().packageName + "," + group.getName()); pref.useSmallerIcon(); return pref; @@ -653,6 +672,10 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements final int groupCount = groupUsages.size(); for (int i = 0; i < groupCount; i++) { final GroupUsage groupUsage = groupUsages.get(i); + // STOPSHIP: Ignore {READ,WRITE}_EXTERNAL_STORAGE since they're going away. + if (groupUsage.getGroup().getLabel().equals("Storage")) { + continue; + } if (groupUsage.getAccessCount() > 0) { accessedCount++; } @@ -692,7 +715,7 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements */ private static int compareAccessTime(@NonNull AppPermissionUsage x, @NonNull AppPermissionUsage y) { - final int timeDiff = compareLong(x.getLastAccessTime(), y.getLastAccessTime()); + final int timeDiff = compareLong(getLastAccessTime(x), getLastAccessTime(y)); if (timeDiff != 0) { return timeDiff; } @@ -701,6 +724,29 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements } /** + * Gets the last time the given app used a permission. + * + * @param appPermissionUsage The app permission usage. + * + * @return The last access time. + */ + private static long getLastAccessTime(@NonNull AppPermissionUsage appPermissionUsage) { + long lastAccessTime = 0; + final List<GroupUsage> groupUsages = appPermissionUsage.getGroupUsages(); + final int groupCount = groupUsages.size(); + for (int i = 0; i < groupCount; i++) { + final GroupUsage groupUsage = groupUsages.get(i); + // STOPSHIP: Ignore {READ,WRITE}_EXTERNAL_STORAGE since they're going away. + // We can replace this with AppPermissionUsage.getLastAccessTime then. + if (groupUsage.getGroup().getLabel().equals("Storage")) { + continue; + } + lastAccessTime = Math.max(lastAccessTime, groupUsage.getLastAccessTime()); + } + return lastAccessTime; + } + + /** * Compare two AppPermissionUsage by their access count. * * Can be used as a {@link java.util.Comparator}. @@ -712,12 +758,11 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements */ private static int compareAccessCount(@NonNull AppPermissionUsage x, @NonNull AppPermissionUsage y) { - final int accessDiff = compareLong(x.getAccessCount(), y.getAccessCount()); + final int accessDiff = compareLong(getAccessCount(x), getAccessCount(y)); if (accessDiff != 0) { return accessDiff; } - // Make sure we lose no data if same - return y.hashCode() - x.hashCode(); + return compareAccessTime(x, y); } /** @@ -741,6 +786,29 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements } /** + * Gets the number of permission usages. + * + * @param appPermissionUsage The app permission usage. + * + * @return The number of permission usages. + */ + private static long getAccessCount(@NonNull AppPermissionUsage appPermissionUsage) { + long accessCount = 0; + final List<GroupUsage> groupUsages = appPermissionUsage.getGroupUsages(); + final int groupCount = groupUsages.size(); + for (int i = 0; i < groupCount; i++) { + final GroupUsage groupUsage = groupUsages.get(i); + // STOPSHIP: Ignore {READ,WRITE}_EXTERNAL_STORAGE since they're going away. + // We can replace this with AppPermissionUsage.getAccessCount then. + if (groupUsage.getGroup().getLabel().equals("Storage")) { + continue; + } + accessCount += groupUsage.getAccessCount(); + } + return accessCount; + } + + /** * Compare two longs. * * Can be used as a {@link java.util.Comparator}. @@ -775,7 +843,7 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements if (timeDiff != 0) { return timeDiff; } - final int countDiff = y.getGroupUsages().size() - x.getGroupUsages().size(); + final int countDiff = compareAccessUsage(x, y); if (countDiff != 0) { return countDiff; } @@ -810,6 +878,25 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements } /** + * Check whether the given time (in milliseconds) is in the current day. + * + * @param time the time in milliseconds + * + * @return whether the given time is in the current day. + */ + private static boolean isToday(long time) { + Calendar today = Calendar.getInstance(Locale.getDefault()); + today.setTimeInMillis(System.currentTimeMillis()); + today.set(Calendar.HOUR, 0); + today.set(Calendar.MINUTE, 0); + today.set(Calendar.SECOND, 0); + + Calendar date = Calendar.getInstance(Locale.getDefault()); + date.setTimeInMillis(time); + return date.after(today); + } + + /** * Show a dialog that allows selecting a permission group by which to filter the entries. */ private void showPermissionFilterDialog() { |