Merge changes from topic "tip_new_page-pi-dev" into pi-dev

am: a276b8d580

Change-Id: Icb1d923c07e48466b7f024e091d01c69a001073e
This commit is contained in:
jackqdyulei
2018-03-07 04:10:32 +00:00
committed by android-build-merger
13 changed files with 1139 additions and 788 deletions

View File

@@ -25,7 +25,7 @@
android:key="battery_graph"/>
<PreferenceCategory
android:key="battery_usage_list"
android:title="@string/battery_detail_since_full_charge"/>
android:key="app_list"
android:title="@string/power_usage_list_summary"/>
</PreferenceScreen>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="power_usage_advanced_screen_legacy"
android:title="@string/advanced_battery_title"
settings:keywords="@string/keywords_battery">
<com.android.settings.fuelgauge.BatteryHistoryPreference
android:key="battery_graph_legacy"/>
<PreferenceCategory
android:key="battery_usage_list_legacy"
android:title="@string/battery_detail_since_full_charge"/>
</PreferenceScreen>

View File

@@ -66,8 +66,4 @@
</PreferenceCategory>
<PreferenceCategory
android:key="app_list"
android:title="@string/power_usage_list_summary"/>
</PreferenceScreen>

View File

@@ -22,6 +22,7 @@ import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.BatteryStats;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.UserHandle;
@@ -81,7 +82,7 @@ public class BatteryAppListPreferenceController extends AbstractPreferenceContro
private Context mPrefContext;
SparseArray<List<Anomaly>> mAnomalySparseArray;
private Handler mHandler = new Handler() {
private Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -149,7 +150,7 @@ public class BatteryAppListPreferenceController extends AbstractPreferenceContro
@Override
public boolean isAvailable() {
return FeatureFlagUtils.isEnabled(mContext, FeatureFlags.BATTERY_DISPLAY_APP_LIST);
return true;
}
@Override
@@ -186,12 +187,17 @@ public class BatteryAppListPreferenceController extends AbstractPreferenceContro
}
}
public void refreshAppListGroup(BatteryStatsHelper statsHelper, boolean showAllApps,
CharSequence timeSequence) {
public void refreshAppListGroup(BatteryStatsHelper statsHelper, boolean showAllApps) {
if (!isAvailable()) {
return;
}
mBatteryStatsHelper = statsHelper;
final long lastFullChargeTime = mBatteryUtils.calculateLastFullChargeTime(
mBatteryStatsHelper, System.currentTimeMillis());
final CharSequence timeSequence = StringUtil.formatRelativeTime(mContext,
lastFullChargeTime,
false);
final int resId = showAllApps ? R.string.power_usage_list_summary_device
: R.string.power_usage_list_summary;
mAppListGroup.setTitle(TextUtils.expandTemplate(mContext.getText(resId), timeSequence));

View File

@@ -13,134 +13,58 @@
*/
package com.android.settings.fuelgauge;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.UserManager;
import android.provider.SearchIndexableResource;
import android.support.annotation.ColorInt;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceGroup;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatterySipper.DrainType;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.datausage.DataUsageUtils;
import com.android.settings.fuelgauge.PowerUsageAdvanced.PowerUsageData.UsageType;
import com.android.settings.SettingsActivity;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.utils.StringUtil;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class PowerUsageAdvanced extends PowerUsageBase {
private static final String TAG = "AdvancedBatteryUsage";
private static final String KEY_BATTERY_GRAPH = "battery_graph";
private static final String KEY_BATTERY_USAGE_LIST = "battery_usage_list";
private static final int STATUS_TYPE = BatteryStats.STATS_SINCE_CHARGED;
private static final String KEY_APP_LIST = "app_list";
private static final String KEY_SHOW_ALL_APPS = "show_all_apps";
@VisibleForTesting
static final int MENU_TOGGLE_APPS = Menu.FIRST + 1;
@VisibleForTesting
final int[] mUsageTypes = {
UsageType.WIFI,
UsageType.CELL,
UsageType.SYSTEM,
UsageType.BLUETOOTH,
UsageType.USER,
UsageType.IDLE,
UsageType.APP,
UsageType.UNACCOUNTED,
UsageType.OVERCOUNTED};
@VisibleForTesting BatteryHistoryPreference mHistPref;
@VisibleForTesting PreferenceGroup mUsageListGroup;
BatteryHistoryPreference mHistPref;
private BatteryUtils mBatteryUtils;
private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
private PackageManager mPackageManager;
private UserManager mUserManager;
private Map<Integer, PowerUsageData> mBatteryDataMap;
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case BatteryEntry.MSG_UPDATE_NAME_ICON:
final int dischargeAmount = mStatsHelper.getStats().getDischargeAmount(
STATUS_TYPE);
final double totalPower = mStatsHelper.getTotalPower();
final BatteryEntry entry = (BatteryEntry) msg.obj;
final int usageType = extractUsageType(entry.sipper);
PowerUsageData usageData = mBatteryDataMap.get(usageType);
Preference pref = findPreference(String.valueOf(usageType));
if (pref != null && usageData != null) {
updateUsageDataSummary(usageData, totalPower, dischargeAmount);
pref.setSummary(usageData.summary);
}
break;
case BatteryEntry.MSG_REPORT_FULLY_DRAWN:
Activity activity = getActivity();
if (activity != null) {
activity.reportFullyDrawn();
}
break;
}
super.handleMessage(msg);
}
};
private BatteryAppListPreferenceController mBatteryAppListPreferenceController;
@VisibleForTesting
boolean mShowAllApps = false;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
final Context context = getContext();
mHistPref = (BatteryHistoryPreference) findPreference(KEY_BATTERY_GRAPH);
mUsageListGroup = (PreferenceGroup) findPreference(KEY_BATTERY_USAGE_LIST);
final Context context = getContext();
mPowerUsageFeatureProvider = FeatureFactory.getFactory(context)
.getPowerUsageFeatureProvider(context);
mPackageManager = context.getPackageManager();
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mBatteryUtils = BatteryUtils.getInstance(context);
// init the summary so other preferences won't have unnecessary move
updateHistPrefSummary(context);
}
@Override
public void onResume() {
super.onResume();
}
@Override
public void onPause() {
BatteryEntry.stopRequestQueue();
mHandler.removeMessages(BatteryEntry.MSG_UPDATE_NAME_ICON);
super.onPause();
restoreSavedInstance(icicle);
}
@Override
@@ -166,24 +90,63 @@ public class PowerUsageAdvanced extends PowerUsageBase {
return R.xml.power_usage_advanced;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.add(Menu.NONE, MENU_TOGGLE_APPS, Menu.NONE,
mShowAllApps ? R.string.hide_extra_apps : R.string.show_all_apps);
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_TOGGLE_APPS:
mShowAllApps = !mShowAllApps;
item.setTitle(mShowAllApps ? R.string.hide_extra_apps : R.string.show_all_apps);
mMetricsFeatureProvider.action(getContext(),
MetricsProto.MetricsEvent.ACTION_SETTINGS_MENU_BATTERY_APPS_TOGGLE,
mShowAllApps);
restartBatteryStatsLoader();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@VisibleForTesting
void restoreSavedInstance(Bundle savedInstance) {
if (savedInstance != null) {
mShowAllApps = savedInstance.getBoolean(KEY_SHOW_ALL_APPS, false);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(KEY_SHOW_ALL_APPS, mShowAllApps);
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
return null;
final List<AbstractPreferenceController> controllers = new ArrayList<>();
mBatteryAppListPreferenceController = new BatteryAppListPreferenceController(context,
KEY_APP_LIST, getLifecycle(), (SettingsActivity) getActivity(), this);
controllers.add(mBatteryAppListPreferenceController);
return controllers;
}
@Override
protected void refreshUi() {
final long startTime = System.currentTimeMillis();
final Context context = getContext();
if (context == null) {
return;
}
updatePreference(mHistPref);
refreshPowerUsageDataList(mStatsHelper, mUsageListGroup);
updateHistPrefSummary(context);
BatteryEntry.startRequestQueue();
BatteryUtils.logRuntime(TAG, "refreshUI", startTime);
mBatteryAppListPreferenceController.refreshAppListGroup(mStatsHelper, mShowAllApps);
}
private void updateHistPrefSummary(Context context) {
@@ -199,278 +162,6 @@ public class PowerUsageAdvanced extends PowerUsageBase {
}
}
@VisibleForTesting
void refreshPowerUsageDataList(BatteryStatsHelper statsHelper,
PreferenceGroup preferenceGroup) {
List<PowerUsageData> dataList = parsePowerUsageData(statsHelper);
preferenceGroup.removeAll();
for (int i = 0, size = dataList.size(); i < size; i++) {
final PowerUsageData batteryData = dataList.get(i);
if (shouldHideCategory(batteryData)) {
continue;
}
final PowerGaugePreference pref = new PowerGaugePreference(getPrefContext());
pref.setKey(String.valueOf(batteryData.usageType));
pref.setTitle(batteryData.titleResId);
pref.setSummary(batteryData.summary);
pref.setPercent(batteryData.percentage);
pref.setSelectable(false);
preferenceGroup.addPreference(pref);
}
}
@VisibleForTesting
@UsageType
int extractUsageType(BatterySipper sipper) {
final DrainType drainType = sipper.drainType;
final int uid = sipper.getUid();
if (drainType == DrainType.WIFI) {
return UsageType.WIFI;
} else if (drainType == DrainType.BLUETOOTH) {
return UsageType.BLUETOOTH;
} else if (drainType == DrainType.IDLE) {
return UsageType.IDLE;
} else if (drainType == DrainType.USER) {
return UsageType.USER;
} else if (drainType == DrainType.CELL) {
return UsageType.CELL;
} else if (drainType == DrainType.UNACCOUNTED) {
return UsageType.UNACCOUNTED;
} else if (drainType == DrainType.OVERCOUNTED) {
return UsageType.OVERCOUNTED;
} else if (mPowerUsageFeatureProvider.isTypeSystem(sipper)
|| mPowerUsageFeatureProvider.isTypeService(sipper)) {
return UsageType.SYSTEM;
} else {
return UsageType.APP;
}
}
@VisibleForTesting
boolean shouldHideCategory(PowerUsageData powerUsageData) {
return powerUsageData.usageType == UsageType.UNACCOUNTED
|| powerUsageData.usageType == UsageType.OVERCOUNTED
|| (powerUsageData.usageType == UsageType.USER && isSingleNormalUser())
|| (powerUsageData.usageType == UsageType.CELL
&& !DataUsageUtils.hasMobileData(getContext()));
}
@VisibleForTesting
boolean shouldShowBatterySipper(BatterySipper batterySipper) {
return batterySipper.drainType != DrainType.SCREEN;
}
@VisibleForTesting
List<PowerUsageData> parsePowerUsageData(BatteryStatsHelper statusHelper) {
final List<BatterySipper> batterySippers = statusHelper.getUsageList();
final Map<Integer, PowerUsageData> batteryDataMap = new HashMap<>();
for (final @UsageType Integer type : mUsageTypes) {
batteryDataMap.put(type, new PowerUsageData(type));
}
// Accumulate power usage based on usage type
for (final BatterySipper sipper : batterySippers) {
sipper.mPackages = mPackageManager.getPackagesForUid(sipper.getUid());
final PowerUsageData usageData = batteryDataMap.get(extractUsageType(sipper));
usageData.totalPowerMah += sipper.totalPowerMah;
if (sipper.drainType == DrainType.APP && sipper.usageTimeMs != 0) {
sipper.usageTimeMs = mBatteryUtils.getProcessTimeMs(
BatteryUtils.StatusType.FOREGROUND, sipper.uidObj, STATUS_TYPE);
}
usageData.totalUsageTimeMs += sipper.usageTimeMs;
if (shouldShowBatterySipper(sipper)) {
usageData.usageList.add(sipper);
}
}
final List<PowerUsageData> batteryDataList = new ArrayList<>(batteryDataMap.values());
final int dischargeAmount = statusHelper.getStats().getDischargeAmount(STATUS_TYPE);
final double totalPower = statusHelper.getTotalPower();
final double hiddenPower = calculateHiddenPower(batteryDataList);
for (final PowerUsageData usageData : batteryDataList) {
usageData.percentage = mBatteryUtils.calculateBatteryPercent(usageData.totalPowerMah,
totalPower, hiddenPower, dischargeAmount);
updateUsageDataSummary(usageData, totalPower, dischargeAmount);
}
Collections.sort(batteryDataList);
mBatteryDataMap = batteryDataMap;
return batteryDataList;
}
@VisibleForTesting
double calculateHiddenPower(List<PowerUsageData> batteryDataList) {
for (final PowerUsageData usageData : batteryDataList) {
if (usageData.usageType == UsageType.UNACCOUNTED) {
return usageData.totalPowerMah;
}
}
return 0;
}
@VisibleForTesting
void updateUsageDataSummary(PowerUsageData usageData, double totalPower, int dischargeAmount) {
if (shouldHideSummary(usageData)) {
return;
}
if (usageData.usageList.size() <= 1) {
CharSequence timeSequence = StringUtil.formatElapsedTime(getContext(),
usageData.totalUsageTimeMs, false);
usageData.summary = usageData.usageType == UsageType.IDLE ? timeSequence
: TextUtils.expandTemplate(getText(R.string.battery_used_for), timeSequence);
} else {
BatterySipper sipper = findBatterySipperWithMaxBatteryUsage(usageData.usageList);
BatteryEntry batteryEntry = new BatteryEntry(getContext(), mHandler, mUserManager,
sipper);
final double percentage = (sipper.totalPowerMah / totalPower) * dischargeAmount;
usageData.summary = getString(R.string.battery_used_by,
Utils.formatPercentage(percentage, true), batteryEntry.name);
}
}
@VisibleForTesting
boolean shouldHideSummary(PowerUsageData powerUsageData) {
@UsageType final int usageType = powerUsageData.usageType;
return usageType == UsageType.CELL
|| usageType == UsageType.BLUETOOTH
|| usageType == UsageType.WIFI
|| usageType == UsageType.APP
|| usageType == UsageType.SYSTEM;
}
@VisibleForTesting
BatterySipper findBatterySipperWithMaxBatteryUsage(List<BatterySipper> usageList) {
BatterySipper sipper = usageList.get(0);
for (int i = 1, size = usageList.size(); i < size; i++) {
final BatterySipper comparedSipper = usageList.get(i);
if (comparedSipper.totalPowerMah > sipper.totalPowerMah) {
sipper = comparedSipper;
}
}
return sipper;
}
@VisibleForTesting
void setPackageManager(PackageManager packageManager) {
mPackageManager = packageManager;
}
@VisibleForTesting
void setPowerUsageFeatureProvider(PowerUsageFeatureProvider provider) {
mPowerUsageFeatureProvider = provider;
}
@VisibleForTesting
void setUserManager(UserManager userManager) {
mUserManager = userManager;
}
@VisibleForTesting
void setBatteryUtils(BatteryUtils batteryUtils) {
mBatteryUtils = batteryUtils;
}
@VisibleForTesting
boolean isSingleNormalUser() {
int count = 0;
for (UserInfo userInfo : mUserManager.getUsers()) {
if (userInfo.isEnabled() && !userInfo.isManagedProfile()) {
count++;
}
}
return count == 1;
}
/**
* Class that contains data used in {@link PowerGaugePreference}.
*/
@VisibleForTesting
static class PowerUsageData implements Comparable<PowerUsageData> {
@Retention(RetentionPolicy.SOURCE)
@IntDef({UsageType.APP,
UsageType.WIFI,
UsageType.CELL,
UsageType.SYSTEM,
UsageType.BLUETOOTH,
UsageType.USER,
UsageType.IDLE,
UsageType.UNACCOUNTED,
UsageType.OVERCOUNTED})
public @interface UsageType {
int APP = 0;
int WIFI = 1;
int CELL = 2;
int SYSTEM = 3;
int BLUETOOTH = 4;
int USER = 5;
int IDLE = 6;
int UNACCOUNTED = 7;
int OVERCOUNTED = 8;
}
@StringRes
public int titleResId;
public CharSequence summary;
public double percentage;
public double totalPowerMah;
public long totalUsageTimeMs;
@ColorInt
public int iconColor;
@UsageType
public int usageType;
public List<BatterySipper> usageList;
public PowerUsageData(@UsageType int usageType) {
this(usageType, 0);
}
public PowerUsageData(@UsageType int usageType, double totalPower) {
this.usageType = usageType;
totalPowerMah = 0;
totalUsageTimeMs = 0;
titleResId = getTitleResId(usageType);
totalPowerMah = totalPower;
usageList = new ArrayList<>();
}
private int getTitleResId(@UsageType int usageType) {
switch (usageType) {
case UsageType.WIFI:
return R.string.power_wifi;
case UsageType.CELL:
return R.string.power_cell;
case UsageType.SYSTEM:
return R.string.power_system;
case UsageType.BLUETOOTH:
return R.string.power_bluetooth;
case UsageType.USER:
return R.string.power_user;
case UsageType.IDLE:
return R.string.power_idle;
case UsageType.UNACCOUNTED:
return R.string.power_unaccounted;
case UsageType.OVERCOUNTED:
return R.string.power_overcounted;
case UsageType.APP:
default:
return R.string.power_apps;
}
}
@Override
public int compareTo(@NonNull PowerUsageData powerUsageData) {
final int diff = Double.compare(powerUsageData.totalPowerMah, totalPowerMah);
return diff != 0 ? diff : usageType - powerUsageData.usageType;
}
}
public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() {
@Override
@@ -480,6 +171,16 @@ public class PowerUsageAdvanced extends PowerUsageBase {
sir.xmlResId = R.xml.power_usage_advanced;
return Arrays.asList(sir);
}
@Override
public List<AbstractPreferenceController> createPreferenceControllers(
Context context) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new BatteryAppListPreferenceController(context,
KEY_APP_LIST, null /* lifecycle */, null /* activity */,
null /* fragment */));
return controllers;
}
};
}

