Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 70ffb3af authored by Joel Galenson's avatar Joel Galenson
Browse files

Only coalesce entries if they have the same timestamp.

When sorting the Permissions Hub by recency, we now only coalesce
entries if they have the same (absolute) timestamp.  Thus instead of
grouping all accesses by the same app together, we now split them up
and only group them if no other events happened in between.  We also
show the timestamp in the summary of the group.

Also do not count system apps in the bar chart unless the user has
chose to show system apps.

All of this actually cleans up the code a bit, which is nice.

Bug: 63532550
Test: Try different sort filters.
Change-Id: Ie09d8c878c3bf4ab7231fd493cb65bda8ed031b8
parent 43b41aaf
Loading
Loading
Loading
Loading
+12 −3
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
@@ -41,13 +42,12 @@ public class ExpandablePreferenceGroup extends PreferenceGroup {
    private @NonNull List<Integer> mSummaryIcons;
    private boolean mExpanded;

    public ExpandablePreferenceGroup(@NonNull Context context,
            @NonNull List<Integer> summaryIcons) {
    public ExpandablePreferenceGroup(@NonNull Context context) {
        super(context, null);

        mContext = context;
        mPreferences = new ArrayList<>();
        mSummaryIcons = summaryIcons;
        mSummaryIcons = new ArrayList<>();
        mExpanded = false;

        setLayoutResource(R.layout.preference_usage);
@@ -111,4 +111,13 @@ public class ExpandablePreferenceGroup extends PreferenceGroup {
        mPreferences.add(preference);
        return true;
    }

    /**
     * Show the given icon next to this preference's summary.
     *
     * @param resId the the resourceId of the drawable to use as the icon.
     */
    public void addSummaryIcon(@DrawableRes int resId) {
        mSummaryIcons.add(resId);
    }
}
+110 −203
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import android.text.format.DateFormat;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -48,7 +49,6 @@ import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.fragment.app.DialogFragment;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;

import com.android.packageinstaller.permission.model.AppPermissionGroup;
@@ -357,13 +357,45 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements
        }
        screen.removeAll();

        // Update bar chart
        mHasSystemApps = false;

        List<Pair<AppPermissionUsage, GroupUsage>> usages = new ArrayList<>();
        int numApps = appPermissionUsages.size();
        for (int appNum = 0; appNum < numApps; appNum++) {
            AppPermissionUsage appUsage = appPermissionUsages.get(appNum);
            List<GroupUsage> appGroups = appUsage.getGroupUsages();
            int numGroups = appGroups.size();
            for (int groupNum = 0; groupNum < numGroups; groupNum++) {
                GroupUsage groupUsage = appGroups.get(groupNum);

                if (groupUsage.getAccessCount() <= 0) {
                    continue;
                }
                final boolean isSystemApp = Utils.isSystem(appUsage.getApp(), mLauncherPkgs);
                if (!mHasSystemApps) {
                    if (isSystemApp) {
                        mHasSystemApps = true;
                        getActivity().invalidateOptionsMenu();
                    }
                }
                if (isSystemApp && !mShowSystem) {
                    continue;
                }
                // STOPSHIP: Ignore {READ,WRITE}_EXTERNAL_STORAGE since they're going away.
                if (groupUsage.getGroup().getLabel().equals("Storage")) {
                    continue;
                }

                usages.add(Pair.create(appUsage, appGroups.get(groupNum)));
            }
        }

        // Update bar chart.
        final TimeFilterItem timeFilterItem = getSelectedFilterItem();
        final BarChartPreference barChart = createBarChart(appPermissionUsages,
                timeFilterItem, context);
        final BarChartPreference barChart = createBarChart(usages, timeFilterItem, context);
        screen.addPreference(barChart);

        // Add the preferences.
        // Add the preference header.
        PreferenceCategory category = new PreferenceCategory(context);
        screen.addPreference(category);
        if (timeFilterItem != null) {
@@ -372,63 +404,32 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements

        // Sort the apps.
        final int sortOption = getSelectedSortOption();
        if (sortOption == SORT_MOST_PERMISSIONS) {
            appPermissionUsages.sort(PermissionUsageFragment::compareAccessUsage);
        if (sortOption == SORT_RECENT) {
            usages.sort(PermissionUsageFragment::compareAccessRecency);
        } else if (sortOption == SORT_MOST_PERMISSIONS) {
            usages.sort(PermissionUsageFragment::compareAccessUsage);
        } else if (sortOption == SORT_MOST_ACCESSES) {
            appPermissionUsages.sort(PermissionUsageFragment::compareAccessCount);
        } else if (sortOption == SORT_RECENT) {
            appPermissionUsages.sort(PermissionUsageFragment::compareAccessRecency);
            usages.sort(PermissionUsageFragment::compareAccessCount);
        } else {
            Log.w(LOG_TAG, "Unexpected sort option: " + sortOption);
        }

        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);

            if (appPermissionUsage.getAccessCount() <= 0) {
                continue;
            }

            final boolean isSystemApp = Utils.isSystem(appPermissionUsage.getApp(),
                    mLauncherPkgs);
            if (!mHasSystemApps) {
                if (isSystemApp) {
                    mHasSystemApps = true;
                    getActivity().invalidateOptionsMenu();
                }
            }
            if (isSystemApp && !mShowSystem) {
                continue;
            }

            if (sortOption == SORT_MOST_ACCESSES) {
                appPermissionUsage.getGroupUsages().sort(
                        PermissionUsageFragment::compareAccessCount);
            } else {
                appPermissionUsage.getGroupUsages().sort(
                        PermissionUsageFragment::compareAccessTime);
            }
        ExpandablePreferenceGroup parent = null;
        AppPermissionUsage lastAppPermissionUsage = null;

            final List<GroupUsage> appGroups = appPermissionUsage.getGroupUsages();
        final int numUsages = usages.size();
        for (int usageNum = 0; usageNum < numUsages; usageNum++) {
            final Pair<AppPermissionUsage, GroupUsage> usage = usages.get(usageNum);
            AppPermissionUsage appPermissionUsage = usage.first;
            GroupUsage groupUsage = usage.second;

            final List<PermissionControlPreference> permissionPrefs = new ArrayList<>();
            final int numGroups = appGroups.size();
            for (int groupNum = 0; groupNum < numGroups; groupNum++) {
                final GroupUsage groupUsage = appGroups.get(groupNum);
            if (mFilterGroup != null && !mFilterGroup.equals(groupUsage.getGroup().getName())) {
                continue;
            }
                // 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());
@@ -436,27 +437,17 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements
                accessTimeString = dateFormat.format(groupUsage.getLastAccessTime());
            }

                    permissionPrefs.add(createPermissionUsagePreference(context,
                            appPermissionUsage, groupUsage, accessTimeString));
                }
            }

            if (permissionPrefs.isEmpty()) {
                continue;
            }

            if (lastAppPermissionUsage != appPermissionUsage) {
                // Add a "parent" entry for the app that will expand to the individual entries.
            PreferenceGroup parent = createExpandablePreferenceGroup(context, appPermissionUsage);
                parent = createExpandablePreferenceGroup(context, appPermissionUsage,
                        sortOption == SORT_RECENT ? accessTimeString : null);
                category.addPreference(parent);

            final int permissionPrefCount = permissionPrefs.size();
            for (int i = 0; i < permissionPrefCount; i++) {
                final PermissionControlPreference permissionPref = permissionPrefs.get(i);
                if (permissionPrefs.size() == 1) {
                    permissionPref.setIcon(appPermissionUsage.getApp().getIcon());
                }
                parent.addPreference(permissionPrefs.get(i));
                lastAppPermissionUsage = appPermissionUsage;
            }

            parent.addPreference(createPermissionUsagePreference(context, appPermissionUsage,
                    groupUsage, accessTimeString));
            parent.addSummaryIcon(groupUsage.getGroup().getIconResId());
        }
    }

