diff --git a/res/xml/power_usage_advanced.xml b/res/xml/power_usage_advanced.xml index b31eb403db1..d32611ce969 100644 --- a/res/xml/power_usage_advanced.xml +++ b/res/xml/power_usage_advanced.xml @@ -25,7 +25,7 @@ android:key="battery_graph"/> + android:key="app_list" + android:title="@string/power_usage_list_summary"/> diff --git a/res/xml/power_usage_advanced_legacy.xml b/res/xml/power_usage_advanced_legacy.xml new file mode 100644 index 00000000000..26be727c038 --- /dev/null +++ b/res/xml/power_usage_advanced_legacy.xml @@ -0,0 +1,31 @@ + + + + + + + + + + diff --git a/res/xml/power_usage_summary.xml b/res/xml/power_usage_summary.xml index d89653ae5ce..ac96151557e 100644 --- a/res/xml/power_usage_summary.xml +++ b/res/xml/power_usage_summary.xml @@ -66,8 +66,4 @@ - - diff --git a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java index 3ba5ee47fc0..de01533272c 100644 --- a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java +++ b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java @@ -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> 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)); @@ -361,7 +367,7 @@ public class BatteryAppListPreferenceController extends AbstractPreferenceContro final long usageTimeMs = sipper.usageTimeMs; if (usageTimeMs >= DateUtils.MINUTE_IN_MILLIS) { final CharSequence timeSequence = - StringUtil.formatElapsedTime(mContext, usageTimeMs, false); + StringUtil.formatElapsedTime(mContext, usageTimeMs, false); preference.setSummary( (sipper.drainType != DrainType.APP || mBatteryUtils.shouldHideSipper(sipper)) ? timeSequence diff --git a/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java b/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java index 2e94e2c74bc..327a6c58bfa 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java +++ b/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java @@ -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 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 createPreferenceControllers(Context context) { - return null; + final List 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 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 parsePowerUsageData(BatteryStatsHelper statusHelper) { - final List batterySippers = statusHelper.getUsageList(); - final Map 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 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 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 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 { - - @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 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 createPreferenceControllers( + Context context) { + final List controllers = new ArrayList<>(); + controllers.add(new BatteryAppListPreferenceController(context, + KEY_APP_LIST, null /* lifecycle */, null /* activity */, + null /* fragment */)); + return controllers; + } }; } diff --git a/src/com/android/settings/fuelgauge/PowerUsageAdvancedLegacy.java b/src/com/android/settings/fuelgauge/PowerUsageAdvancedLegacy.java new file mode 100644 index 00000000000..a4e3fefeea7 --- /dev/null +++ b/src/com/android/settings/fuelgauge/PowerUsageAdvancedLegacy.java @@ -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 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 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 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 parsePowerUsageData(BatteryStatsHelper statusHelper) { + final List batterySippers = statusHelper.getUsageList(); + final Map 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 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 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 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 { + + @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 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 getXmlResourcesToIndex( + Context context, boolean enabled) { + final SearchIndexableResource sir = new SearchIndexableResource(context); + sir.xmlResId = R.xml.power_usage_advanced_legacy; + return Arrays.asList(sir); + } + }; + +} diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java index bf0b627f910..b64dc526bad 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java +++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java @@ -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> 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 diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummaryLegacy.java b/src/com/android/settings/fuelgauge/PowerUsageSummaryLegacy.java index 79425cb6b28..d321bb76f57 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageSummaryLegacy.java +++ b/src/com/android/settings/fuelgauge/PowerUsageSummaryLegacy.java @@ -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(); diff --git a/src/com/android/settings/search/SearchIndexableResourcesImpl.java b/src/com/android/settings/search/SearchIndexableResourcesImpl.java index 87c2a9107b7..1798d34e303 100644 --- a/src/com/android/settings/search/SearchIndexableResourcesImpl.java +++ b/src/com/android/settings/search/SearchIndexableResourcesImpl.java @@ -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); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryAppListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryAppListPreferenceControllerTest.java index b223a10ed21..8156428d136 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryAppListPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryAppListPreferenceControllerTest.java @@ -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(); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedLegacyTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedLegacyTest.java new file mode 100644 index 00000000000..756d9137e19 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedLegacyTest.java @@ -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 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 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 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 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 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 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()) + .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()); + } +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java index 81d40a36ac4..4a905b4603a 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java @@ -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 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]); - } - } + mFragment.onCreateOptionsMenu(mMenu, mMenuInflater); - @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 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 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 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 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 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()) - .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()); + verify(mMenu).add(Menu.NONE, PowerUsageAdvanced.MENU_TOGGLE_APPS, Menu.NONE, + R.string.show_all_apps); } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java index 7f6e39d4096..6176bef4544 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java @@ -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 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 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;