View File

@@ -0,0 +1,485 @@
/*
* 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.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.UserManager;
import android.provider.SearchIndexableResource;
import android.support.annotation.ColorInt;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceGroup;
import android.text.TextUtils;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatterySipper.DrainType;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.datausage.DataUsageUtils;
import com.android.settings.fuelgauge.PowerUsageAdvancedLegacy.PowerUsageData.UsageType;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.utils.StringUtil;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class PowerUsageAdvancedLegacy extends PowerUsageBase {
private static final String TAG = "AdvancedBatteryUsage";
private static final String KEY_BATTERY_GRAPH = "battery_graph_legacy";
private static final String KEY_BATTERY_USAGE_LIST = "battery_usage_list_legacy";
private static final int STATUS_TYPE = BatteryStats.STATS_SINCE_CHARGED;
@VisibleForTesting
final int[] mUsageTypes = {
UsageType.WIFI,
UsageType.CELL,
UsageType.SYSTEM,
UsageType.BLUETOOTH,
UsageType.USER,
UsageType.IDLE,
UsageType.APP,
UsageType.UNACCOUNTED,
UsageType.OVERCOUNTED};
@VisibleForTesting BatteryHistoryPreference mHistPref;
@VisibleForTesting PreferenceGroup mUsageListGroup;
private BatteryUtils mBatteryUtils;
private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
private PackageManager mPackageManager;
private UserManager mUserManager;
private Map<Integer, PowerUsageData> mBatteryDataMap;
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case BatteryEntry.MSG_UPDATE_NAME_ICON:
final int dischargeAmount = mStatsHelper.getStats().getDischargeAmount(
STATUS_TYPE);
final double totalPower = mStatsHelper.getTotalPower();
final BatteryEntry entry = (BatteryEntry) msg.obj;
final int usageType = extractUsageType(entry.sipper);
PowerUsageData usageData = mBatteryDataMap.get(usageType);
Preference pref = findPreference(String.valueOf(usageType));
if (pref != null && usageData != null) {
updateUsageDataSummary(usageData, totalPower, dischargeAmount);
pref.setSummary(usageData.summary);
}
break;
case BatteryEntry.MSG_REPORT_FULLY_DRAWN:
Activity activity = getActivity();
if (activity != null) {
activity.reportFullyDrawn();
}
break;
}
super.handleMessage(msg);
}
};
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mHistPref = (BatteryHistoryPreference) findPreference(KEY_BATTERY_GRAPH);
mUsageListGroup = (PreferenceGroup) findPreference(KEY_BATTERY_USAGE_LIST);
final Context context = getContext();
mPowerUsageFeatureProvider = FeatureFactory.getFactory(context)
.getPowerUsageFeatureProvider(context);
mPackageManager = context.getPackageManager();
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mBatteryUtils = BatteryUtils.getInstance(context);
// init the summary so other preferences won't have unnecessary move
updateHistPrefSummary(context);
}
@Override
public void onResume() {
super.onResume();
}
@Override
public void onPause() {
BatteryEntry.stopRequestQueue();
mHandler.removeMessages(BatteryEntry.MSG_UPDATE_NAME_ICON);
super.onPause();
}
@Override
public void onDestroy() {
super.onDestroy();
if (getActivity().isChangingConfigurations()) {
BatteryEntry.clearUidCache();
}
}
@Override
public int getMetricsCategory() {
return MetricsProto.MetricsEvent.FUELGAUGE_BATTERY_HISTORY_DETAIL;
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.power_usage_advanced_legacy;
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
return null;
}
@Override
protected void refreshUi() {
final long startTime = System.currentTimeMillis();
final Context context = getContext();
if (context == null) {
return;
}
updatePreference(mHistPref);
refreshPowerUsageDataList(mStatsHelper, mUsageListGroup);
updateHistPrefSummary(context);
BatteryEntry.startRequestQueue();
BatteryUtils.logRuntime(TAG, "refreshUI", startTime);
}
private void updateHistPrefSummary(Context context) {
Intent batteryIntent =
context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
final boolean plugged = batteryIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) != 0;
if (mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(context) && !plugged) {
mHistPref.setBottomSummary(
mPowerUsageFeatureProvider.getAdvancedUsageScreenInfoString());
} else {
mHistPref.hideBottomSummary();
}
}
@VisibleForTesting
void refreshPowerUsageDataList(BatteryStatsHelper statsHelper,
PreferenceGroup preferenceGroup) {
List<PowerUsageData> dataList = parsePowerUsageData(statsHelper);
preferenceGroup.removeAll();
for (int i = 0, size = dataList.size(); i < size; i++) {
final PowerUsageData batteryData = dataList.get(i);
if (shouldHideCategory(batteryData)) {
continue;
}
final PowerGaugePreference pref = new PowerGaugePreference(getPrefContext());
pref.setKey(String.valueOf(batteryData.usageType));
pref.setTitle(batteryData.titleResId);
pref.setSummary(batteryData.summary);
pref.setPercent(batteryData.percentage);
pref.setSelectable(false);
preferenceGroup.addPreference(pref);
}
}
@VisibleForTesting
@UsageType
int extractUsageType(BatterySipper sipper) {
final DrainType drainType = sipper.drainType;
final int uid = sipper.getUid();
if (drainType == DrainType.WIFI) {
return UsageType.WIFI;
} else if (drainType == DrainType.BLUETOOTH) {
return UsageType.BLUETOOTH;
} else if (drainType == DrainType.IDLE) {
return UsageType.IDLE;
} else if (drainType == DrainType.USER) {
return UsageType.USER;
} else if (drainType == DrainType.CELL) {
return UsageType.CELL;
} else if (drainType == DrainType.UNACCOUNTED) {
return UsageType.UNACCOUNTED;
} else if (drainType == DrainType.OVERCOUNTED) {
return UsageType.OVERCOUNTED;
} else if (mPowerUsageFeatureProvider.isTypeSystem(sipper)
|| mPowerUsageFeatureProvider.isTypeService(sipper)) {
return UsageType.SYSTEM;
} else {
return UsageType.APP;
}
}
@VisibleForTesting
boolean shouldHideCategory(PowerUsageData powerUsageData) {
return powerUsageData.usageType == UsageType.UNACCOUNTED
|| powerUsageData.usageType == UsageType.OVERCOUNTED
|| (powerUsageData.usageType == UsageType.USER && isSingleNormalUser())
|| (powerUsageData.usageType == UsageType.CELL
&& !DataUsageUtils.hasMobileData(getContext()));
}
@VisibleForTesting
boolean shouldShowBatterySipper(BatterySipper batterySipper) {
return batterySipper.drainType != DrainType.SCREEN;
}
@VisibleForTesting
List<PowerUsageData> parsePowerUsageData(BatteryStatsHelper statusHelper) {
final List<BatterySipper> batterySippers = statusHelper.getUsageList();
final Map<Integer, PowerUsageData> batteryDataMap = new HashMap<>();
for (final @UsageType Integer type : mUsageTypes) {
batteryDataMap.put(type, new PowerUsageData(type));
}
// Accumulate power usage based on usage type
for (final BatterySipper sipper : batterySippers) {
sipper.mPackages = mPackageManager.getPackagesForUid(sipper.getUid());
final PowerUsageData usageData = batteryDataMap.get(extractUsageType(sipper));
usageData.totalPowerMah += sipper.totalPowerMah;
if (sipper.drainType == DrainType.APP && sipper.usageTimeMs != 0) {
sipper.usageTimeMs = mBatteryUtils.getProcessTimeMs(
BatteryUtils.StatusType.FOREGROUND, sipper.uidObj, STATUS_TYPE);
}
usageData.totalUsageTimeMs += sipper.usageTimeMs;
if (shouldShowBatterySipper(sipper)) {
usageData.usageList.add(sipper);
}
}
final List<PowerUsageData> batteryDataList = new ArrayList<>(batteryDataMap.values());
final int dischargeAmount = statusHelper.getStats().getDischargeAmount(STATUS_TYPE);
final double totalPower = statusHelper.getTotalPower();
final double hiddenPower = calculateHiddenPower(batteryDataList);
for (final PowerUsageData usageData : batteryDataList) {
usageData.percentage = mBatteryUtils.calculateBatteryPercent(usageData.totalPowerMah,
totalPower, hiddenPower, dischargeAmount);
updateUsageDataSummary(usageData, totalPower, dischargeAmount);
}
Collections.sort(batteryDataList);
mBatteryDataMap = batteryDataMap;
return batteryDataList;
}
@VisibleForTesting
double calculateHiddenPower(List<PowerUsageData> batteryDataList) {
for (final PowerUsageData usageData : batteryDataList) {
if (usageData.usageType == UsageType.UNACCOUNTED) {
return usageData.totalPowerMah;
}
}
return 0;
}
@VisibleForTesting
void updateUsageDataSummary(PowerUsageData usageData, double totalPower, int dischargeAmount) {
if (shouldHideSummary(usageData)) {
return;
}
if (usageData.usageList.size() <= 1) {
CharSequence timeSequence = StringUtil.formatElapsedTime(getContext(),
usageData.totalUsageTimeMs, false);
usageData.summary = usageData.usageType == UsageType.IDLE ? timeSequence
: TextUtils.expandTemplate(getText(R.string.battery_used_for), timeSequence);
} else {
BatterySipper sipper = findBatterySipperWithMaxBatteryUsage(usageData.usageList);
BatteryEntry batteryEntry = new BatteryEntry(getContext(), mHandler, mUserManager,
sipper);
final double percentage = (sipper.totalPowerMah / totalPower) * dischargeAmount;
usageData.summary = getString(R.string.battery_used_by,
Utils.formatPercentage(percentage, true), batteryEntry.name);
}
}
@VisibleForTesting
boolean shouldHideSummary(PowerUsageData powerUsageData) {
@UsageType final int usageType = powerUsageData.usageType;
return usageType == UsageType.CELL
|| usageType == UsageType.BLUETOOTH
|| usageType == UsageType.WIFI
|| usageType == UsageType.APP
|| usageType == UsageType.SYSTEM;
}
@VisibleForTesting
BatterySipper findBatterySipperWithMaxBatteryUsage(List<BatterySipper> usageList) {
BatterySipper sipper = usageList.get(0);
for (int i = 1, size = usageList.size(); i < size; i++) {
final BatterySipper comparedSipper = usageList.get(i);
if (comparedSipper.totalPowerMah > sipper.totalPowerMah) {
sipper = comparedSipper;
}
}
return sipper;
}
@VisibleForTesting
void setPackageManager(PackageManager packageManager) {
mPackageManager = packageManager;
}
@VisibleForTesting
void setPowerUsageFeatureProvider(PowerUsageFeatureProvider provider) {
mPowerUsageFeatureProvider = provider;
}
@VisibleForTesting
void setUserManager(UserManager userManager) {
mUserManager = userManager;
}
@VisibleForTesting
void setBatteryUtils(BatteryUtils batteryUtils) {
mBatteryUtils = batteryUtils;
}
@VisibleForTesting
boolean isSingleNormalUser() {
int count = 0;
for (UserInfo userInfo : mUserManager.getUsers()) {
if (userInfo.isEnabled() && !userInfo.isManagedProfile()) {
count++;
}
}
return count == 1;
}
/**
* Class that contains data used in {@link PowerGaugePreference}.
*/
@VisibleForTesting
static class PowerUsageData implements Comparable<PowerUsageData> {
@Retention(RetentionPolicy.SOURCE)
@IntDef({UsageType.APP,
UsageType.WIFI,
UsageType.CELL,
UsageType.SYSTEM,
UsageType.BLUETOOTH,
UsageType.USER,
UsageType.IDLE,
UsageType.UNACCOUNTED,
UsageType.OVERCOUNTED})
public @interface UsageType {
int APP = 0;
int WIFI = 1;
int CELL = 2;
int SYSTEM = 3;
int BLUETOOTH = 4;
int USER = 5;
int IDLE = 6;
int UNACCOUNTED = 7;
int OVERCOUNTED = 8;
}
@StringRes
public int titleResId;
public CharSequence summary;
public double percentage;
public double totalPowerMah;
public long totalUsageTimeMs;
@ColorInt
public int iconColor;
@UsageType
public int usageType;
public List<BatterySipper> usageList;
public PowerUsageData(@UsageType int usageType) {
this(usageType, 0);
}
public PowerUsageData(@UsageType int usageType, double totalPower) {
this.usageType = usageType;
totalPowerMah = 0;
totalUsageTimeMs = 0;
titleResId = getTitleResId(usageType);
totalPowerMah = totalPower;
usageList = new ArrayList<>();
}
private int getTitleResId(@UsageType int usageType) {
switch (usageType) {
case UsageType.WIFI:
return R.string.power_wifi;
case UsageType.CELL:
return R.string.power_cell;
case UsageType.SYSTEM:
return R.string.power_system;
case UsageType.BLUETOOTH:
return R.string.power_bluetooth;
case UsageType.USER:
return R.string.power_user;
case UsageType.IDLE:
return R.string.power_idle;
case UsageType.UNACCOUNTED:
return R.string.power_unaccounted;
case UsageType.OVERCOUNTED:
return R.string.power_overcounted;
case UsageType.APP:
default:
return R.string.power_apps;
}
}
@Override
public int compareTo(@NonNull PowerUsageData powerUsageData) {
final int diff = Double.compare(powerUsageData.totalPowerMah, totalPowerMah);
return diff != 0 ? diff : usageType - powerUsageData.usageType;
}
}
public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() {
@Override
public List<SearchIndexableResource> getXmlResourcesToIndex(
Context context, boolean enabled) {
final SearchIndexableResource sir = new SearchIndexableResource(context);
sir.xmlResId = R.xml.power_usage_advanced_legacy;
return Arrays.asList(sir);
}
};
}

