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

Commit 69a54aed authored by Joel Galenson's avatar Joel Galenson
Browse files

Update AppPermissionUsageFragment to the newest mocks.

Have AppPermissionUsageFragment use the same summary text as
PermissionUsageFragment and give it a time spinner.

Bug: 63532550
Test: Open AppPermissionUsageFragment, use time spinner.

Change-Id: I54e374a86b47dabac5d61984c4da405333c4fea4
parent a8aef3ac
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
     Copyright (C) 2019 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="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:gravity="center" >

    <com.android.settingslib.widget.settingsspinner.SettingsSpinner
        android:id="@+id/filter_spinner"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:layout_marginTop="12dp"
        android:layout_marginBottom="8dp"/>

</LinearLayout>
+92 −22
Original line number Diff line number Diff line
@@ -26,24 +26,29 @@ import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Spinner;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;

import com.android.packageinstaller.permission.model.AppPermissionGroup;
import com.android.packageinstaller.permission.model.AppPermissionUsage;
import com.android.packageinstaller.permission.model.AppPermissionUsage.GroupUsage;
import com.android.packageinstaller.permission.model.PermissionUsages;
import com.android.packageinstaller.permission.ui.handheld.FilterSpinner.FilterSpinnerAdapter;
import com.android.packageinstaller.permission.ui.handheld.FilterSpinner.TimeFilterItem;
import com.android.packageinstaller.permission.utils.Utils;
import com.android.permissioncontroller.R;

import java.util.Comparator;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * Show the usage of all permission groups by a single app.
@@ -51,15 +56,29 @@ import java.util.concurrent.TimeUnit;
 * <p>Shows a list of app usage of permission groups, each of which links to
 * AppPermissionsFragment.
 */
