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

Commit b9ab196b authored by Lifu Tang's avatar Lifu Tang
Browse files

Sort recent apps, show message when no recent apps

Change-Id: I70bb1913435b46fc87aec10075e43f98ef07ef62
parent dbd2f759
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -2377,6 +2377,8 @@
    <string name="location_mode_location_off_title">Location off</string>
    <!-- [CHAR LIMIT=30] Location settings screen, sub category for recent location requests -->
    <string name="location_category_recent_location_requests">Recent location requests</string>
    <!-- [CHAR LIMIT=30] Location settings screen, displayed when there's no recent app accessing location -->
    <string name="location_no_recent_apps">No recent apps</string>
    <!-- [CHAR LIMIT=30] Location settings screen, sub category for location services -->
    <string name="location_category_location_services">Location services</string>
    <!-- [CHAR LIMIT=30] Location settings screen, recent location requests high battery use-->
+8 −0
Original line number Diff line number Diff line
@@ -119,6 +119,14 @@ public class BatterySipper implements Comparable<BatterySipper> {
        return mPackages;
    }

    public int getUid() {
        // Bail out if the current sipper is not an App sipper.
        if (uidObj == null) {
            return 0;
        }
        return uidObj.getUid();
    }

    void getQuickNameIconForUid(Uid uidObj) {
        final int uid = uidObj.getUid();
        final String uidString = Integer.toString(uid);
+140 −54
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceActivity;
@@ -33,9 +34,11 @@ import com.android.settings.applications.InstalledAppDetails;
import com.android.settings.fuelgauge.BatterySipper;
import com.android.settings.fuelgauge.BatteryStatsHelper;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Retrieves the information of applications which accessed location recently.
@@ -93,12 +96,69 @@ public class RecentLocationApps {
        }
    }

    private PreferenceScreen createRecentLocationEntry(
            PreferenceManager preferenceManager,
            Drawable icon,
            CharSequence label,
            boolean isHighBattery,
            Preference.OnPreferenceClickListener listener) {
        PreferenceScreen screen = preferenceManager.createPreferenceScreen(mActivity);
        screen.setIcon(icon);
        screen.setTitle(label);
        if (isHighBattery) {
            screen.setSummary(R.string.location_high_battery_use);
        } else {
            screen.setSummary(R.string.location_low_battery_use);
        }
        screen.setOnPreferenceClickListener(listener);
        return screen;
    }

    /**
     * Stores a BatterySipper object and records whether the sipper has been used.
     */
    private static final class BatterySipperWrapper {
        private BatterySipper mSipper;
        private boolean mUsed;

        public BatterySipperWrapper(BatterySipper sipper) {
            mSipper = sipper;
            mUsed = false;
        }

        public BatterySipper batterySipper() {
            return mSipper;
        }

        public boolean used() {
            return mUsed;
        }

        public void setUsed() {
            mUsed = true;
        }
    }

    /**
     * Fills a list of applications which queried location recently within
     * specified time.
     */
    public void fillAppList(PreferenceCategory container) {
        HashMap<String, Boolean> packageMap = new HashMap<String, Boolean>();
        // Retrieve Uid-based battery blaming info and generate a package to BatterySipper HashMap
        // for later faster looking up.
        mStatsHelper.refreshStats();
        List<BatterySipper> usageList = mStatsHelper.getUsageList();
        // Key: package Uid. Value: BatterySipperWrapper.
        HashMap<Integer, BatterySipperWrapper> sipperMap =
                new HashMap<Integer, BatterySipperWrapper>(usageList.size());
        for (BatterySipper sipper: usageList) {
            int uid = sipper.getUid();
            if (uid != 0) {
                sipperMap.put(uid, new BatterySipperWrapper(sipper));
            }
        }

        // Retrieve a location usage list from AppOps
        AppOpsManager aoManager =
                (AppOpsManager) mActivity.getSystemService(Context.APP_OPS_SERVICE);
        List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps(
@@ -107,68 +167,56 @@ public class RecentLocationApps {
                    AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION,
                });
        PreferenceManager preferenceManager = container.getPreferenceManager();

        // Process the AppOps list and generate a preference list.
        ArrayList<PreferenceScreen> prefs = new ArrayList<PreferenceScreen>();
        long now = System.currentTimeMillis();
        for (AppOpsManager.PackageOps ops : appOps) {
            processPackageOps(now, container, preferenceManager, ops, packageMap);
            BatterySipperWrapper wrapper = sipperMap.get(ops.getUid());
            PreferenceScreen screen = getScreenFromOps(preferenceManager, now, ops, wrapper);
            if (screen != null) {
                prefs.add(screen);
            }

        mStatsHelper.refreshStats();
        List<BatterySipper> usageList = mStatsHelper.getUsageList();
        for (BatterySipper sipper : usageList) {
            sipper.loadNameAndIcon();
            String[] packages = sipper.getPackages();
            if (packages == null) {
                continue;
            }
            for (String curPackage : packages) {
                if (packageMap.containsKey(curPackage)) {
                    PreferenceScreen screen = preferenceManager.createPreferenceScreen(mActivity);
                    screen.setIcon(sipper.getIcon());
                    screen.setTitle(sipper.getLabel());
                    if (packageMap.get(curPackage)) {
                        screen.setSummary(R.string.location_high_battery_use);
                    } else {
                        screen.setSummary(R.string.location_low_battery_use);
                    }
                    container.addPreference(screen);
                    screen.setOnPreferenceClickListener(new UidEntryClickedListener(sipper));
                    packageMap.remove(curPackage);
                    break;
        }

        if (prefs.size() > 0) {
            // If there's some items to display, sort the items and add them to the container.
            Collections.sort(prefs, new Comparator<PreferenceScreen>() {
                @Override
                public int compare(PreferenceScreen lhs, PreferenceScreen rhs) {
                    return lhs.getTitle().toString().compareTo(rhs.getTitle().toString());
                }
            });
            for (PreferenceScreen entry : prefs) {
                container.addPreference(entry);
            }

        // Typically there shouldn't be any entry left in the HashMap. But if there are any, add
        // them to the list and link them to the app info page.
        for (Map.Entry<String, Boolean> entry : packageMap.entrySet()) {
            try {
                PreferenceScreen screen = preferenceManager.createPreferenceScreen(mActivity);
                ApplicationInfo appInfo = mPackageManager.getApplicationInfo(
                        entry.getKey(), PackageManager.GET_META_DATA);
                screen.setIcon(mPackageManager.getApplicationIcon(appInfo));
                screen.setTitle(mPackageManager.getApplicationLabel(appInfo));
                // if used both high and low battery within the time interval, show as "high
                // battery"
                if (entry.getValue()) {
                    screen.setSummary(R.string.location_high_battery_use);
        } else {
                    screen.setSummary(R.string.location_low_battery_use);
                }
                screen.setOnPreferenceClickListener(
                        new PackageEntryClickedListener(entry.getKey()));
            // If there's no item to display, add a "No recent apps" item.
            PreferenceScreen screen = preferenceManager.createPreferenceScreen(mActivity);
            screen.setTitle(R.string.location_no_recent_apps);
            screen.setSelectable(false);
            screen.setEnabled(false);
            container.addPreference(screen);
            } catch (PackageManager.NameNotFoundException e) {
                // ignore the current app and move on to the next.
            }
        }
    }

    private void processPackageOps(
            long now,
            PreferenceCategory container,
    /**
     * Creates a PreferenceScreen entry for the given PackageOps.
     *
     * This method examines the time interval of the PackageOps first. If the PackageOps is older
     * than the designated interval, this method ignores the PackageOps object and returns null.
     *
     * When the PackageOps is fresh enough, if the package has a corresponding battery blaming entry
     * in the Uid-based battery sipper list, this method returns a PreferenceScreen pointing to the
     * Uid battery blaming page. If the package doesn't have a battery sipper entry (typically
     * shouldn't happen), this method returns a PreferenceScreen pointing to the App Info page for
     * that package.
     */
    private PreferenceScreen getScreenFromOps(
            PreferenceManager preferenceManager,
            long now,
            AppOpsManager.PackageOps ops,
            HashMap<String, Boolean> packageMap) {
            BatterySipperWrapper wrapper) {
        String packageName = ops.getPackageName();
        List<AppOpsManager.OpEntry> entries = ops.getOps();
        boolean highBattery = false;
@@ -193,9 +241,47 @@ public class RecentLocationApps {
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, packageName + " hadn't used location within the time interval.");
            }
            return;
            return null;
        }

        // The package is fresh enough, continue.

        PreferenceScreen screen = null;
        if (wrapper != null) {
            // Contains sipper. Link to Battery Blaming page.

            // We're listing by UID rather than package. Check whether the entry has been used
            // before to prevent the same UID from showing up twice.
            if (!wrapper.used()) {
                BatterySipper sipper = wrapper.batterySipper();
                sipper.loadNameAndIcon();
                screen = createRecentLocationEntry(
                        preferenceManager,
                        sipper.getIcon(),
                        sipper.getLabel(),
                        highBattery,
                        new UidEntryClickedListener(sipper));
                wrapper.setUsed();
            }
        } else {
            // No corresponding sipper. Link to App Info page.

            // This is grouped by package rather than UID, but that's OK because this branch
            // shouldn't happen in practice.
            try {
                ApplicationInfo appInfo = mPackageManager.getApplicationInfo(
                        packageName, PackageManager.GET_META_DATA);
                screen = createRecentLocationEntry(
                        preferenceManager,
                        mPackageManager.getApplicationIcon(appInfo),
                        mPackageManager.getApplicationLabel(appInfo),
                        highBattery,
                        new PackageEntryClickedListener(packageName));
            } catch (PackageManager.NameNotFoundException e) {
                Log.wtf(TAG, "Package not found: " + packageName);
            }
        }

        packageMap.put(packageName, highBattery);
        return screen;
    }
}