View File

@@ -39,6 +39,7 @@ import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
import com.android.settings.applications.LayoutPreference;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.SummaryLoader;
import com.android.settings.display.BatteryPercentagePreferenceController;
import com.android.settings.fuelgauge.anomaly.Anomaly;
@@ -49,7 +50,6 @@ import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.utils.PowerUtil;
import com.android.settingslib.utils.StringUtil;
@@ -68,7 +68,6 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
static final String TAG = "PowerUsageSummary";
private static final boolean DEBUG = false;
private static final String KEY_APP_LIST = "app_list";
private static final String KEY_BATTERY_HEADER = "battery_header";
private static final String KEY_BATTERY_TIP = "battery_tip";
@@ -80,7 +79,10 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
static final int BATTERY_INFO_LOADER = 1;
@VisibleForTesting
static final int BATTERY_TIP_LOADER = 2;
private static final int MENU_STATS_TYPE = Menu.FIRST;
@VisibleForTesting
static final int MENU_STATS_TYPE = Menu.FIRST;
@VisibleForTesting
static final int MENU_ADVANCED_BATTERY = Menu.FIRST + 1;
public static final int DEBUG_INFO_LOADER = 3;
@VisibleForTesting
@@ -101,7 +103,6 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
SparseArray<List<Anomaly>> mAnomalySparseArray;
@VisibleForTesting
BatteryHeaderPreferenceController mBatteryHeaderPreferenceController;
private BatteryAppListPreferenceController mBatteryAppListPreferenceController;
private BatteryTipPreferenceController mBatteryTipPreferenceController;
private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;
@@ -231,9 +232,6 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
mBatteryHeaderPreferenceController = new BatteryHeaderPreferenceController(
context, activity, this /* host */, lifecycle);
controllers.add(mBatteryHeaderPreferenceController);
mBatteryAppListPreferenceController = new BatteryAppListPreferenceController(context,
KEY_APP_LIST, lifecycle, activity, this);
controllers.add(mBatteryAppListPreferenceController);
mBatteryTipPreferenceController = new BatteryTipPreferenceController(context,
KEY_BATTERY_TIP, (SettingsActivity) getActivity(), this /* fragment */, this /*
BatteryTipListener */);
@@ -250,6 +248,8 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
.setAlphabeticShortcut('t');
}
menu.add(Menu.NONE, MENU_ADVANCED_BATTERY, Menu.NONE, R.string.advanced_battery_title);
super.onCreateOptionsMenu(menu, inflater);
}
@@ -260,11 +260,6 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
@Override
public boolean onOptionsItemSelected(MenuItem item) {
final SettingsActivity sa = (SettingsActivity) getActivity();
final Context context = getContext();
final MetricsFeatureProvider metricsFeatureProvider =
FeatureFactory.getFactory(context).getMetricsFeatureProvider();
switch (item.getItemId()) {
case MENU_STATS_TYPE:
if (mStatsType == BatteryStats.STATS_SINCE_CHARGED) {
@@ -274,6 +269,13 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
}
refreshUi();
return true;
case MENU_ADVANCED_BATTERY:
new SubSettingLauncher(getContext())
.setDestination(PowerUsageAdvanced.class.getName())
.setSourceMetricsCategory(getMetricsCategory())
.setTitle(R.string.advanced_battery_title)
.launch();
return true;
default:
return super.onOptionsItemSelected(item);
}
@@ -294,11 +296,6 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
updateLastFullChargePreference(lastFullChargeTime);
mScreenUsagePref.setSubtitle(StringUtil.formatElapsedTime(getContext(),
mBatteryUtils.calculateScreenUsageTime(mStatsHelper), false));
final CharSequence timeSequence = StringUtil.formatRelativeTime(context, lastFullChargeTime,
false);
mBatteryAppListPreferenceController.refreshAppListGroup(mStatsHelper,
false /* showAllApps */, timeSequence);
}
@VisibleForTesting

