Sort recent apps, show message when no recent apps
Change-Id: I70bb1913435b46fc87aec10075e43f98ef07ef62
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6f242ca..b49d929 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -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-->
diff --git a/src/com/android/settings/fuelgauge/BatterySipper.java b/src/com/android/settings/fuelgauge/BatterySipper.java
index 5245434..fcc8f69 100644
--- a/src/com/android/settings/fuelgauge/BatterySipper.java
+++ b/src/com/android/settings/fuelgauge/BatterySipper.java
@@ -119,6 +119,14 @@
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);
diff --git a/src/com/android/settings/location/RecentLocationApps.java b/src/com/android/settings/location/RecentLocationApps.java
index fda734c..9a59563 100644
--- a/src/com/android/settings/location/RecentLocationApps.java
+++ b/src/com/android/settings/location/RecentLocationApps.java
@@ -20,6 +20,7 @@
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.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 @@
}
}
+ 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 @@
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);
- }
-
- 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;
- }
+ BatterySipperWrapper wrapper = sipperMap.get(ops.getUid());
+ PreferenceScreen screen = getScreenFromOps(preferenceManager, now, ops, wrapper);
+ if (screen != null) {
+ prefs.add(screen);
}
}
- // 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);
+ 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());
}
- screen.setOnPreferenceClickListener(
- new PackageEntryClickedListener(entry.getKey()));
- container.addPreference(screen);
- } catch (PackageManager.NameNotFoundException e) {
- // ignore the current app and move on to the next.
+ });
+ for (PreferenceScreen entry : prefs) {
+ container.addPreference(entry);
}
+ } else {
+ // 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);
}
}
- 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 @@
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, packageName + " hadn't used location within the time interval.");
}
- return;
+ return null;
}
- packageMap.put(packageName, highBattery);
+ // 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);
+ }
+ }
+
+ return screen;
}
}