@@ -497,13 +488,14 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements
    /**
     * Create a bar chart showing the permissions that are used by the most apps.
     *
     * @param appPermissionUsages app permission usages
     * @param usages the usages
     * @param timeFilterItem the time filter, or null if no filter is set
     * @param context the context
     *
     * @return the Preference representing the bar chart
     */
    private BarChartPreference createBarChart(@NonNull List<AppPermissionUsage> appPermissionUsages,
    private BarChartPreference createBarChart(
            @NonNull List<Pair<AppPermissionUsage, GroupUsage>> usages,
            @Nullable TimeFilterItem timeFilterItem, @NonNull Context context) {
        BarChartInfo.Builder builder = new BarChartInfo.Builder();
        BarChartPreference barChart = new BarChartPreference(context, null);
@@ -521,21 +513,10 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements

        final ArrayList<AppPermissionGroup> groups = new ArrayList<>();
        final ArrayMap<String, Integer> groupToAppCount = new ArrayMap<>();
        final int appCount = appPermissionUsages.size();
        for (int i = 0; i < appCount; i++) {
            final AppPermissionUsage appPermissionUsage = appPermissionUsages.get(i);
            final List<AppPermissionUsage.GroupUsage> groupUsages =
                    appPermissionUsage.getGroupUsages();
            final int groupCount = groupUsages.size();
            for (int j = 0; j < groupCount; j++) {
                final GroupUsage groupUsage = groupUsages.get(j);
                if (groupUsage.getAccessCount() <= 0) {
                    continue;
                }
                // STOPSHIP: Ignore {READ,WRITE}_EXTERNAL_STORAGE since they're going away.
                if (groupUsage.getGroup().getLabel().equals("Storage")) {
                    continue;
                }
        final int usageCount = usages.size();
        for (int i = 0; i < usageCount; i++) {
            final Pair<AppPermissionUsage, GroupUsage> usage = usages.get(i);
            GroupUsage groupUsage = usage.second;
            final Integer count = groupToAppCount.get(groupUsage.getGroup().getName());
            if (count == null) {
                groups.add(groupUsage.getGroup());
@@ -544,7 +525,6 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements
                groupToAppCount.put(groupUsage.getGroup().getName(), count + 1);
            }
        }
        }

        groups.sort((x, y) -> {
            final int usageDiff = compareLong(groupToAppCount.get(x.getName()),
@@ -585,24 +565,14 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements
     *
     * @return the expandable preference group.
     */
    private PreferenceGroup createExpandablePreferenceGroup(@NonNull Context context,
            @NonNull AppPermissionUsage appPermissionUsage) {
        final List<GroupUsage> groupUsages = appPermissionUsage.getGroupUsages();
        final List<Integer> permissionIcons = new ArrayList<>(groupUsages.size());
        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());
            }
        }
        PreferenceGroup preference = new ExpandablePreferenceGroup(context, permissionIcons);
    private ExpandablePreferenceGroup createExpandablePreferenceGroup(@NonNull Context context,
            @NonNull AppPermissionUsage appPermissionUsage, @Nullable String summaryString) {
        ExpandablePreferenceGroup preference = new ExpandablePreferenceGroup(context);
        preference.setTitle(appPermissionUsage.getApp().getLabel());
        preference.setIcon(appPermissionUsage.getApp().getIcon());
        if (summaryString != null) {
            preference.setSummary(summaryString);
        }
        return preference;
    }

@@ -642,22 +612,24 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements
    }

    /**
     * Compare two AppPermissionUsage by their permission usage.
     * Compare two usages by the number of apps that accessed each group.
     *
     * Can be used as a {@link java.util.Comparator}.
     *
     * @param x an AppPermissionUsage.
     * @param y an AppPermissionUsage.
     * We ignore the GroupUsage here to ensure that we keep all usages by the same app together.
     *
     * @param x a usage.
     * @param y a usage.
     *
     * @return see {@link java.util.Comparator#compare(Object, Object)}.
     */
    private static int compareAccessUsage(@NonNull AppPermissionUsage x,
            @NonNull AppPermissionUsage y) {
        final int groupDiff = getAccessedGroupCount(y) - getAccessedGroupCount(x);
    private static int compareAccessUsage(@NonNull Pair<AppPermissionUsage, GroupUsage> x,
            @NonNull Pair<AppPermissionUsage, GroupUsage> y) {
        final int groupDiff = getAccessedGroupCount(y.first) - getAccessedGroupCount(x.first);
        if (groupDiff != 0) {
            return groupDiff;
        }
        return compareAccessTime(x, y);
        return compareAccessTime(x.first, y.first);
    }

    /**
@@ -684,18 +656,19 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements
    }

    /**
     * Compare two AppPermissionUsage by their access time.
     * Compare two usages by their access time.
     *
     * Can be used as a {@link java.util.Comparator}.
     *
     * @param x an AppPermissionUsage.
     * @param y an AppPermissionUsage.
     * @param x a usage.
     * @param y a usage.
     *
     * @return see {@link java.util.Comparator#compare(Object, Object)}.
     */
    private static int compareAccessTime(@NonNull AppPermissionUsage.GroupUsage x,
            @NonNull AppPermissionUsage.GroupUsage y) {
        final int timeDiff = compareLong(x.getLastAccessTime(), y.getLastAccessTime());
    private static int compareAccessTime(@NonNull Pair<AppPermissionUsage, GroupUsage> x,
            @NonNull Pair<AppPermissionUsage, GroupUsage> y) {
        final int timeDiff = compareLong(x.second.getLastAccessTime(),
                y.second.getLastAccessTime());
        if (timeDiff != 0) {
            return timeDiff;
        }
@@ -715,7 +688,7 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements
     */
    private static int compareAccessTime(@NonNull AppPermissionUsage x,
            @NonNull AppPermissionUsage y) {
        final int timeDiff = compareLong(getLastAccessTime(x), getLastAccessTime(y));
        final int timeDiff = compareLong(x.getLastAccessTime(), y.getLastAccessTime());
        if (timeDiff != 0) {
            return timeDiff;
        }
@@ -724,90 +697,24 @@ 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.
     * Compare two usages by their access count.
     *
     * Can be used as a {@link java.util.Comparator}.
     *
     * @param x an AppPermissionUsage.
     * @param y an AppPermissionUsage.
     * @param x a usage.
     * @param y a usage.
     *
     * @return see {@link java.util.Comparator#compare(Object, Object)}.
     */
    private static int compareAccessCount(@NonNull AppPermissionUsage x,
            @NonNull AppPermissionUsage y) {
        final int accessDiff = compareLong(getAccessCount(x), getAccessCount(y));
    private static int compareAccessCount(@NonNull Pair<AppPermissionUsage, GroupUsage> x,
            @NonNull Pair<AppPermissionUsage, GroupUsage> y) {
        final int accessDiff = compareLong(x.second.getAccessCount(), y.second.getAccessCount());
        if (accessDiff != 0) {
            return accessDiff;
        }
        return compareAccessTime(x, y);
    }

    /**
     * Compare two AppPermissionUsage by their access count.
     *
     * Can be used as a {@link java.util.Comparator}.
     *
     * @param x an AppPermissionUsage.
     * @param y an AppPermissionUsage.
     *
     * @return see {@link java.util.Comparator#compare(Object, Object)}.
     */
    private static int compareAccessCount(@NonNull AppPermissionUsage.GroupUsage x,
            @NonNull GroupUsage y) {
        final int accessDiff = compareLong(x.getAccessCount(), y.getAccessCount());
        if (accessDiff != 0) {
            return accessDiff;
        }
        // Make sure we lose no data if same
        return y.hashCode() - x.hashCode();
    }

    /**
     * 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.
     *
@@ -828,17 +735,17 @@ public class PermissionUsageFragment extends PermissionsFrameFragment implements
    }

    /**
     * Compare two AppPermissionUsage by recency of access.
     * Compare two usages by recency of access.
     *
     * Can be used as a {@link java.util.Comparator}.
     *
     * @param x an AppPermissionUsage.
     * @param y an AppPermissionUsage.
     * @param x a usage.
     * @param y a usage.
     *
     * @return see {@link java.util.Comparator#compare(Object, Object)}.
     */
    private static int compareAccessRecency(@NonNull AppPermissionUsage x,
            @NonNull AppPermissionUsage y) {
    private static int compareAccessRecency(@NonNull Pair<AppPermissionUsage, GroupUsage> x,
            @NonNull Pair<AppPermissionUsage, GroupUsage> y) {
        final int timeDiff = compareAccessTime(x, y);
        if (timeDiff != 0) {
            return timeDiff;