View File

@@ -415,7 +415,7 @@ public class PowerUsageSummaryLegacy extends PowerUsageBase implements
private void performBatteryHeaderClick() {
if (mPowerFeatureProvider.isAdvancedUiEnabled()) {
new SubSettingLauncher(getContext())
.setDestination(PowerUsageAdvanced.class.getName())
.setDestination(PowerUsageAdvancedLegacy.class.getName())
.setSourceMetricsCategory(getMetricsCategory())
.setTitle(R.string.advanced_battery_title)
.launch();

View File

@@ -49,8 +49,9 @@ import com.android.settings.display.NightDisplaySettings;
import com.android.settings.display.ScreenZoomSettings;
import com.android.settings.dream.DreamSettings;
import com.android.settings.enterprise.EnterprisePrivacySettings;
import com.android.settings.fuelgauge.batterysaver.BatterySaverSettings;
import com.android.settings.fuelgauge.PowerUsageAdvanced;
import com.android.settings.fuelgauge.PowerUsageAdvancedLegacy;
import com.android.settings.fuelgauge.batterysaver.BatterySaverSettings;
import com.android.settings.fuelgauge.PowerUsageSummary;
import com.android.settings.fuelgauge.SmartBatterySettings;
import com.android.settings.gestures.AssistGestureSettings;
@@ -122,6 +123,7 @@ public class SearchIndexableResourcesImpl implements SearchIndexableResources {
addIndex(ZenModeSettings.class);
addIndex(StorageSettings.class);
addIndex(PowerUsageAdvanced.class);
addIndex(PowerUsageAdvancedLegacy.class);
addIndex(DefaultAppSettings.class);
addIndex(ManageAssist.class);
addIndex(SpecialAccessSettings.class);

View File

@@ -202,20 +202,6 @@ public class BatteryAppListPreferenceControllerTest {
assertThat(mPreferenceController.shouldHideSipper(mNormalBatterySipper)).isFalse();
}
@Test
public void testIsAvailable_featureOn_returnTrue() {
FeatureFlagUtils.setEnabled(mContext, FeatureFlags.BATTERY_DISPLAY_APP_LIST, true);
assertThat(mPreferenceController.isAvailable()).isTrue();
}
@Test
public void testIsAvailable_featureOff_returnFalse() {
FeatureFlagUtils.setEnabled(mContext, FeatureFlags.BATTERY_DISPLAY_APP_LIST, false);
assertThat(mPreferenceController.isAvailable()).isFalse();
}
@Test
public void testNeverUseFakeData() {
assertThat(BatteryAppListPreferenceController.USE_FAKE_DATA).isFalse();

View File

@@ -0,0 +1,437 @@
/*
* 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 static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.net.ConnectivityManager;
import android.os.UserManager;
import android.support.v7.preference.PreferenceCategory;
import android.support.v7.preference.PreferenceGroup;
import android.support.v7.preference.PreferenceManager;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatterySipper.DrainType;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.testutils.BatteryTestUtils;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.fuelgauge.PowerUsageAdvancedLegacy.PowerUsageData;
import com.android.settings.fuelgauge.PowerUsageAdvancedLegacy.PowerUsageData.UsageType;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class)
public class PowerUsageAdvancedLegacyTest {
private static final int FAKE_UID_1 = 50;
private static final int FAKE_UID_2 = 100;
private static final int DISCHARGE_AMOUNT = 60;
private static final double TYPE_APP_USAGE = 80;
private static final double TYPE_BLUETOOTH_USAGE = 50;
private static final double TYPE_WIFI_USAGE = 0;
private static final double TOTAL_USAGE = TYPE_APP_USAGE * 2 + TYPE_BLUETOOTH_USAGE
+ TYPE_WIFI_USAGE;
private static final double TOTAL_POWER = 500;
private static final double PRECISION = 0.001;
private static final String STUB_STRING = "stub_string";
@Mock
private BatterySipper mNormalBatterySipper;
@Mock
private BatterySipper mMaxBatterySipper;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private BatteryStatsHelper mBatteryStatsHelper;
@Mock
private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
@Mock
private PackageManager mPackageManager;
@Mock
private UserManager mUserManager;
@Mock
private BatteryHistoryPreference mHistPref;
@Mock
private PreferenceGroup mUsageListGroup;
@Mock
private ConnectivityManager mConnectivityManager;
@Mock
private UserInfo mNormalUserInfo;
@Mock
private UserInfo mManagedUserInfo;
private PowerUsageAdvancedLegacy mPowerUsageAdvanced;
private PowerUsageData mPowerUsageData;
private Context mShadowContext;
private Intent mDischargingBatteryIntent;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mShadowContext = spy(RuntimeEnvironment.application);
mPowerUsageAdvanced = spy(new PowerUsageAdvancedLegacy());
List<BatterySipper> batterySippers = new ArrayList<>();
batterySippers.add(new BatterySipper(DrainType.APP,
new FakeUid(FAKE_UID_1), TYPE_APP_USAGE));
batterySippers.add(new BatterySipper(DrainType.APP,
new FakeUid(FAKE_UID_2), TYPE_APP_USAGE));
batterySippers.add(new BatterySipper(DrainType.BLUETOOTH, new FakeUid(FAKE_UID_1),
TYPE_BLUETOOTH_USAGE));
batterySippers.add(new BatterySipper(DrainType.WIFI, new FakeUid(FAKE_UID_1),
TYPE_WIFI_USAGE));
mDischargingBatteryIntent = BatteryTestUtils.getDischargingIntent();
doReturn(mDischargingBatteryIntent).when(mShadowContext).registerReceiver(any(), any());
when(mBatteryStatsHelper.getStats().getDischargeAmount(anyInt())).thenReturn(
DISCHARGE_AMOUNT);
when(mBatteryStatsHelper.getUsageList()).thenReturn(batterySippers);
when(mBatteryStatsHelper.getTotalPower()).thenReturn(TOTAL_USAGE);
when(mPowerUsageAdvanced.getContext()).thenReturn(mShadowContext);
doReturn(STUB_STRING).when(mPowerUsageAdvanced).getString(anyInt(), any(), any());
doReturn(STUB_STRING).when(mPowerUsageAdvanced).getString(anyInt(), any());
doReturn(mShadowContext.getText(R.string.battery_used_for)).when(
mPowerUsageAdvanced).getText(R.string.battery_used_for);
mPowerUsageAdvanced.setPackageManager(mPackageManager);
mPowerUsageAdvanced.setPowerUsageFeatureProvider(mPowerUsageFeatureProvider);
mPowerUsageAdvanced.setUserManager(mUserManager);
mPowerUsageAdvanced.setBatteryUtils(BatteryUtils.getInstance(mShadowContext));
when(mShadowContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(
mConnectivityManager);
mPowerUsageData = new PowerUsageData(UsageType.USER);
mMaxBatterySipper.totalPowerMah = TYPE_BLUETOOTH_USAGE;
mMaxBatterySipper.drainType = DrainType.BLUETOOTH;
mNormalBatterySipper.drainType = DrainType.SCREEN;
doReturn(true).when(mNormalUserInfo).isEnabled();
doReturn(false).when(mNormalUserInfo).isManagedProfile();
doReturn(true).when(mManagedUserInfo).isEnabled();
doReturn(true).when(mManagedUserInfo).isManagedProfile();
}
@Test
public void testPrefs_shouldNotBeSelectable() {
PreferenceManager pm = new PreferenceManager(mShadowContext);
when(mPowerUsageAdvanced.getPreferenceManager()).thenReturn(pm);
PreferenceGroup prefGroup = spy(new PreferenceCategory(mShadowContext));
when(prefGroup.getPreferenceManager()).thenReturn(pm);
mPowerUsageAdvanced.refreshPowerUsageDataList(mBatteryStatsHelper, prefGroup);
assertThat(prefGroup.getPreferenceCount()).isAtLeast(1);
for (int i = 0, count = prefGroup.getPreferenceCount(); i < count; i++) {
PowerGaugePreference pref = (PowerGaugePreference) prefGroup.getPreference(i);
assertThat(pref.isSelectable()).isFalse();
}
}
@Test
public void testExtractUsageType_TypeSystem_ReturnSystem() {
mNormalBatterySipper.drainType = DrainType.APP;
when(mPowerUsageFeatureProvider.isTypeSystem(any())).thenReturn(true);
assertThat(mPowerUsageAdvanced.extractUsageType(mNormalBatterySipper))
.isEqualTo(UsageType.SYSTEM);
}
@Test
public void testExtractUsageType_TypeEqualsToDrainType_ReturnRelevantType() {
final DrainType drainTypes[] = {DrainType.WIFI, DrainType.BLUETOOTH, DrainType.IDLE,
DrainType.USER, DrainType.CELL, DrainType.UNACCOUNTED};
final int usageTypes[] = {UsageType.WIFI, UsageType.BLUETOOTH, UsageType.IDLE,
UsageType.USER, UsageType.CELL, UsageType.UNACCOUNTED};
assertThat(drainTypes.length).isEqualTo(usageTypes.length);
for (int i = 0, size = drainTypes.length; i < size; i++) {
mNormalBatterySipper.drainType = drainTypes[i];
assertThat(mPowerUsageAdvanced.extractUsageType(mNormalBatterySipper))
.isEqualTo(usageTypes[i]);
}
}
@Test
public void testExtractUsageType_TypeService_ReturnSystem() {
mNormalBatterySipper.drainType = DrainType.APP;
when(mNormalBatterySipper.getUid()).thenReturn(FAKE_UID_1);
when(mPowerUsageFeatureProvider.isTypeService(any())).thenReturn(true);
assertThat(mPowerUsageAdvanced.extractUsageType(mNormalBatterySipper))
.isEqualTo(UsageType.SYSTEM);
}
@Test
public void testParsePowerUsageData_PercentageCalculatedCorrectly() {
final double percentApp = TYPE_APP_USAGE * 2 / TOTAL_USAGE * DISCHARGE_AMOUNT;
final double percentWifi = TYPE_WIFI_USAGE / TOTAL_USAGE * DISCHARGE_AMOUNT;
final double percentBluetooth = TYPE_BLUETOOTH_USAGE / TOTAL_USAGE * DISCHARGE_AMOUNT;
List<PowerUsageData> batteryData =
mPowerUsageAdvanced.parsePowerUsageData(mBatteryStatsHelper);
for (PowerUsageData data : batteryData) {
switch (data.usageType) {
case UsageType.WIFI:
assertThat(data.percentage).isWithin(PRECISION).of(percentWifi);
break;
case UsageType.APP:
assertThat(data.percentage).isWithin(PRECISION).of(percentApp);
break;
case UsageType.BLUETOOTH:
assertThat(data.percentage).isWithin(PRECISION).of(percentBluetooth);
break;
default:
break;
}
}
}
@Test
public void testUpdateUsageDataSummary_onlyOneApp_showUsageTime() {
final String expectedSummary = "Used for 0m";
mPowerUsageData.usageList.add(mNormalBatterySipper);
mPowerUsageAdvanced.updateUsageDataSummary(mPowerUsageData, TOTAL_POWER, DISCHARGE_AMOUNT);
assertThat(mPowerUsageData.summary.toString()).isEqualTo(expectedSummary);
}
@Test
public void testUpdateUsageDataSummary_typeIdle_showUsageTime() {
mPowerUsageData.usageType = UsageType.IDLE;
mPowerUsageData.usageList.add(mNormalBatterySipper);
mPowerUsageAdvanced.updateUsageDataSummary(mPowerUsageData, TOTAL_POWER, DISCHARGE_AMOUNT);
assertThat(mPowerUsageData.summary.toString()).isEqualTo("0m");
}
@Test
public void testUpdateUsageDataSummary_moreThanOneApp_showMaxUsageApp() {
mPowerUsageData.usageList.add(mNormalBatterySipper);
mPowerUsageData.usageList.add(mMaxBatterySipper);
doReturn(mMaxBatterySipper).when(mPowerUsageAdvanced)
.findBatterySipperWithMaxBatteryUsage(mPowerUsageData.usageList);
final double percentage = (TYPE_BLUETOOTH_USAGE / TOTAL_POWER) * DISCHARGE_AMOUNT;
mPowerUsageAdvanced.updateUsageDataSummary(mPowerUsageData, TOTAL_POWER, DISCHARGE_AMOUNT);
verify(mPowerUsageAdvanced).getString(eq(R.string.battery_used_by),
eq(Utils.formatPercentage(percentage, true)), any());
}
@Test
public void testFindBatterySipperWithMaxBatteryUsage_findCorrectOne() {
mPowerUsageData.usageList.add(mNormalBatterySipper);
mPowerUsageData.usageList.add(mMaxBatterySipper);
BatterySipper sipper =
mPowerUsageAdvanced.findBatterySipperWithMaxBatteryUsage(mPowerUsageData.usageList);
assertThat(sipper).isEqualTo(mMaxBatterySipper);
}
@Test
public void testInit_ContainsAllUsageType() {
final int[] usageTypeSet = mPowerUsageAdvanced.mUsageTypes;
assertThat(usageTypeSet).asList().containsExactly(UsageType.APP, UsageType.WIFI,
UsageType.CELL, UsageType.BLUETOOTH, UsageType.IDLE, UsageType.USER,
UsageType.SYSTEM, UsageType.UNACCOUNTED, UsageType.OVERCOUNTED);
}
@Test
public void testPowerUsageData_SortedByUsage() {
List<PowerUsageData> dataList = new ArrayList<>();
dataList.add(new PowerUsageData(UsageType.WIFI, TYPE_WIFI_USAGE));
dataList.add(new PowerUsageData(UsageType.BLUETOOTH, TYPE_BLUETOOTH_USAGE));
dataList.add(new PowerUsageData(UsageType.APP, TYPE_APP_USAGE));
Collections.sort(dataList);
for (int i = 1, size = dataList.size(); i < size; i++) {
assertThat(dataList.get(i - 1).totalPowerMah).isAtLeast(dataList.get(i).totalPowerMah);
}
}
@Test
public void testShouldHideCategory_typeUnAccounted_returnTrue() {
mPowerUsageData.usageType = UsageType.UNACCOUNTED;
assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isTrue();
}
@Test
public void testShouldHideCategory_typeOverCounted_returnTrue() {
mPowerUsageData.usageType = UsageType.OVERCOUNTED;
assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isTrue();
}
@Test
public void testShouldHideCategory_typeUserAndOnlyOneNormalUser_returnTrue() {
mPowerUsageData.usageType = UsageType.USER;
List<UserInfo> userInfos = new ArrayList<>();
userInfos.add(mNormalUserInfo);
userInfos.add(mManagedUserInfo);
doReturn(userInfos).when(mUserManager).getUsers();
assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isTrue();
}
@Test
public void testShouldHideCategory_typeCellWhileNotSupported_returnTrue() {
mPowerUsageData.usageType = UsageType.CELL;
doReturn(false).when(mConnectivityManager)
.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isTrue();
}
@Test
public void testShouldHideCategory_typeCellWhileSupported_returnFalse() {
mPowerUsageData.usageType = UsageType.CELL;
doReturn(true).when(mConnectivityManager).isNetworkSupported(
ConnectivityManager.TYPE_MOBILE);
assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isFalse();
}
@Test
public void testShouldHideCategory_typeUserAndMoreThanOne_returnFalse() {
mPowerUsageData.usageType = UsageType.USER;
List<UserInfo> userInfos = new ArrayList<>();
userInfos.add(mNormalUserInfo);
userInfos.add(mNormalUserInfo);
doReturn(userInfos).when(mUserManager).getUsers();
assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isFalse();
}
@Test
public void testShouldHideCategory_typeNormal_returnFalse() {
mPowerUsageData.usageType = UsageType.APP;
assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isFalse();
}
@Test
public void testShouldHideSummary_typeCell_returnTrue() {
mPowerUsageData.usageType = UsageType.CELL;
assertThat(mPowerUsageAdvanced.shouldHideSummary(mPowerUsageData)).isTrue();
}
@Test
public void testShouldHideSummary_typeSystem_returnTrue() {
mPowerUsageData.usageType = UsageType.SYSTEM;
assertThat(mPowerUsageAdvanced.shouldHideSummary(mPowerUsageData)).isTrue();
}
@Test
public void testShouldHideSummary_typeWifi_returnTrue() {
mPowerUsageData.usageType = UsageType.WIFI;
assertThat(mPowerUsageAdvanced.shouldHideSummary(mPowerUsageData)).isTrue();
}
@Test
public void testShouldHideSummary_typeBluetooth_returnTrue() {
mPowerUsageData.usageType = UsageType.BLUETOOTH;
assertThat(mPowerUsageAdvanced.shouldHideSummary(mPowerUsageData)).isTrue();
}
@Test
public void testShouldHideSummary_typeApp_returnTrue() {
mPowerUsageData.usageType = UsageType.APP;
assertThat(mPowerUsageAdvanced.shouldHideSummary(mPowerUsageData)).isTrue();
}
@Test
public void testShouldHideSummary_typeNormal_returnFalse() {
mPowerUsageData.usageType = UsageType.IDLE;
assertThat(mPowerUsageAdvanced.shouldHideSummary(mPowerUsageData)).isFalse();
}
@Test
public void testShouldShowBatterySipper_typeScreen_returnFalse() {
mNormalBatterySipper.drainType = DrainType.SCREEN;
assertThat(mPowerUsageAdvanced.shouldShowBatterySipper(mNormalBatterySipper)).isFalse();
}
@Test
public void testShouldShowBatterySipper_typeNormal_returnTrue() {
mNormalBatterySipper.drainType = DrainType.APP;
assertThat(mPowerUsageAdvanced.shouldShowBatterySipper(mNormalBatterySipper)).isTrue();
}
@Test
public void testCalculateHiddenPower_returnCorrectPower() {
List<PowerUsageData> powerUsageDataList = new ArrayList<>();
final double unaccountedPower = 100;
final double normalPower = 150;
powerUsageDataList.add(new PowerUsageData(UsageType.UNACCOUNTED, unaccountedPower));
powerUsageDataList.add(new PowerUsageData(UsageType.APP, normalPower));
powerUsageDataList.add(new PowerUsageData(UsageType.CELL, normalPower));
assertThat(mPowerUsageAdvanced.calculateHiddenPower(powerUsageDataList))
.isWithin(PRECISION).of(unaccountedPower);
}
@Test
public void testRefreshUi_addsSubtextWhenAppropriate() {
// Mock out all the battery stuff
mPowerUsageAdvanced.mHistPref = mHistPref;
mPowerUsageAdvanced.mStatsHelper = mBatteryStatsHelper;
doReturn(new ArrayList<PowerUsageData>())
.when(mPowerUsageAdvanced).parsePowerUsageData(any());
doReturn("").when(mPowerUsageAdvanced).getString(anyInt());
mPowerUsageAdvanced.mUsageListGroup = mUsageListGroup;
// refresh the ui and check that text was not updated when enhanced prediction disabled
when(mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(any()))
.thenReturn(false);
mPowerUsageAdvanced.refreshUi();
verify(mHistPref, never()).setBottomSummary(any());
// refresh the ui and check that text was updated when enhanced prediction enabled
when(mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(any())).thenReturn(true);
mPowerUsageAdvanced.refreshUi();
verify(mHistPref, atLeastOnce()).setBottomSummary(any());
}
}

View File

@@ -16,34 +16,25 @@
package com.android.settings.fuelgauge;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.net.ConnectivityManager;
import android.os.UserManager;
import android.support.v7.preference.PreferenceCategory;
import android.support.v7.preference.PreferenceGroup;
import android.support.v7.preference.PreferenceManager;
import android.os.Bundle;
import android.support.v7.preference.PreferenceScreen;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatterySipper.DrainType;
import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.fuelgauge.PowerUsageAdvanced.PowerUsageData;
import com.android.settings.fuelgauge.PowerUsageAdvanced.PowerUsageData.UsageType;
import com.android.settings.testutils.BatteryTestUtils;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
@@ -54,385 +45,64 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class)
public class PowerUsageAdvancedTest {
private static final int FAKE_UID_1 = 50;
private static final int FAKE_UID_2 = 100;
private static final int DISCHARGE_AMOUNT = 60;
private static final double TYPE_APP_USAGE = 80;
private static final double TYPE_BLUETOOTH_USAGE = 50;
private static final double TYPE_WIFI_USAGE = 0;
private static final double TOTAL_USAGE = TYPE_APP_USAGE * 2 + TYPE_BLUETOOTH_USAGE
+ TYPE_WIFI_USAGE;
private static final double TOTAL_POWER = 500;
private static final double PRECISION = 0.001;
private static final String STUB_STRING = "stub_string";
@Mock
private BatterySipper mNormalBatterySipper;
@Mock
private BatterySipper mMaxBatterySipper;
private PreferenceScreen mPreferenceScreen;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private BatteryStatsHelper mBatteryStatsHelper;
private Menu mMenu;
@Mock
private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
private MenuInflater mMenuInflater;
@Mock
private PackageManager mPackageManager;
@Mock
private UserManager mUserManager;
@Mock
private BatteryHistoryPreference mHistPref;
@Mock
private PreferenceGroup mUsageListGroup;
@Mock
private ConnectivityManager mConnectivityManager;
@Mock
private UserInfo mNormalUserInfo;
@Mock
private UserInfo mManagedUserInfo;
private PowerUsageAdvanced mPowerUsageAdvanced;
private PowerUsageData mPowerUsageData;
private Context mShadowContext;
private Intent mDischargingBatteryIntent;
private MenuItem mToggleAppsMenu;
private Context mContext;
private PowerUsageAdvanced mFragment;
private FakeFeatureFactory mFeatureFactory;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mShadowContext = spy(RuntimeEnvironment.application);
mPowerUsageAdvanced = spy(new PowerUsageAdvanced());
List<BatterySipper> batterySippers = new ArrayList<>();
batterySippers.add(new BatterySipper(DrainType.APP,
new FakeUid(FAKE_UID_1), TYPE_APP_USAGE));
batterySippers.add(new BatterySipper(DrainType.APP,
new FakeUid(FAKE_UID_2), TYPE_APP_USAGE));
batterySippers.add(new BatterySipper(DrainType.BLUETOOTH, new FakeUid(FAKE_UID_1),
TYPE_BLUETOOTH_USAGE));
batterySippers.add(new BatterySipper(DrainType.WIFI, new FakeUid(FAKE_UID_1),
TYPE_WIFI_USAGE));
mContext = RuntimeEnvironment.application;
mFeatureFactory = FakeFeatureFactory.setupForTest();
when(mToggleAppsMenu.getItemId()).thenReturn(PowerUsageAdvanced.MENU_TOGGLE_APPS);
mDischargingBatteryIntent = BatteryTestUtils.getDischargingIntent();
doReturn(mDischargingBatteryIntent).when(mShadowContext).registerReceiver(any(), any());
when(mBatteryStatsHelper.getStats().getDischargeAmount(anyInt())).thenReturn(
DISCHARGE_AMOUNT);
when(mBatteryStatsHelper.getUsageList()).thenReturn(batterySippers);
when(mBatteryStatsHelper.getTotalPower()).thenReturn(TOTAL_USAGE);
when(mPowerUsageAdvanced.getContext()).thenReturn(mShadowContext);
doReturn(STUB_STRING).when(mPowerUsageAdvanced).getString(anyInt(), any(), any());
doReturn(STUB_STRING).when(mPowerUsageAdvanced).getString(anyInt(), any());
doReturn(mShadowContext.getText(R.string.battery_used_for)).when(
mPowerUsageAdvanced).getText(R.string.battery_used_for);
mPowerUsageAdvanced.setPackageManager(mPackageManager);
mPowerUsageAdvanced.setPowerUsageFeatureProvider(mPowerUsageFeatureProvider);
mPowerUsageAdvanced.setUserManager(mUserManager);
mPowerUsageAdvanced.setBatteryUtils(BatteryUtils.getInstance(mShadowContext));
when(mShadowContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(
mConnectivityManager);
mPowerUsageData = new PowerUsageData(UsageType.USER);
mMaxBatterySipper.totalPowerMah = TYPE_BLUETOOTH_USAGE;
mMaxBatterySipper.drainType = DrainType.BLUETOOTH;
mNormalBatterySipper.drainType = DrainType.SCREEN;
doReturn(true).when(mNormalUserInfo).isEnabled();
doReturn(false).when(mNormalUserInfo).isManagedProfile();
doReturn(true).when(mManagedUserInfo).isEnabled();
doReturn(true).when(mManagedUserInfo).isManagedProfile();
mFragment = spy(new PowerUsageAdvanced());
mFragment.onAttach(mContext);
}
@Test
public void testPrefs_shouldNotBeSelectable() {
PreferenceManager pm = new PreferenceManager(mShadowContext);
when(mPowerUsageAdvanced.getPreferenceManager()).thenReturn(pm);
PreferenceGroup prefGroup = spy(new PreferenceCategory(mShadowContext));
when(prefGroup.getPreferenceManager()).thenReturn(pm);
public void testSaveInstanceState_showAllAppsRestored() {
Bundle bundle = new Bundle();
mFragment.mShowAllApps = true;
doReturn(mPreferenceScreen).when(mFragment).getPreferenceScreen();
mPowerUsageAdvanced.refreshPowerUsageDataList(mBatteryStatsHelper, prefGroup);
assertThat(prefGroup.getPreferenceCount()).isAtLeast(1);
for (int i = 0, count = prefGroup.getPreferenceCount(); i < count; i++) {
PowerGaugePreference pref = (PowerGaugePreference) prefGroup.getPreference(i);
assertThat(pref.isSelectable()).isFalse();
}
mFragment.onSaveInstanceState(bundle);
mFragment.restoreSavedInstance(bundle);
assertThat(mFragment.mShowAllApps).isTrue();
}
@Test
public void testExtractUsageType_TypeSystem_ReturnSystem() {
mNormalBatterySipper.drainType = DrainType.APP;
when(mPowerUsageFeatureProvider.isTypeSystem(any())).thenReturn(true);
public void testOptionsMenu_menuAppToggle_metricEventInvoked() {
mFragment.mShowAllApps = false;
doNothing().when(mFragment).restartBatteryStatsLoader();
assertThat(mPowerUsageAdvanced.extractUsageType(mNormalBatterySipper))
.isEqualTo(UsageType.SYSTEM);
mFragment.onOptionsItemSelected(mToggleAppsMenu);
verify(mFeatureFactory.metricsFeatureProvider).action(nullable(Context.class),
eq(MetricsProto.MetricsEvent.ACTION_SETTINGS_MENU_BATTERY_APPS_TOGGLE), eq(true));
}
@Test
public void testExtractUsageType_TypeEqualsToDrainType_ReturnRelevantType() {
final DrainType drainTypes[] = {DrainType.WIFI, DrainType.BLUETOOTH, DrainType.IDLE,
DrainType.USER, DrainType.CELL, DrainType.UNACCOUNTED};
final int usageTypes[] = {UsageType.WIFI, UsageType.BLUETOOTH, UsageType.IDLE,
UsageType.USER, UsageType.CELL, UsageType.UNACCOUNTED};
public void testOptionsMenu_toggleAppsEnabled() {
when(mFeatureFactory.powerUsageFeatureProvider.isPowerAccountingToggleEnabled())
.thenReturn(true);
mFragment.mShowAllApps = false;
assertThat(drainTypes.length).isEqualTo(usageTypes.length);
for (int i = 0, size = drainTypes.length; i < size; i++) {
mNormalBatterySipper.drainType = drainTypes[i];
assertThat(mPowerUsageAdvanced.extractUsageType(mNormalBatterySipper))
.isEqualTo(usageTypes[i]);
}
}
@Test
public void testExtractUsageType_TypeService_ReturnSystem() {
mNormalBatterySipper.drainType = DrainType.APP;
when(mNormalBatterySipper.getUid()).thenReturn(FAKE_UID_1);
when(mPowerUsageFeatureProvider.isTypeService(any())).thenReturn(true);
assertThat(mPowerUsageAdvanced.extractUsageType(mNormalBatterySipper))
.isEqualTo(UsageType.SYSTEM);
}
@Test
public void testParsePowerUsageData_PercentageCalculatedCorrectly() {
final double percentApp = TYPE_APP_USAGE * 2 / TOTAL_USAGE * DISCHARGE_AMOUNT;
final double percentWifi = TYPE_WIFI_USAGE / TOTAL_USAGE * DISCHARGE_AMOUNT;
final double percentBluetooth = TYPE_BLUETOOTH_USAGE / TOTAL_USAGE * DISCHARGE_AMOUNT;
List<PowerUsageData> batteryData =
mPowerUsageAdvanced.parsePowerUsageData(mBatteryStatsHelper);
for (PowerUsageData data : batteryData) {
switch (data.usageType) {
case UsageType.WIFI:
assertThat(data.percentage).isWithin(PRECISION).of(percentWifi);
break;
case UsageType.APP:
assertThat(data.percentage).isWithin(PRECISION).of(percentApp);
break;
case UsageType.BLUETOOTH:
assertThat(data.percentage).isWithin(PRECISION).of(percentBluetooth);
break;
default:
break;
}
}
}
@Test
public void testUpdateUsageDataSummary_onlyOneApp_showUsageTime() {
final String expectedSummary = "Used for 0m";
mPowerUsageData.usageList.add(mNormalBatterySipper);
mPowerUsageAdvanced.updateUsageDataSummary(mPowerUsageData, TOTAL_POWER, DISCHARGE_AMOUNT);
assertThat(mPowerUsageData.summary.toString()).isEqualTo(expectedSummary);
}
@Test
public void testUpdateUsageDataSummary_typeIdle_showUsageTime() {
mPowerUsageData.usageType = UsageType.IDLE;
mPowerUsageData.usageList.add(mNormalBatterySipper);
mPowerUsageAdvanced.updateUsageDataSummary(mPowerUsageData, TOTAL_POWER, DISCHARGE_AMOUNT);
assertThat(mPowerUsageData.summary.toString()).isEqualTo("0m");
}
@Test
public void testUpdateUsageDataSummary_moreThanOneApp_showMaxUsageApp() {
mPowerUsageData.usageList.add(mNormalBatterySipper);
mPowerUsageData.usageList.add(mMaxBatterySipper);
doReturn(mMaxBatterySipper).when(mPowerUsageAdvanced)
.findBatterySipperWithMaxBatteryUsage(mPowerUsageData.usageList);
final double percentage = (TYPE_BLUETOOTH_USAGE / TOTAL_POWER) * DISCHARGE_AMOUNT;
mPowerUsageAdvanced.updateUsageDataSummary(mPowerUsageData, TOTAL_POWER, DISCHARGE_AMOUNT);
verify(mPowerUsageAdvanced).getString(eq(R.string.battery_used_by),
eq(Utils.formatPercentage(percentage, true)), any());
}
@Test
public void testFindBatterySipperWithMaxBatteryUsage_findCorrectOne() {
mPowerUsageData.usageList.add(mNormalBatterySipper);
mPowerUsageData.usageList.add(mMaxBatterySipper);
BatterySipper sipper =
mPowerUsageAdvanced.findBatterySipperWithMaxBatteryUsage(mPowerUsageData.usageList);
assertThat(sipper).isEqualTo(mMaxBatterySipper);
}
@Test
public void testInit_ContainsAllUsageType() {
final int[] usageTypeSet = mPowerUsageAdvanced.mUsageTypes;
assertThat(usageTypeSet).asList().containsExactly(UsageType.APP, UsageType.WIFI,
UsageType.CELL, UsageType.BLUETOOTH, UsageType.IDLE, UsageType.USER,
UsageType.SYSTEM, UsageType.UNACCOUNTED, UsageType.OVERCOUNTED);
}
@Test
public void testPowerUsageData_SortedByUsage() {
List<PowerUsageData> dataList = new ArrayList<>();
dataList.add(new PowerUsageData(UsageType.WIFI, TYPE_WIFI_USAGE));
dataList.add(new PowerUsageData(UsageType.BLUETOOTH, TYPE_BLUETOOTH_USAGE));
dataList.add(new PowerUsageData(UsageType.APP, TYPE_APP_USAGE));
Collections.sort(dataList);
for (int i = 1, size = dataList.size(); i < size; i++) {
assertThat(dataList.get(i - 1).totalPowerMah).isAtLeast(dataList.get(i).totalPowerMah);
}
}
@Test
public void testShouldHideCategory_typeUnAccounted_returnTrue() {
mPowerUsageData.usageType = UsageType.UNACCOUNTED;
assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isTrue();
}
@Test
public void testShouldHideCategory_typeOverCounted_returnTrue() {
mPowerUsageData.usageType = UsageType.OVERCOUNTED;
assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isTrue();
}
@Test
public void testShouldHideCategory_typeUserAndOnlyOneNormalUser_returnTrue() {
mPowerUsageData.usageType = UsageType.USER;
List<UserInfo> userInfos = new ArrayList<>();
userInfos.add(mNormalUserInfo);
userInfos.add(mManagedUserInfo);
doReturn(userInfos).when(mUserManager).getUsers();
assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isTrue();
}
@Test
public void testShouldHideCategory_typeCellWhileNotSupported_returnTrue() {
mPowerUsageData.usageType = UsageType.CELL;
doReturn(false).when(mConnectivityManager)
.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isTrue();
}
@Test
public void testShouldHideCategory_typeCellWhileSupported_returnFalse() {
mPowerUsageData.usageType = UsageType.CELL;
doReturn(true).when(mConnectivityManager).isNetworkSupported(
ConnectivityManager.TYPE_MOBILE);
assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isFalse();
}
@Test
public void testShouldHideCategory_typeUserAndMoreThanOne_returnFalse() {
mPowerUsageData.usageType = UsageType.USER;
List<UserInfo> userInfos = new ArrayList<>();
userInfos.add(mNormalUserInfo);
userInfos.add(mNormalUserInfo);
doReturn(userInfos).when(mUserManager).getUsers();
assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isFalse();
}
@Test
public void testShouldHideCategory_typeNormal_returnFalse() {
mPowerUsageData.usageType = UsageType.APP;
assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isFalse();
}
@Test
public void testShouldHideSummary_typeCell_returnTrue() {
mPowerUsageData.usageType = UsageType.CELL;
assertThat(mPowerUsageAdvanced.shouldHideSummary(mPowerUsageData)).isTrue();
}
@Test
public void testShouldHideSummary_typeSystem_returnTrue() {
mPowerUsageData.usageType = UsageType.SYSTEM;
assertThat(mPowerUsageAdvanced.shouldHideSummary(mPowerUsageData)).isTrue();
}
@Test
public void testShouldHideSummary_typeWifi_returnTrue() {
mPowerUsageData.usageType = UsageType.WIFI;
assertThat(mPowerUsageAdvanced.shouldHideSummary(mPowerUsageData)).isTrue();
}
@Test
public void testShouldHideSummary_typeBluetooth_returnTrue() {
mPowerUsageData.usageType = UsageType.BLUETOOTH;
assertThat(mPowerUsageAdvanced.shouldHideSummary(mPowerUsageData)).isTrue();
}
@Test
public void testShouldHideSummary_typeApp_returnTrue() {
mPowerUsageData.usageType = UsageType.APP;
assertThat(mPowerUsageAdvanced.shouldHideSummary(mPowerUsageData)).isTrue();
}
@Test
public void testShouldHideSummary_typeNormal_returnFalse() {
mPowerUsageData.usageType = UsageType.IDLE;
assertThat(mPowerUsageAdvanced.shouldHideSummary(mPowerUsageData)).isFalse();
}
@Test
public void testShouldShowBatterySipper_typeScreen_returnFalse() {
mNormalBatterySipper.drainType = DrainType.SCREEN;
assertThat(mPowerUsageAdvanced.shouldShowBatterySipper(mNormalBatterySipper)).isFalse();
}
@Test
public void testShouldShowBatterySipper_typeNormal_returnTrue() {
mNormalBatterySipper.drainType = DrainType.APP;
assertThat(mPowerUsageAdvanced.shouldShowBatterySipper(mNormalBatterySipper)).isTrue();
}
@Test
public void testCalculateHiddenPower_returnCorrectPower() {
List<PowerUsageData> powerUsageDataList = new ArrayList<>();
final double unaccountedPower = 100;
final double normalPower = 150;
powerUsageDataList.add(new PowerUsageData(UsageType.UNACCOUNTED, unaccountedPower));
powerUsageDataList.add(new PowerUsageData(UsageType.APP, normalPower));
powerUsageDataList.add(new PowerUsageData(UsageType.CELL, normalPower));
assertThat(mPowerUsageAdvanced.calculateHiddenPower(powerUsageDataList))
.isWithin(PRECISION).of(unaccountedPower);
}
@Test
public void testRefreshUi_addsSubtextWhenAppropriate() {
// Mock out all the battery stuff
mPowerUsageAdvanced.mHistPref = mHistPref;
mPowerUsageAdvanced.mStatsHelper = mBatteryStatsHelper;
doReturn(new ArrayList<PowerUsageData>())
.when(mPowerUsageAdvanced).parsePowerUsageData(any());
doReturn("").when(mPowerUsageAdvanced).getString(anyInt());
mPowerUsageAdvanced.mUsageListGroup = mUsageListGroup;
// refresh the ui and check that text was not updated when enhanced prediction disabled
when(mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(any()))
.thenReturn(false);
mPowerUsageAdvanced.refreshUi();
verify(mHistPref, never()).setBottomSummary(any());
// refresh the ui and check that text was updated when enhanced prediction enabled
when(mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(any())).thenReturn(true);
mPowerUsageAdvanced.refreshUi();
verify(mHistPref, atLeastOnce()).setBottomSummary(any());
mFragment.onCreateOptionsMenu(mMenu, mMenuInflater);
verify(mMenu).add(Menu.NONE, PowerUsageAdvanced.MENU_TOGGLE_APPS, Menu.NONE,
R.string.show_all_apps);
}
}

View File

@@ -15,6 +15,8 @@
*/
package com.android.settings.fuelgauge;
import static com.android.settings.fuelgauge.PowerUsageSummary.MENU_ADVANCED_BATTERY;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
@@ -35,6 +37,8 @@ import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.SparseArray;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
@@ -53,7 +57,6 @@ import com.android.settingslib.core.AbstractPreferenceController;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
@@ -114,6 +117,12 @@ public class PowerUsageSummaryTest {
private LoaderManager mLoaderManager;
@Mock
private BatteryHeaderPreferenceController mBatteryHeaderPreferenceController;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Menu mMenu;
@Mock
private MenuInflater mMenuInflater;
@Mock
private MenuItem mAdvancedPageMenu;
private List<BatterySipper> mUsageList;
private Context mRealContext;
@@ -122,12 +131,13 @@ public class PowerUsageSummaryTest {
private BatteryMeterView mBatteryMeterView;
private PowerGaugePreference mScreenUsagePref;
private PowerGaugePreference mLastFullChargePref;
private Intent mIntent;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mRealContext = RuntimeEnvironment.application;
mRealContext = spy(RuntimeEnvironment.application);
mFeatureFactory = FakeFeatureFactory.setupForTest();
mScreenUsagePref = new PowerGaugePreference(mRealContext);
mLastFullChargePref = new PowerGaugePreference(mRealContext);
@@ -137,6 +147,7 @@ public class PowerUsageSummaryTest {
mBatteryMeterView.mDrawable = new BatteryMeterView.BatteryMeterDrawable(mRealContext, 0);
doNothing().when(mFragment).restartBatteryStatsLoader();
doReturn(mock(LoaderManager.class)).when(mFragment).getLoaderManager();
doReturn(MENU_ADVANCED_BATTERY).when(mAdvancedPageMenu).getItemId();
when(mFragment.getActivity()).thenReturn(mSettingsActivity);
when(mFeatureFactory.powerUsageFeatureProvider.getAdditionalBatteryInfoIntent())
@@ -294,6 +305,35 @@ public class PowerUsageSummaryTest {
verify(mBatteryHeaderPreferenceController, never()).quickUpdateHeaderPreference();
}
@Test
public void testOptionsMenu_advancedPageEnabled() {
when(mFeatureFactory.powerUsageFeatureProvider.isPowerAccountingToggleEnabled())
.thenReturn(true);
mFragment.onCreateOptionsMenu(mMenu, mMenuInflater);
verify(mMenu).add(Menu.NONE, MENU_ADVANCED_BATTERY, Menu.NONE,
R.string.advanced_battery_title);
}
@Test
public void testOptionsMenu_clickAdvancedPage_fireIntent() {
final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
doAnswer(invocation -> {
// Get the intent in which it has the app info bundle
mIntent = captor.getValue();
return true;
}).when(mRealContext).startActivity(captor.capture());
mFragment.onOptionsItemSelected(mAdvancedPageMenu);
assertThat(mIntent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)).isEqualTo(
PowerUsageAdvanced.class.getName());
assertThat(
mIntent.getIntExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, 0)).isEqualTo(
R.string.advanced_battery_title);
}
public static class TestFragment extends PowerUsageSummary {
private Context mContext;