in the new design, we should store all data into the databae for showing the battery history, we should not filter the items first from the consumed battery value threshold Bug: 191468827 Test: make SettingsRoboTests Change-Id: I19d971fc5cdcc40af1693dc8ba2c78586da22d49
548 lines
22 KiB
Java
548 lines
22 KiB
Java
/*
|
|
* Copyright (C) 2017 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.settings.fuelgauge;
|
|
|
|
import android.app.Activity;
|
|
import android.content.Context;
|
|
import android.content.pm.PackageManager;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.os.AggregateBatteryConsumer;
|
|
import android.os.BatteryConsumer;
|
|
import android.os.BatteryUsageStats;
|
|
import android.os.Handler;
|
|
import android.os.Looper;
|
|
import android.os.Message;
|
|
import android.os.Process;
|
|
import android.os.UidBatteryConsumer;
|
|
import android.os.UserBatteryConsumer;
|
|
import android.os.UserHandle;
|
|
import android.os.UserManager;
|
|
import android.text.TextUtils;
|
|
import android.text.format.DateUtils;
|
|
import android.util.ArrayMap;
|
|
import android.util.SparseArray;
|
|
|
|
import androidx.annotation.VisibleForTesting;
|
|
import androidx.preference.Preference;
|
|
import androidx.preference.PreferenceGroup;
|
|
import androidx.preference.PreferenceScreen;
|
|
|
|
import com.android.internal.os.PowerProfile;
|
|
import com.android.settings.R;
|
|
import com.android.settings.SettingsActivity;
|
|
import com.android.settings.core.InstrumentedPreferenceFragment;
|
|
import com.android.settings.core.PreferenceControllerMixin;
|
|
import com.android.settingslib.core.AbstractPreferenceController;
|
|
import com.android.settingslib.core.lifecycle.Lifecycle;
|
|
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
|
import com.android.settingslib.core.lifecycle.events.OnDestroy;
|
|
import com.android.settingslib.core.lifecycle.events.OnPause;
|
|
import com.android.settingslib.utils.StringUtil;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Comparator;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* Controller that update the battery header view
|
|
*/
|
|
public class BatteryAppListPreferenceController extends AbstractPreferenceController
|
|
implements PreferenceControllerMixin, LifecycleObserver, OnPause, OnDestroy {
|
|
@VisibleForTesting
|
|
static final boolean USE_FAKE_DATA = false;
|
|
private static final int MAX_ITEMS_TO_LIST = USE_FAKE_DATA ? 30 : 20;
|
|
private static final int MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP = 10;
|
|
private static final String MEDIASERVER_PACKAGE_NAME = "mediaserver";
|
|
|
|
private final String mPreferenceKey;
|
|
@VisibleForTesting
|
|
PreferenceGroup mAppListGroup;
|
|
private BatteryUsageStats mBatteryUsageStats;
|
|
private ArrayMap<String, Preference> mPreferenceCache;
|
|
@VisibleForTesting
|
|
BatteryUtils mBatteryUtils;
|
|
private final UserManager mUserManager;
|
|
private final PackageManager mPackageManager;
|
|
private final SettingsActivity mActivity;
|
|
private final InstrumentedPreferenceFragment mFragment;
|
|
private Context mPrefContext;
|
|
|
|
/**
|
|
* Battery attribution list configuration.
|
|
*/
|
|
public interface Config {
|
|
/**
|
|
* Returns true if the attribution list should be shown.
|
|
*/
|
|
boolean shouldShowBatteryAttributionList(Context context);
|
|
}
|
|
|
|
@VisibleForTesting
|
|
static Config sConfig = new Config() {
|
|
@Override
|
|
public boolean shouldShowBatteryAttributionList(Context context) {
|
|
if (USE_FAKE_DATA) {
|
|
return true;
|
|
}
|
|
|
|
PowerProfile powerProfile = new PowerProfile(context);
|
|
return powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL)
|
|
>= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP;
|
|
}
|
|
};
|
|
|
|
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
|
|
@Override
|
|
public void handleMessage(Message msg) {
|
|
switch (msg.what) {
|
|
case BatteryEntry.MSG_UPDATE_NAME_ICON:
|
|
BatteryEntry entry = (BatteryEntry) msg.obj;
|
|
PowerGaugePreference pgp = mAppListGroup.findPreference(entry.getKey());
|
|
if (pgp != null) {
|
|
final int userId = UserHandle.getUserId(entry.getUid());
|
|
final UserHandle userHandle = new UserHandle(userId);
|
|
pgp.setIcon(mUserManager.getBadgedIconForUser(entry.getIcon(), userHandle));
|
|
pgp.setTitle(entry.name);
|
|
if (entry.isAppEntry()) {
|
|
pgp.setContentDescription(entry.name);
|
|
}
|
|
}
|
|
break;
|
|
case BatteryEntry.MSG_REPORT_FULLY_DRAWN:
|
|
Activity activity = mActivity;
|
|
if (activity != null) {
|
|
activity.reportFullyDrawn();
|
|
}
|
|
break;
|
|
}
|
|
super.handleMessage(msg);
|
|
}
|
|
};
|
|
|
|
public BatteryAppListPreferenceController(Context context, String preferenceKey,
|
|
Lifecycle lifecycle, SettingsActivity activity,
|
|
InstrumentedPreferenceFragment fragment) {
|
|
super(context);
|
|
|
|
if (lifecycle != null) {
|
|
lifecycle.addObserver(this);
|
|
}
|
|
|
|
mPreferenceKey = preferenceKey;
|
|
mBatteryUtils = BatteryUtils.getInstance(context);
|
|
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
|
|
mPackageManager = context.getPackageManager();
|
|
mActivity = activity;
|
|
mFragment = fragment;
|
|
}
|
|
|
|
@Override
|
|
public void onPause() {
|
|
BatteryEntry.stopRequestQueue();
|
|
mHandler.removeMessages(BatteryEntry.MSG_UPDATE_NAME_ICON);
|
|
}
|
|
|
|
@Override
|
|
public void onDestroy() {
|
|
if (mActivity.isChangingConfigurations()) {
|
|
BatteryEntry.clearUidCache();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void displayPreference(PreferenceScreen screen) {
|
|
super.displayPreference(screen);
|
|
mPrefContext = screen.getContext();
|
|
mAppListGroup = screen.findPreference(mPreferenceKey);
|
|
mAppListGroup.setTitle(mPrefContext.getString(R.string.power_usage_list_summary));
|
|
}
|
|
|
|
@Override
|
|
public boolean isAvailable() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public String getPreferenceKey() {
|
|
return mPreferenceKey;
|
|
}
|
|
|
|
@Override
|
|
public boolean handlePreferenceTreeClick(Preference preference) {
|
|
if (preference instanceof PowerGaugePreference) {
|
|
PowerGaugePreference pgp = (PowerGaugePreference) preference;
|
|
BatteryEntry entry = pgp.getInfo();
|
|
AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity,
|
|
mFragment, entry, pgp.getPercent(), /*isValidToShowSummary=*/ true);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Refreshes the list of battery consumers using the supplied BatteryUsageStats.
|
|
*/
|
|
public void refreshAppListGroup(BatteryUsageStats batteryUsageStats, boolean showAllApps) {
|
|
if (!isAvailable()) {
|
|
return;
|
|
}
|
|
|
|
mBatteryUsageStats = USE_FAKE_DATA ? getFakeStats() : batteryUsageStats;
|
|
mAppListGroup.setTitle(R.string.power_usage_list_summary);
|
|
|
|
boolean addedSome = false;
|
|
|
|
cacheRemoveAllPrefs(mAppListGroup);
|
|
mAppListGroup.setOrderingAsAdded(false);
|
|
|
|
if (sConfig.shouldShowBatteryAttributionList(mContext)) {
|
|
final int dischargePercentage = getDischargePercentage(batteryUsageStats);
|
|
final List<BatteryEntry> usageList =
|
|
getCoalescedUsageList(showAllApps, /*loadDataInBackground=*/ true);
|
|
final double totalPower = batteryUsageStats.getConsumedPower();
|
|
final int numSippers = usageList.size();
|
|
for (int i = 0; i < numSippers; i++) {
|
|
final BatteryEntry entry = usageList.get(i);
|
|
|
|
final double percentOfTotal = mBatteryUtils.calculateBatteryPercent(
|
|
entry.getConsumedPower(), totalPower, dischargePercentage);
|
|
|
|
if (((int) (percentOfTotal + .5)) < 1) {
|
|
continue;
|
|
}
|
|
|
|
final UserHandle userHandle = new UserHandle(UserHandle.getUserId(entry.getUid()));
|
|
final Drawable badgedIcon = mUserManager.getBadgedIconForUser(entry.getIcon(),
|
|
userHandle);
|
|
final CharSequence contentDescription = mUserManager.getBadgedLabelForUser(
|
|
entry.getLabel(), userHandle);
|
|
|
|
final String key = entry.getKey();
|
|
PowerGaugePreference pref = (PowerGaugePreference) getCachedPreference(key);
|
|
if (pref == null) {
|
|
pref = new PowerGaugePreference(mPrefContext, badgedIcon,
|
|
contentDescription, entry);
|
|
pref.setKey(key);
|
|
}
|
|
entry.percent = percentOfTotal;
|
|
pref.setTitle(entry.getLabel());
|
|
pref.setOrder(i + 1);
|
|
pref.setPercent(percentOfTotal);
|
|
pref.shouldShowAnomalyIcon(false);
|
|
setUsageSummary(pref, entry);
|
|
addedSome = true;
|
|
mAppListGroup.addPreference(pref);
|
|
if (mAppListGroup.getPreferenceCount() - getCachedCount()
|
|
> (MAX_ITEMS_TO_LIST + 1)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!addedSome) {
|
|
addNotAvailableMessage();
|
|
}
|
|
removeCachedPrefs(mAppListGroup);
|
|
|
|
BatteryEntry.startRequestQueue();
|
|
}
|
|
|
|
/**
|
|
* Gets the BatteryEntry list by using the supplied BatteryUsageStats.
|
|
*/
|
|
public List<BatteryEntry> getBatteryEntryList(
|
|
BatteryUsageStats batteryUsageStats, boolean showAllApps) {
|
|
mBatteryUsageStats = USE_FAKE_DATA ? getFakeStats() : batteryUsageStats;
|
|
if (!sConfig.shouldShowBatteryAttributionList(mContext)) {
|
|
return null;
|
|
}
|
|
final int dischargePercentage = getDischargePercentage(batteryUsageStats);
|
|
final List<BatteryEntry> usageList =
|
|
getCoalescedUsageList(showAllApps, /*loadDataInBackground=*/ false);
|
|
final double totalPower = batteryUsageStats.getConsumedPower();
|
|
for (int i = 0; i < usageList.size(); i++) {
|
|
final BatteryEntry entry = usageList.get(i);
|
|
final double percentOfTotal = mBatteryUtils.calculateBatteryPercent(
|
|
entry.getConsumedPower(), totalPower, dischargePercentage);
|
|
entry.percent = percentOfTotal;
|
|
}
|
|
return usageList;
|
|
}
|
|
|
|
private int getDischargePercentage(BatteryUsageStats batteryUsageStats) {
|
|
int dischargePercentage = batteryUsageStats.getDischargePercentage();
|
|
if (dischargePercentage < 0) {
|
|
dischargePercentage = 0;
|
|
}
|
|
return dischargePercentage;
|
|
}
|
|
|
|
/**
|
|
* We want to coalesce some UIDs. For example, dex2oat runs under a shared gid that
|
|
* exists for all users of the same app. We detect this case and merge the power use
|
|
* for dex2oat to the device OWNER's use of the app.
|
|
*
|
|
* @return A sorted list of apps using power.
|
|
*/
|
|
private List<BatteryEntry> getCoalescedUsageList(
|
|
boolean showAllApps, boolean loadDataInBackground) {
|
|
final SparseArray<BatteryEntry> batteryEntryList = new SparseArray<>();
|
|
|
|
final ArrayList<BatteryEntry> results = new ArrayList<>();
|
|
final List<UidBatteryConsumer> uidBatteryConsumers =
|
|
mBatteryUsageStats.getUidBatteryConsumers();
|
|
|
|
// Sort to have all apps with "real" UIDs first, followed by apps that are supposed
|
|
// to be combined with the real ones.
|
|
uidBatteryConsumers.sort(Comparator.comparingInt(
|
|
consumer -> consumer.getUid() == getRealUid(consumer) ? 0 : 1));
|
|
|
|
for (int i = 0, size = uidBatteryConsumers.size(); i < size; i++) {
|
|
final UidBatteryConsumer consumer = uidBatteryConsumers.get(i);
|
|
final int uid = getRealUid(consumer);
|
|
|
|
final String[] packages = mPackageManager.getPackagesForUid(uid);
|
|
if (mBatteryUtils.shouldHideUidBatteryConsumerUnconditionally(consumer, packages)) {
|
|
continue;
|
|
}
|
|
|
|
final boolean isHidden = mBatteryUtils.shouldHideUidBatteryConsumer(consumer, packages);
|
|
if (isHidden && !showAllApps) {
|
|
continue;
|
|
}
|
|
|
|
final int index = batteryEntryList.indexOfKey(uid);
|
|
if (index < 0) {
|
|
// New entry.
|
|
batteryEntryList.put(uid, new BatteryEntry(mContext, mHandler, mUserManager,
|
|
consumer, isHidden, uid, packages, null, loadDataInBackground));
|
|
} else {
|
|
// Combine BatterySippers if we already have one with this UID.
|
|
final BatteryEntry existingSipper = batteryEntryList.valueAt(index);
|
|
existingSipper.add(consumer);
|
|
}
|
|
}
|
|
|
|
final BatteryConsumer deviceConsumer = mBatteryUsageStats.getAggregateBatteryConsumer(
|
|
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
|
|
final BatteryConsumer appsConsumer = mBatteryUsageStats.getAggregateBatteryConsumer(
|
|
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
|
|
|
|
for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
|
|
componentId++) {
|
|
if (!showAllApps
|
|
&& mBatteryUtils.shouldHideDevicePowerComponent(deviceConsumer, componentId)) {
|
|
continue;
|
|
}
|
|
|
|
results.add(new BatteryEntry(mContext, componentId,
|
|
deviceConsumer.getConsumedPower(componentId),
|
|
appsConsumer.getConsumedPower(componentId),
|
|
deviceConsumer.getUsageDurationMillis(componentId)));
|
|
}
|
|
|
|
for (int componentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
|
|
componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
|
|
+ deviceConsumer.getCustomPowerComponentCount();
|
|
componentId++) {
|
|
if (!showAllApps) {
|
|
continue;
|
|
}
|
|
|
|
results.add(new BatteryEntry(mContext, componentId,
|
|
deviceConsumer.getCustomPowerComponentName(componentId),
|
|
deviceConsumer.getConsumedPowerForCustomComponent(componentId),
|
|
appsConsumer.getConsumedPowerForCustomComponent(componentId)));
|
|
}
|
|
|
|
if (showAllApps) {
|
|
final List<UserBatteryConsumer> userBatteryConsumers =
|
|
mBatteryUsageStats.getUserBatteryConsumers();
|
|
for (int i = 0, size = userBatteryConsumers.size(); i < size; i++) {
|
|
final UserBatteryConsumer consumer = userBatteryConsumers.get(i);
|
|
results.add(new BatteryEntry(mContext, mHandler, mUserManager,
|
|
consumer, /* isHidden */ true, Process.INVALID_UID, null, null,
|
|
loadDataInBackground));
|
|
}
|
|
}
|
|
|
|
final int numUidSippers = batteryEntryList.size();
|
|
|
|
for (int i = 0; i < numUidSippers; i++) {
|
|
results.add(batteryEntryList.valueAt(i));
|
|
}
|
|
|
|
// The sort order must have changed, so re-sort based on total power use.
|
|
results.sort(BatteryEntry.COMPARATOR);
|
|
return results;
|
|
}
|
|
|
|
private int getRealUid(UidBatteryConsumer consumer) {
|
|
int realUid = consumer.getUid();
|
|
|
|
// Check if this UID is a shared GID. If so, we combine it with the OWNER's
|
|
// actual app UID.
|
|
if (isSharedGid(consumer.getUid())) {
|
|
realUid = UserHandle.getUid(UserHandle.USER_SYSTEM,
|
|
UserHandle.getAppIdFromSharedAppGid(consumer.getUid()));
|
|
}
|
|
|
|
// Check if this UID is a system UID (mediaserver, logd, nfc, drm, etc).
|
|
if (isSystemUid(realUid)
|
|
&& !MEDIASERVER_PACKAGE_NAME.equals(consumer.getPackageWithHighestDrain())) {
|
|
// Use the system UID for all UIDs running in their own sandbox that
|
|
// are not apps. We exclude mediaserver because we already are expected to
|
|
// report that as a separate item.
|
|
realUid = Process.SYSTEM_UID;
|
|
}
|
|
return realUid;
|
|
}
|
|
|
|
@VisibleForTesting
|
|
void setUsageSummary(Preference preference, BatteryEntry entry) {
|
|
// Only show summary when usage time is longer than one minute
|
|
final long usageTimeMs = entry.getTimeInForegroundMs();
|
|
if (shouldShowSummary(entry) && usageTimeMs >= DateUtils.MINUTE_IN_MILLIS) {
|
|
final CharSequence timeSequence =
|
|
StringUtil.formatElapsedTime(mContext, usageTimeMs, false, false);
|
|
preference.setSummary(
|
|
entry.isHidden()
|
|
? timeSequence
|
|
: TextUtils.expandTemplate(mContext.getText(R.string.battery_used_for),
|
|
timeSequence));
|
|
}
|
|
}
|
|
|
|
private void cacheRemoveAllPrefs(PreferenceGroup group) {
|
|
mPreferenceCache = new ArrayMap<>();
|
|
final int N = group.getPreferenceCount();
|
|
for (int i = 0; i < N; i++) {
|
|
Preference p = group.getPreference(i);
|
|
if (TextUtils.isEmpty(p.getKey())) {
|
|
continue;
|
|
}
|
|
mPreferenceCache.put(p.getKey(), p);
|
|
}
|
|
}
|
|
|
|
private boolean shouldShowSummary(BatteryEntry entry) {
|
|
final CharSequence[] allowlistPackages = mContext.getResources()
|
|
.getTextArray(R.array.allowlist_hide_summary_in_battery_usage);
|
|
final String target = entry.getDefaultPackageName();
|
|
|
|
for (CharSequence packageName : allowlistPackages) {
|
|
if (TextUtils.equals(target, packageName)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static boolean isSharedGid(int uid) {
|
|
return UserHandle.getAppIdFromSharedAppGid(uid) > 0;
|
|
}
|
|
|
|
private static boolean isSystemUid(int uid) {
|
|
final int appUid = UserHandle.getAppId(uid);
|
|
return appUid >= Process.SYSTEM_UID && appUid < Process.FIRST_APPLICATION_UID;
|
|
}
|
|
|
|
private BatteryUsageStats getFakeStats() {
|
|
BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(new String[0])
|
|
.setDischargePercentage(100);
|
|
|
|
float use = 500;
|
|
final AggregateBatteryConsumer.Builder appsBatteryConsumerBuilder =
|
|
builder.getAggregateBatteryConsumerBuilder(
|
|
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
|
|
final AggregateBatteryConsumer.Builder deviceBatteryConsumerBuilder =
|
|
builder.getAggregateBatteryConsumerBuilder(
|
|
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
|
|
for (@BatteryConsumer.PowerComponent int componentId : new int[]{
|
|
BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY,
|
|
BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
|
|
BatteryConsumer.POWER_COMPONENT_CAMERA,
|
|
BatteryConsumer.POWER_COMPONENT_FLASHLIGHT,
|
|
BatteryConsumer.POWER_COMPONENT_IDLE,
|
|
BatteryConsumer.POWER_COMPONENT_MEMORY,
|
|
BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
|
|
BatteryConsumer.POWER_COMPONENT_PHONE,
|
|
BatteryConsumer.POWER_COMPONENT_SCREEN,
|
|
BatteryConsumer.POWER_COMPONENT_WIFI,
|
|
}) {
|
|
appsBatteryConsumerBuilder.setConsumedPower(componentId, use);
|
|
deviceBatteryConsumerBuilder.setConsumedPower(componentId, use * 2);
|
|
use += 5;
|
|
}
|
|
|
|
use = 450;
|
|
for (int i = 0; i < 100; i++) {
|
|
builder.getOrCreateUidBatteryConsumerBuilder(
|
|
new FakeUid(Process.FIRST_APPLICATION_UID + i))
|
|
.setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, 10000 + i * 1000)
|
|
.setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, 20000 + i * 2000)
|
|
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, use);
|
|
use += 1;
|
|
}
|
|
|
|
// Simulate dex2oat process.
|
|
builder.getOrCreateUidBatteryConsumerBuilder(new FakeUid(Process.FIRST_APPLICATION_UID))
|
|
.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CPU, 100000)
|
|
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 1000.0)
|
|
.setPackageWithHighestDrain("dex2oat");
|
|
|
|
builder.getOrCreateUidBatteryConsumerBuilder(new FakeUid(Process.FIRST_APPLICATION_UID + 1))
|
|
.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CPU, 100000)
|
|
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 1000.0)
|
|
.setPackageWithHighestDrain("dex2oat");
|
|
|
|
builder.getOrCreateUidBatteryConsumerBuilder(
|
|
new FakeUid(UserHandle.getSharedAppGid(Process.LOG_UID)))
|
|
.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CPU, 100000)
|
|
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 900.0);
|
|
|
|
return builder.build();
|
|
}
|
|
|
|
private Preference getCachedPreference(String key) {
|
|
return mPreferenceCache != null ? mPreferenceCache.remove(key) : null;
|
|
}
|
|
|
|
private void removeCachedPrefs(PreferenceGroup group) {
|
|
for (Preference p : mPreferenceCache.values()) {
|
|
group.removePreference(p);
|
|
}
|
|
mPreferenceCache = null;
|
|
}
|
|
|
|
private int getCachedCount() {
|
|
return mPreferenceCache != null ? mPreferenceCache.size() : 0;
|
|
}
|
|
|
|
private void addNotAvailableMessage() {
|
|
final String NOT_AVAILABLE = "not_available";
|
|
Preference notAvailable = getCachedPreference(NOT_AVAILABLE);
|
|
if (notAvailable == null) {
|
|
notAvailable = new Preference(mPrefContext);
|
|
notAvailable.setKey(NOT_AVAILABLE);
|
|
notAvailable.setTitle(R.string.power_usage_not_available);
|
|
notAvailable.setSelectable(false);
|
|
mAppListGroup.addPreference(notAvailable);
|
|
}
|
|
}
|
|
}
|