public class AppPermissionUsageFragment extends SettingsWithButtonHeader {
public class AppPermissionUsageFragment extends SettingsWithButtonHeader implements
        OnItemSelectedListener {

    private static final String LOG_TAG = "AppPermissionUsageFragment";

    private static final String KEY_SPINNER_TIME_INDEX = "_time_index";
    private static final String SPINNER_TIME_INDEX_KEY = AppPermissionUsageFragment.class.getName()
            + KEY_SPINNER_TIME_INDEX;

    private @NonNull String mPackageName;
    private @NonNull ApplicationInfo mAppInfo;

    private @NonNull PermissionUsages mPermissionUsages;

    private Spinner mFilterSpinner;
    private FilterSpinnerAdapter<TimeFilterItem> mFilterAdapter;

    /**
     * Only used to restore spinner state after onCreate. Once the list of times is reported, this
     * becomes invalid.
     */
    private int mSavedSpinnerIndex;

    /**
     * @return A new fragment
     */
@@ -82,6 +101,11 @@ public class AppPermissionUsageFragment extends SettingsWithButtonHeader {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (savedInstanceState != null) {
            mSavedSpinnerIndex = savedInstanceState.getInt(SPINNER_TIME_INDEX_KEY);
        }

        setLoading(true, false);
        setHasOptionsMenu(true);
        ActionBar ab = getActivity().getActionBar();
@@ -89,22 +113,37 @@ public class AppPermissionUsageFragment extends SettingsWithButtonHeader {
            ab.setDisplayHomeAsUpEnabled(true);
        }

        String packageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
        mPackageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
        UserHandle userHandle = getArguments().getParcelable(Intent.EXTRA_USER);
        Activity activity = getActivity();
        mAppInfo = getApplicationInfo(getActivity(), packageName, userHandle);
        mAppInfo = getApplicationInfo(getActivity(), mPackageName, userHandle);
        if (mAppInfo == null) {
            Toast.makeText(activity, R.string.app_not_found_dlg_title, Toast.LENGTH_LONG).show();
            activity.finish();
            return;
        }

        final long beginTimeMillis = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(24);
        mPermissionUsages = new PermissionUsages(getContext());
        mPermissionUsages.load(mAppInfo.uid, packageName, null, beginTimeMillis, Long.MAX_VALUE,
                PermissionUsages.USAGE_FLAG_LAST | PermissionUsages.USAGE_FLAG_HISTORICAL,
                getActivity().getLoaderManager(),
                true, this::updateUi, false);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        Context context = getPreferenceManager().getContext();
        ViewGroup root = (ViewGroup) super.onCreateView(inflater, container, savedInstanceState);

        View spinnerView = inflater.inflate(R.layout.single_spinner, root, false);
        getPreferencesContainer().addView(spinnerView, 1);

        mFilterSpinner = spinnerView.requireViewById(R.id.filter_spinner);
        mFilterAdapter = new FilterSpinnerAdapter<>(context);
        mFilterSpinner.setAdapter(mFilterAdapter);
        mFilterSpinner.setOnItemSelectedListener(this);

        FilterSpinner.addTimeFilters(mFilterAdapter, context);
        mFilterSpinner.setSelection(mSavedSpinnerIndex);

        return root;
    }

    @Override
@@ -114,6 +153,21 @@ public class AppPermissionUsageFragment extends SettingsWithButtonHeader {
        setHeader(icon, Utils.getFullAppLabel(mAppInfo, getContext()), true);
    }

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        reloadData();
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(SPINNER_TIME_INDEX_KEY, mFilterSpinner.getSelectedItemPosition());
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == android.R.id.home) {
@@ -163,6 +217,11 @@ public class AppPermissionUsageFragment extends SettingsWithButtonHeader {
            return;
        }

        // Get the current values of the time filter.
        TimeFilterItem timeFilterItem = getSelectedFilterItem();
        long startTime = (timeFilterItem == null ? 0
                : (System.currentTimeMillis() - timeFilterItem.getTime()));

        final AppPermissionUsage appPermissionUsage = permissionUsages.get(0);
        final List<AppPermissionUsage.GroupUsage> groupUsages = appPermissionUsage.getGroupUsages();
        groupUsages.sort(Comparator.comparing(GroupUsage::getAccessCount).reversed());
@@ -170,7 +229,7 @@ public class AppPermissionUsageFragment extends SettingsWithButtonHeader {
        final int permissionCount = groupUsages.size();
        for (int permissionIdx = 0; permissionIdx < permissionCount; permissionIdx++) {
            final GroupUsage groupUsage = groupUsages.get(permissionIdx);
            if (groupUsage.getAccessCount() <= 0) {
            if (groupUsage.getAccessCount() <= 0 || groupUsage.getLastAccessTime() < startTime) {
                continue;
            }
            final AppPermissionGroup group = groupUsage.getGroup();
@@ -178,18 +237,9 @@ public class AppPermissionUsageFragment extends SettingsWithButtonHeader {
            if (group.getLabel().equals("Storage")) {
                continue;
            }
            Preference pref = new PermissionControlPreference(context, group);
            PermissionControlPreference pref = new PermissionControlPreference(context, group);
            pref.setTitle(groupUsage.getGroup().getLabel());
            if (groupUsage.getAccessDuration() == 0) {
                pref.setSummary(context.getString(R.string.app_permission_usage_summary_no_duration,
                        groupUsage.getAccessCount(), Utils.getRelativeLastUsageString(context,
                                groupUsage)));
            } else {
                pref.setSummary(context.getString(R.string.app_permission_usage_summary,
                        groupUsage.getAccessCount(),
                        Utils.getUsageDurationString(context, groupUsage),
                        Utils.getRelativeLastUsageString(context, groupUsage)));
            }
            pref.setUsageSummary(groupUsage, Utils.getAbsoluteLastUsageString(context, groupUsage));
            pref.setIcon(Utils.applyTint(context, group.getIconResId(),
                    android.R.attr.colorControlNormal));
            pref.setKey(group.getName());
@@ -198,4 +248,24 @@ public class AppPermissionUsageFragment extends SettingsWithButtonHeader {

        setLoading(false, true);
    }

    private TimeFilterItem getSelectedFilterItem() {
        int pos = mFilterSpinner.getSelectedItemPosition();
        if (pos != AdapterView.INVALID_POSITION) {
            return mFilterAdapter.getFilter(pos);
        }
        return null;
    }

    private void reloadData() {
        TimeFilterItem timeFilterItem = getSelectedFilterItem();
        if (timeFilterItem == null) {
            return;
        }
        long beginTimeMillis = Math.max(System.currentTimeMillis() - timeFilterItem.getTime(), 0);
        mPermissionUsages.load(mAppInfo.uid, mPackageName, null, beginTimeMillis, Long.MAX_VALUE,
                PermissionUsages.USAGE_FLAG_LAST | PermissionUsages.USAGE_FLAG_HISTORICAL,
                getActivity().getLoaderManager(),
                true, this::updateUi, false);
    }
}
+178 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.packageinstaller.permission.ui.handheld;

import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MINUTES;

import android.content.Context;

import androidx.annotation.NonNull;
import androidx.annotation.StringRes;

import com.android.permissioncontroller.R;
import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter;

import java.util.ArrayList;

/**
 * Utility class for using filter spinners.
 */
public class FilterSpinner {

    private FilterSpinner() {
        /* do nothing - hide constructor */
    }

    /**
     * An adapter that stores the entries in a filter spinner.
     *
     * @param <T> The type of the entries in the filter spinner.
     */
    public static class FilterSpinnerAdapter<T extends SpinnerItem> extends
            SettingsSpinnerAdapter<CharSequence> {
        private final ArrayList<T> mFilterOptions = new ArrayList<>();

        FilterSpinnerAdapter(@NonNull Context context) {
            super(context);
        }

        /**
         * Add the given filter to this adapter.
         *
         * @param filter the filter to add
         */
        public void addFilter(@NonNull T filter) {
            mFilterOptions.add(filter);
            notifyDataSetChanged();
        }

        /**
         * Get the filter at the given position.
         *
         * @param position the index of the filter to get.
         *
         * @return the filter at the given index.
         */
        public T getFilter(int position) {
            return mFilterOptions.get(position);
        }

        @Override
        public int getCount() {
            return mFilterOptions.size();
        }

        @Override
        public CharSequence getItem(int position) {
            return mFilterOptions.get(position).getLabel();
        }

        @Override
        public void clear() {
            mFilterOptions.clear();
            super.clear();
        }

    }

    /**
     * An interface to represent items that we can use as filters.
     */
    public interface SpinnerItem {
        /**
         * Get the label of this item to display to the user.
         *
         * @return the label of this item.
         */
        @NonNull String getLabel();
    }

    /**
     * A spinner item representing a given time, e.g., "in the last hour".
     */
    public static class TimeFilterItem implements SpinnerItem {
        private final long mTime;
        private final @NonNull String mLabel;
        private final @StringRes int mGraphTitleRes;
        private final @StringRes int mListTitleRes;

        TimeFilterItem(long time, @NonNull String label, @StringRes int graphTitleRes,
                @StringRes int listTitleRes) {
            mTime = time;
            mLabel = label;
            mGraphTitleRes = graphTitleRes;
            mListTitleRes = listTitleRes;
        }

        /**
         * Get the time represented by this object in milliseconds.
         *
         * @return the time represented by this object.
         */
        public long getTime() {
            return mTime;
        }

        public @NonNull String getLabel() {
            return mLabel;
        }

        public @StringRes int getGraphTitleRes() {
            return mGraphTitleRes;
        }

        public @StringRes int getListTitleRes() {
            return mListTitleRes;
        }
    }

    /**
     * Add time filter entries.
     *
     * @param adapter the filter spinner adapter
     * @param context the context
     */
    public static void addTimeFilters(@NonNull FilterSpinnerAdapter<TimeFilterItem> adapter,
            @NonNull Context context) {
        adapter.addFilter(new TimeFilterItem(Long.MAX_VALUE,
                context.getString(R.string.permission_usage_any_time),
                R.string.permission_usage_bar_chart_title_any_time,
                R.string.permission_usage_list_title_any_time));
        adapter.addFilter(new TimeFilterItem(DAYS.toMillis(7),
                context.getString(R.string.permission_usage_last_7_days),
                R.string.permission_usage_bar_chart_title_last_7_days,
                R.string.permission_usage_list_title_last_7_days));
        adapter.addFilter(new TimeFilterItem(DAYS.toMillis(1),
                context.getString(R.string.permission_usage_last_day),
                R.string.permission_usage_bar_chart_title_last_day,
                R.string.permission_usage_list_title_last_day));
        adapter.addFilter(new TimeFilterItem(HOURS.toMillis(1),
                context.getString(R.string.permission_usage_last_hour),
                R.string.permission_usage_bar_chart_title_last_hour,
                R.string.permission_usage_list_title_last_hour));
        adapter.addFilter(new TimeFilterItem(MINUTES.toMillis(15),
                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));
        adapter.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));
    }
}
+18 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;

import com.android.packageinstaller.permission.model.AppPermissionGroup;
import com.android.packageinstaller.permission.model.AppPermissionUsage.GroupUsage;
import com.android.packageinstaller.permission.ui.AppPermissionActivity;
import com.android.permissioncontroller.R;

@@ -113,6 +114,23 @@ public class PermissionControlPreference extends Preference {
        setSummary("");
    }

    /**
     * Sets this preference's summary based on its permission usage.
     *
     * @param groupUsage the usage information
     * @param accessTimeStr the string representing the last access time
     */
    public void setUsageSummary(@NonNull GroupUsage groupUsage, @NonNull String accessTimeStr) {
        if (groupUsage.getBackgroundAccessCount() == 0) {
            setSummary(mContext.getString(R.string.permission_usage_summary, accessTimeStr,
                    groupUsage.getForegroundAccessCount()));
        } else {
            setSummary(
                    mContext.getString(R.string.permission_usage_summary_background, accessTimeStr,
                            groupUsage.getAccessCount(), groupUsage.getBackgroundAccessCount()));
        }
    }

    /**
     * Sets this preference to show the given icons to the left of its title.
     *
+5 −124
Original line number Diff line number Diff line
@@ -17,9 +17,6 @@
package com.android.packageinstaller.permission.ui.handheld;

import static java.lang.annotation.RetentionPolicy.SOURCE;
import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MINUTES;

import android.app.ActionBar;
import android.app.AlertDialog;
@@ -45,7 +42,6 @@ import android.widget.Spinner;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.fragment.app.DialogFragment;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
@@ -54,13 +50,15 @@ import com.android.packageinstaller.permission.model.AppPermissionGroup;
import com.android.packageinstaller.permission.model.AppPermissionUsage;
import com.android.packageinstaller.permission.model.AppPermissionUsage.GroupUsage;
import com.android.packageinstaller.permission.model.PermissionUsages;
import com.android.packageinstaller.permission.ui.handheld.FilterSpinner.FilterSpinnerAdapter;
import com.android.packageinstaller.permission.ui.handheld.FilterSpinner.SpinnerItem;
import com.android.packageinstaller.permission.ui.handheld.FilterSpinner.TimeFilterItem;
import com.android.packageinstaller.permission.utils.Utils;
import com.android.permissioncontroller.R;
import com.android.settingslib.HelpUtils;
import com.android.settingslib.widget.BarChartInfo;
import com.android.settingslib.widget.BarChartPreference;
import com.android.settingslib.widget.BarViewInfo;
import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter;

import java.lang.annotation.Retention;
import java.text.Collator;
@@ -211,30 +209,7 @@ public class PermissionUsageFragment extends SettingsWithButtonHeader implements
        mSortSpinner.setOnItemSelectedListener(this);

        // Add time spinner entries.
        mFilterAdapterTime.addFilter(new TimeFilterItem(Long.MAX_VALUE,
                context.getString(R.string.permission_usage_any_time),
                R.string.permission_usage_bar_chart_title_any_time,
                R.string.permission_usage_list_title_any_time));
        mFilterAdapterTime.addFilter(new TimeFilterItem(DAYS.toMillis(7),
                context.getString(R.string.permission_usage_last_7_days),
                R.string.permission_usage_bar_chart_title_last_7_days,
                R.string.permission_usage_list_title_last_7_days));
        mFilterAdapterTime.addFilter(new TimeFilterItem(DAYS.toMillis(1),
                context.getString(R.string.permission_usage_last_day),
                R.string.permission_usage_bar_chart_title_last_day,
                R.string.permission_usage_list_title_last_day));
        mFilterAdapterTime.addFilter(new TimeFilterItem(HOURS.toMillis(1),
                context.getString(R.string.permission_usage_last_hour),
                R.string.permission_usage_bar_chart_title_last_hour,
                R.string.permission_usage_list_title_last_hour));
        mFilterAdapterTime.addFilter(new TimeFilterItem(MINUTES.toMillis(15),
                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));
        FilterSpinner.addTimeFilters(mFilterAdapterTime, context);
        mFilterSpinnerTime.setSelection(mSavedTimeSpinnerIndex);

        // Add sort spinner entries.
@@ -590,17 +565,7 @@ public class PermissionUsageFragment extends SettingsWithButtonHeader implements

        final AppPermissionGroup group = groupUsage.getGroup();
        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_background,
                            accessTimeStr, groupUsage.getAccessCount(),
                            groupUsage.getBackgroundAccessCount()));
        }
        pref.setUsageSummary(groupUsage, accessTimeStr);
        pref.setTitleIcons(Collections.singletonList(group.getIconResId()));
        pref.setKey(group.getApp().packageName + "," + group.getName());
        pref.useSmallerIcon();
@@ -885,90 +850,6 @@ public class PermissionUsageFragment extends SettingsWithButtonHeader implements
        }
    }

    /**
     * An adapter that stores the entries in a filter spinner.
     * @param <T> The type of the entries in the filter spinner.
     */
    private static class FilterSpinnerAdapter<T extends SpinnerItem> extends
            SettingsSpinnerAdapter<CharSequence> {
        private final ArrayList<T> mFilterOptions = new ArrayList<>();

        FilterSpinnerAdapter(@NonNull Context context) {
            super(context);
        }

        public void addFilter(@NonNull T filter) {
            mFilterOptions.add(filter);
            notifyDataSetChanged();
        }

        public T getFilter(int position) {
            return mFilterOptions.get(position);
        }

        @Override
        public int getCount() {
            return mFilterOptions.size();
        }

        @Override
        public CharSequence getItem(int position) {
            return mFilterOptions.get(position).getLabel();
        }

        @Override
        public void clear() {
            mFilterOptions.clear();
            super.clear();
        }
    }

    /**
     * An interface to represent items that we can use as filters.
     */
    private interface SpinnerItem {
        @NonNull String getLabel();
    }

    /**
     * A spinner item representing a given time, e.g., "in the last hour".
     */
    private static class TimeFilterItem implements SpinnerItem {
        private final long mTime;
        private final @NonNull String mLabel;
        private final @StringRes int mGraphTitleRes;
        private final @StringRes int mListTitleRes;

        TimeFilterItem(long time, @NonNull String label, @StringRes int graphTitleRes,
                @StringRes int listTitleRes) {
            mTime = time;
            mLabel = label;
            mGraphTitleRes = graphTitleRes;
            mListTitleRes = listTitleRes;
        }

        /**
         * Get the time represented by this object in milliseconds.
         *
         * @return the time represented by this object.
         */
        public long getTime() {
            return mTime;
        }

        public @NonNull String getLabel() {
            return mLabel;
        }

        public @StringRes int getGraphTitleRes() {
            return mGraphTitleRes;
        }

        public @StringRes int getListTitleRes() {
            return mListTitleRes;
        }
    }

    /**
     * A spinner item representing different ways to sort the entries.
     */