Add feature flag for battery settings page.

Create PowerUsageSummaryLegacy and power_usage_summary_legacy, which
contains the old implementaion.

Bug: 69797586
Test: RunSettingsRoboTests
Change-Id: Icb9c25f06889462a6eedea48ae166043a6636848
This commit is contained in:
jackqdyulei
2017-11-27 13:49:19 -08:00
parent 82789a1f39
commit 166102c3c0
12 changed files with 1038 additions and 12 deletions

View File

@@ -2213,16 +2213,13 @@
<activity android:name="Settings$PowerUsageSummaryActivity" <activity android:name="Settings$PowerUsageSummaryActivity"
android:label="@string/power_usage_summary_title" android:label="@string/power_usage_summary_title"
android:icon="@drawable/ic_settings_battery" android:icon="@drawable/ic_settings_battery"
android:taskAffinity=""> android:enabled="false">
<intent-filter android:priority="1"> <!-- TODO(b/69867246): add priority for this intent-filter -->
<intent-filter>
<action android:name="android.intent.action.POWER_USAGE_SUMMARY" /> <action android:name="android.intent.action.POWER_USAGE_SUMMARY" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
<intent-filter> <!-- TODO(b/69867246): add shortcut intent-filter -->
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="com.android.settings.SHORTCUT" />
</intent-filter>
<intent-filter android:priority="8"> <intent-filter android:priority="8">
<action android:name="com.android.settings.action.SETTINGS" /> <action android:name="com.android.settings.action.SETTINGS" />
</intent-filter> </intent-filter>
@@ -2241,6 +2238,27 @@
android:value="com.android.settings.fuelgauge.PowerUsageSummary" /> android:value="com.android.settings.fuelgauge.PowerUsageSummary" />
</activity-alias> </activity-alias>
<activity android:name=".Settings$PowerUsageSummaryLegacyActivity"
android:label="@string/power_usage_summary_title"
android:icon="@drawable/ic_settings_battery">
<intent-filter android:priority="1">
<action android:name="android.intent.action.POWER_USAGE_SUMMARY" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="com.android.settings.SHORTCUT" />
</intent-filter>
<intent-filter android:priority="8">
<action android:name="com.android.settings.action.SETTINGS" />
</intent-filter>
<meta-data android:name="com.android.settings.category"
android:value="com.android.settings.category.ia.homepage" />
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.fuelgauge.PowerUsageSummaryLegacy" />
</activity>
<activity android:name="Settings$BatterySaverSettingsActivity" <activity android:name="Settings$BatterySaverSettingsActivity"
android:label="@string/battery_saver" android:label="@string/battery_saver"
android:icon="@drawable/ic_settings_battery" android:icon="@drawable/ic_settings_battery"

View File

@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 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_summary_screen"
android:title="@string/power_usage_summary_title"
settings:keywords="@string/keywords_battery">
<com.android.settings.applications.LayoutPreference
android:key="battery_header"
android:selectable="true"
android:layout="@layout/battery_header"/>
<Preference
android:key="high_usage"
android:icon="@drawable/ic_battery_alert_24dp"
android:title="@string/power_high_usage_title"/>
<PreferenceCategory
android:key="device_usage_list">
<com.android.settings.fuelgauge.PowerGaugePreference
android:key="last_full_charge"
android:title="@string/battery_last_full_charge"
android:selectable="false"/>
<com.android.settings.fuelgauge.PowerGaugePreference
android:key="screen_usage"
android:title="@string/device_screen_usage"
android:selectable="false"/>
</PreferenceCategory>
<PreferenceCategory
android:key="power_management"
android:title="@string/battery_power_management">
<com.android.settings.widget.MasterSwitchPreference
android:fragment="com.android.settings.fuelgauge.BatterySaverSettings"
android:key="battery_saver_summary"
android:title="@string/battery_saver"/>
<SwitchPreference
android:key="battery_percentage"
android:title="@string/battery_percentage"
android:summary="@string/battery_percentage_description"/>
<!-- Cross-listed item, if you change this, also change it in display_settings.xml -->
<SwitchPreference
android:key="auto_brightness_battery"
android:title="@string/auto_brightness_title"
android:summary="@string/auto_brightness_summary"
settings:keywords="@string/keywords_display_auto_brightness"/>
<!-- Cross-listed item, if you change this, also change it in display_settings.xml -->
<com.android.settings.TimeoutListPreference
android:key="screen_timeout_battery"
android:title="@string/screen_timeout"
android:summary="@string/screen_timeout_summary"
android:entries="@array/screen_timeout_entries"
android:entryValues="@array/screen_timeout_values"/>
<!-- Cross-listed item, if you change this, also change it in display_settings.xml -->
<Preference
android:key="ambient_display_battery"
android:title="@string/ambient_display_screen_title"
android:fragment="com.android.settings.display.AmbientDisplaySettings" />
</PreferenceCategory>
<PreferenceCategory
android:key="app_list"
android:title="@string/power_usage_list_summary"/>
</PreferenceScreen>

View File

@@ -16,6 +16,7 @@
package com.android.settings; package com.android.settings;
import static com.android.settings.core.FeatureFlags.BATTERY_SETTINGS_V2;
import static com.android.settings.core.FeatureFlags.CONNECTED_DEVICE_V2; import static com.android.settings.core.FeatureFlags.CONNECTED_DEVICE_V2;
import android.os.Bundle; import android.os.Bundle;
@@ -72,7 +73,6 @@ public class Settings extends SettingsActivity {
public static class PrivacySettingsActivity extends SettingsActivity { /* empty */ } public static class PrivacySettingsActivity extends SettingsActivity { /* empty */ }
public static class FactoryResetActivity extends SettingsActivity { /* empty */ } public static class FactoryResetActivity extends SettingsActivity { /* empty */ }
public static class RunningServicesActivity extends SettingsActivity { /* empty */ } public static class RunningServicesActivity extends SettingsActivity { /* empty */ }
public static class PowerUsageSummaryActivity extends SettingsActivity { /* empty */ }
public static class BatterySaverSettingsActivity extends SettingsActivity { /* empty */ } public static class BatterySaverSettingsActivity extends SettingsActivity { /* empty */ }
public static class AccountSyncSettingsActivity extends SettingsActivity { /* empty */ } public static class AccountSyncSettingsActivity extends SettingsActivity { /* empty */ }
public static class AccountSyncSettingsInAddAccountActivity extends SettingsActivity { /* empty */ } public static class AccountSyncSettingsInAddAccountActivity extends SettingsActivity { /* empty */ }
@@ -174,6 +174,8 @@ public class Settings extends SettingsActivity {
} }
} }
public static class ConnectedDeviceDashboardActivityOld extends SettingsActivity {} public static class ConnectedDeviceDashboardActivityOld extends SettingsActivity {}
public static class PowerUsageSummaryActivity extends SettingsActivity { /* empty */ }
public static class PowerUsageSummaryLegacyActivity extends SettingsActivity { /* empty */ }
public static class AppAndNotificationDashboardActivity extends SettingsActivity {} public static class AppAndNotificationDashboardActivity extends SettingsActivity {}
public static class StorageDashboardActivity extends SettingsActivity {} public static class StorageDashboardActivity extends SettingsActivity {}
public static class UserAndAccountDashboardActivity extends SettingsActivity {} public static class UserAndAccountDashboardActivity extends SettingsActivity {}

View File

@@ -810,9 +810,17 @@ public class SettingsActivity extends SettingsDrawerActivity
Utils.showSimCardTile(this), isAdmin) Utils.showSimCardTile(this), isAdmin)
|| somethingChanged; || somethingChanged;
final boolean isBatterySettingsV2Enabled = FeatureFactory.getFactory(this)
.getPowerUsageFeatureProvider(this)
.isBatteryV2Enabled();
// Enable new battery page if v2 enabled
somethingChanged = setTileEnabled(new ComponentName(packageName, somethingChanged = setTileEnabled(new ComponentName(packageName,
Settings.PowerUsageSummaryActivity.class.getName()), Settings.PowerUsageSummaryActivity.class.getName()),
mBatteryPresent, isAdmin) || somethingChanged; mBatteryPresent && isBatterySettingsV2Enabled, isAdmin) || somethingChanged;
// Enable legacy battery page if v2 disabled
somethingChanged = setTileEnabled(new ComponentName(packageName,
Settings.PowerUsageSummaryLegacyActivity.class.getName()),
mBatteryPresent && !isBatterySettingsV2Enabled, isAdmin) || somethingChanged;
somethingChanged = setTileEnabled(new ComponentName(packageName, somethingChanged = setTileEnabled(new ComponentName(packageName,
Settings.UserSettingsActivity.class.getName()), Settings.UserSettingsActivity.class.getName()),

View File

@@ -25,4 +25,5 @@ public class FeatureFlags {
public static final String SUGGESTIONS_V2 = "new_settings_suggestion"; public static final String SUGGESTIONS_V2 = "new_settings_suggestion";
public static final String APP_INFO_V2 = "settings_app_info_v2"; public static final String APP_INFO_V2 = "settings_app_info_v2";
public static final String CONNECTED_DEVICE_V2 = "settings_connected_device_v2"; public static final String CONNECTED_DEVICE_V2 = "settings_connected_device_v2";
public static final String BATTERY_SETTINGS_V2 = "settings_battery_v2";
} }

View File

@@ -79,6 +79,7 @@ import com.android.settings.enterprise.EnterprisePrivacySettings;
import com.android.settings.fuelgauge.AdvancedPowerUsageDetail; import com.android.settings.fuelgauge.AdvancedPowerUsageDetail;
import com.android.settings.fuelgauge.BatterySaverSettings; import com.android.settings.fuelgauge.BatterySaverSettings;
import com.android.settings.fuelgauge.PowerUsageSummary; import com.android.settings.fuelgauge.PowerUsageSummary;
import com.android.settings.fuelgauge.PowerUsageSummaryLegacy;
import com.android.settings.gestures.AssistGestureSettings; import com.android.settings.gestures.AssistGestureSettings;
import com.android.settings.gestures.DoubleTapPowerSettings; import com.android.settings.gestures.DoubleTapPowerSettings;
import com.android.settings.gestures.DoubleTapScreenSettings; import com.android.settings.gestures.DoubleTapScreenSettings;
@@ -181,6 +182,7 @@ public class SettingsGateway {
AndroidBeam.class.getName(), AndroidBeam.class.getName(),
WifiDisplaySettings.class.getName(), WifiDisplaySettings.class.getName(),
PowerUsageSummary.class.getName(), PowerUsageSummary.class.getName(),
PowerUsageSummaryLegacy.class.getName(),
AccountSyncSettings.class.getName(), AccountSyncSettings.class.getName(),
AssistGestureSettings.class.getName(), AssistGestureSettings.class.getName(),
SwipeToNotificationSettings.class.getName(), SwipeToNotificationSettings.class.getName(),

View File

@@ -115,4 +115,9 @@ public interface PowerUsageFeatureProvider {
* enabled. This string notifies users that the estimate is using enhanced prediction. * enabled. This string notifies users that the estimate is using enhanced prediction.
*/ */
String getAdvancedUsageScreenInfoString(); String getAdvancedUsageScreenInfoString();
/**
* Checks whether to display the battery v2.
*/
boolean isBatteryV2Enabled();
} }

View File

@@ -16,12 +16,15 @@
package com.android.settings.fuelgauge; package com.android.settings.fuelgauge;
import static com.android.settings.core.FeatureFlags.BATTERY_SETTINGS_V2;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.os.Process; import android.os.Process;
import android.util.FeatureFlagUtils;
import android.util.SparseIntArray; import android.util.SparseIntArray;
import com.android.internal.os.BatterySipper; import com.android.internal.os.BatterySipper;
@@ -36,9 +39,11 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider
PACKAGE_CALENDAR_PROVIDER, PACKAGE_SYSTEMUI}; PACKAGE_CALENDAR_PROVIDER, PACKAGE_SYSTEMUI};
protected PackageManager mPackageManager; protected PackageManager mPackageManager;
protected Context mContext;
public PowerUsageFeatureProviderImpl(Context context) { public PowerUsageFeatureProviderImpl(Context context) {
mPackageManager = context.getPackageManager(); mPackageManager = context.getPackageManager();
mContext = context.getApplicationContext();
} }
@Override @Override
@@ -133,4 +138,9 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider
public String getAdvancedUsageScreenInfoString() { public String getAdvancedUsageScreenInfoString() {
return null; return null;
} }
@Override
public boolean isBatteryV2Enabled() {
return FeatureFlagUtils.isEnabled(mContext, BATTERY_SETTINGS_V2);
}
} }

View File

@@ -251,7 +251,7 @@ public class PowerUsageSummary extends PowerUsageBase implements
@Override @Override
public int getMetricsCategory() { public int getMetricsCategory() {
return MetricsEvent.FUELGAUGE_POWER_USAGE_SUMMARY; return MetricsEvent.FUELGAUGE_POWER_USAGE_SUMMARY_V2;
} }
@Override @Override

View File

@@ -0,0 +1,889 @@
/*
* Copyright (C) 2009 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.app.LoaderManager.LoaderCallbacks;
import android.content.Context;
import android.content.Loader;
import android.graphics.drawable.Drawable;
import android.os.BatteryStats;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Process;
import android.os.UserHandle;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceGroup;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.format.Formatter;
import android.util.Log;
import android.util.SparseArray;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.widget.TextView;
import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatterySipper.DrainType;
import com.android.internal.os.PowerProfile;
import com.android.settings.R;
import com.android.settings.Settings.HighPowerApplicationsActivity;
import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
import com.android.settings.applications.LayoutPreference;
import com.android.settings.applications.manageapplications.ManageApplications;
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
import com.android.settings.dashboard.SummaryLoader;
import com.android.settings.display.AmbientDisplayPreferenceController;
import com.android.settings.display.AutoBrightnessPreferenceController;
import com.android.settings.display.BatteryPercentagePreferenceController;
import com.android.settings.display.TimeoutPreferenceController;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy;
import com.android.settings.fuelgauge.anomaly.AnomalyDialogFragment.AnomalyDialogListener;
import com.android.settings.fuelgauge.anomaly.AnomalyLoader;
import com.android.settings.fuelgauge.anomaly.AnomalySummaryPreferenceController;
import com.android.settings.fuelgauge.anomaly.AnomalyUtils;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.AbstractPreferenceController;
import java.util.ArrayList;
import java.util.List;
/**
* Displays a list of apps and subsystems that consume power, ordered by how much power was
* consumed since the last time it was unplugged.
*
* This is the battery page used in Android O with the app usage list. It is also used for battery
* debug.
*/
public class PowerUsageSummaryLegacy extends PowerUsageBase implements
AnomalyDialogListener, OnLongClickListener, OnClickListener {
static final String TAG = "PowerUsageSummaryLegacy";
private static final boolean DEBUG = false;
private static final boolean USE_FAKE_DATA = false;
private static final String KEY_APP_LIST = "app_list";
private static final String KEY_BATTERY_HEADER = "battery_header";
private static final String KEY_SHOW_ALL_APPS = "show_all_apps";
private static final int MAX_ITEMS_TO_LIST = USE_FAKE_DATA ? 30 : 10;
private static final int MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP = 10;
private static final String KEY_SCREEN_USAGE = "screen_usage";
private static final String KEY_TIME_SINCE_LAST_FULL_CHARGE = "last_full_charge";
private static final String KEY_AUTO_BRIGHTNESS = "auto_brightness_battery";
private static final String KEY_SCREEN_TIMEOUT = "screen_timeout_battery";
private static final String KEY_AMBIENT_DISPLAY = "ambient_display_battery";
private static final String KEY_BATTERY_SAVER_SUMMARY = "battery_saver_summary";
private static final String KEY_HIGH_USAGE = "high_usage";
@VisibleForTesting
static final int ANOMALY_LOADER = 1;
@VisibleForTesting
static final int BATTERY_INFO_LOADER = 2;
private static final int MENU_STATS_TYPE = Menu.FIRST;
@VisibleForTesting
static final int MENU_HIGH_POWER_APPS = Menu.FIRST + 3;
@VisibleForTesting
static final int MENU_TOGGLE_APPS = Menu.FIRST + 4;
private static final int MENU_HELP = Menu.FIRST + 5;
public static final int DEBUG_INFO_LOADER = 3;
@VisibleForTesting
boolean mShowAllApps = false;
@VisibleForTesting
PowerGaugePreference mScreenUsagePref;
@VisibleForTesting
PowerGaugePreference mLastFullChargePref;
@VisibleForTesting
PowerUsageFeatureProvider mPowerFeatureProvider;
@VisibleForTesting
BatteryUtils mBatteryUtils;
@VisibleForTesting
LayoutPreference mBatteryLayoutPref;
/**
* SparseArray that maps uid to {@link Anomaly}, so we could find {@link Anomaly} by uid
*/
@VisibleForTesting
SparseArray<List<Anomaly>> mAnomalySparseArray;
@VisibleForTesting
PreferenceGroup mAppListGroup;
@VisibleForTesting
BatteryHeaderPreferenceController mBatteryHeaderPreferenceController;
private AnomalySummaryPreferenceController mAnomalySummaryPreferenceController;
private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;
private LoaderCallbacks<List<Anomaly>> mAnomalyLoaderCallbacks =
new LoaderCallbacks<List<Anomaly>>() {
@Override
public Loader<List<Anomaly>> onCreateLoader(int id, Bundle args) {
return new AnomalyLoader(getContext(), mStatsHelper);
}
@Override
public void onLoadFinished(Loader<List<Anomaly>> loader, List<Anomaly> data) {
final AnomalyUtils anomalyUtils = AnomalyUtils.getInstance(getContext());
anomalyUtils.logAnomalies(mMetricsFeatureProvider, data,
MetricsEvent.FUELGAUGE_POWER_USAGE_SUMMARY);
// show high usage preference if possible
mAnomalySummaryPreferenceController.updateAnomalySummaryPreference(data);
updateAnomalySparseArray(data);
refreshAnomalyIcon();
}
@Override
public void onLoaderReset(Loader<List<Anomaly>> loader) {
}
};
@VisibleForTesting
LoaderCallbacks<BatteryInfo> mBatteryInfoLoaderCallbacks =
new LoaderCallbacks<BatteryInfo>() {
@Override
public Loader<BatteryInfo> onCreateLoader(int i, Bundle bundle) {
return new BatteryInfoLoader(getContext(), mStatsHelper);
}
@Override
public void onLoadFinished(Loader<BatteryInfo> loader, BatteryInfo batteryInfo) {
mBatteryHeaderPreferenceController.updateHeaderPreference(batteryInfo);
}
@Override
public void onLoaderReset(Loader<BatteryInfo> loader) {
// do nothing
}
};
LoaderCallbacks<List<BatteryInfo>> mBatteryInfoDebugLoaderCallbacks =
new LoaderCallbacks<List<BatteryInfo>>() {
@Override
public Loader<List<BatteryInfo>> onCreateLoader(int i, Bundle bundle) {
return new DebugEstimatesLoader(getContext(), mStatsHelper);
}
@Override
public void onLoadFinished(Loader<List<BatteryInfo>> loader,
List<BatteryInfo> batteryInfos) {
final BatteryMeterView batteryView = (BatteryMeterView) mBatteryLayoutPref
.findViewById(R.id.battery_header_icon);
final TextView percentRemaining =
mBatteryLayoutPref.findViewById(R.id.battery_percent);
final TextView summary1 = mBatteryLayoutPref.findViewById(R.id.summary1);
final TextView summary2 = mBatteryLayoutPref.findViewById(R.id.summary2);
BatteryInfo oldInfo = batteryInfos.get(0);
BatteryInfo newInfo = batteryInfos.get(1);
percentRemaining.setText(Utils.formatPercentage(oldInfo.batteryLevel));
// set the text to the old estimate (copied from battery info). Note that this
// can sometimes say 0 time remaining because battery stats requires the phone
// be unplugged for a period of time before being willing ot make an estimate.
summary1.setText(mPowerFeatureProvider.getOldEstimateDebugString(
Formatter.formatShortElapsedTime(getContext(),
BatteryUtils.convertUsToMs(oldInfo.remainingTimeUs))));
// for this one we can just set the string directly
summary2.setText(mPowerFeatureProvider.getEnhancedEstimateDebugString(
Formatter.formatShortElapsedTime(getContext(),
BatteryUtils.convertUsToMs(newInfo.remainingTimeUs))));
batteryView.setBatteryLevel(oldInfo.batteryLevel);
batteryView.setCharging(!oldInfo.discharging);
}
@Override
public void onLoaderReset(Loader<List<BatteryInfo>> loader) {
}
};
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setAnimationAllowed(true);
initFeatureProvider();
mBatteryLayoutPref = (LayoutPreference) findPreference(KEY_BATTERY_HEADER);
mAppListGroup = (PreferenceGroup) findPreference(KEY_APP_LIST);
mScreenUsagePref = (PowerGaugePreference) findPreference(KEY_SCREEN_USAGE);
mLastFullChargePref = (PowerGaugePreference) findPreference(
KEY_TIME_SINCE_LAST_FULL_CHARGE);
mFooterPreferenceMixin.createFooterPreference().setTitle(R.string.battery_footer_summary);
mAnomalySummaryPreferenceController = new AnomalySummaryPreferenceController(
(SettingsActivity) getActivity(), this, MetricsEvent.FUELGAUGE_POWER_USAGE_SUMMARY);
mBatteryUtils = BatteryUtils.getInstance(getContext());
mAnomalySparseArray = new SparseArray<>();
restartBatteryInfoLoader();
restoreSavedInstance(icicle);
}
@Override
public int getMetricsCategory() {
return MetricsEvent.FUELGAUGE_POWER_USAGE_SUMMARY;
}
@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 void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(KEY_SHOW_ALL_APPS, mShowAllApps);
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
if (mAnomalySummaryPreferenceController.onPreferenceTreeClick(preference)) {
return true;
}
if (KEY_BATTERY_HEADER.equals(preference.getKey())) {
performBatteryHeaderClick();
return true;
} else if (!(preference instanceof PowerGaugePreference)) {
return super.onPreferenceTreeClick(preference);
}
PowerGaugePreference pgp = (PowerGaugePreference) preference;
BatteryEntry entry = pgp.getInfo();
AdvancedPowerUsageDetail.startBatteryDetailPage((SettingsActivity) getActivity(),
this, mStatsHelper, mStatsType, entry, pgp.getPercent(),
mAnomalySparseArray.get(entry.sipper.getUid()));
return super.onPreferenceTreeClick(preference);
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.power_usage_summary_legacy;
}
@Override
protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
mBatteryHeaderPreferenceController = new BatteryHeaderPreferenceController(
context, getActivity(), this /* host */, getLifecycle());
controllers.add(mBatteryHeaderPreferenceController);
controllers.add(new AutoBrightnessPreferenceController(context, KEY_AUTO_BRIGHTNESS));
controllers.add(new TimeoutPreferenceController(context, KEY_SCREEN_TIMEOUT));
controllers.add(new BatterySaverController(context, getLifecycle()));
controllers.add(new BatteryPercentagePreferenceController(context));
controllers.add(new AmbientDisplayPreferenceController(
context,
new AmbientDisplayConfiguration(context),
KEY_AMBIENT_DISPLAY));
return controllers;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
if (DEBUG) {
menu.add(Menu.NONE, MENU_STATS_TYPE, Menu.NONE, R.string.menu_stats_total)
.setIcon(com.android.internal.R.drawable.ic_menu_info_details)
.setAlphabeticShortcut('t');
}
menu.add(Menu.NONE, MENU_HIGH_POWER_APPS, Menu.NONE, R.string.high_power_apps);
if (mPowerFeatureProvider.isPowerAccountingToggleEnabled()) {
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 int getHelpResource() {
return R.string.help_url_battery;
}
@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) {
mStatsType = BatteryStats.STATS_SINCE_UNPLUGGED;
} else {
mStatsType = BatteryStats.STATS_SINCE_CHARGED;
}
refreshUi();
return true;
case MENU_HIGH_POWER_APPS:
Bundle args = new Bundle();
args.putString(ManageApplications.EXTRA_CLASSNAME,
HighPowerApplicationsActivity.class.getName());
sa.startPreferencePanel(this, ManageApplications.class.getName(), args,
R.string.high_power_apps, null, null, 0);
metricsFeatureProvider.action(context,
MetricsEvent.ACTION_SETTINGS_MENU_BATTERY_OPTIMIZATION);
return true;
case MENU_TOGGLE_APPS:
mShowAllApps = !mShowAllApps;
item.setTitle(mShowAllApps ? R.string.hide_extra_apps : R.string.show_all_apps);
metricsFeatureProvider.action(context,
MetricsEvent.ACTION_SETTINGS_MENU_BATTERY_APPS_TOGGLE, mShowAllApps);
restartBatteryStatsLoader(false /* clearHeader */);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@VisibleForTesting
void restoreSavedInstance(Bundle savedInstance) {
if (savedInstance != null) {
mShowAllApps = savedInstance.getBoolean(KEY_SHOW_ALL_APPS, false);
}
}
private void addNotAvailableMessage() {
final String NOT_AVAILABLE = "not_available";
Preference notAvailable = getCachedPreference(NOT_AVAILABLE);
if (notAvailable == null) {
notAvailable = new Preference(getPrefContext());
notAvailable.setKey(NOT_AVAILABLE);
notAvailable.setTitle(R.string.power_usage_not_available);
mAppListGroup.addPreference(notAvailable);
}
}
private void performBatteryHeaderClick() {
if (mPowerFeatureProvider.isAdvancedUiEnabled()) {
Utils.startWithFragment(getContext(), PowerUsageAdvanced.class.getName(), null,
null, 0, R.string.advanced_battery_title, null, getMetricsCategory());
} else {
mStatsHelper.storeStatsHistoryInFile(BatteryHistoryDetail.BATTERY_HISTORY_FILE);
Bundle args = new Bundle(2);
args.putString(BatteryHistoryDetail.EXTRA_STATS,
BatteryHistoryDetail.BATTERY_HISTORY_FILE);
args.putParcelable(BatteryHistoryDetail.EXTRA_BROADCAST,
mStatsHelper.getBatteryBroadcast());
Utils.startWithFragment(getContext(), BatteryHistoryDetail.class.getName(), args,
null, 0, R.string.history_details_title, null, getMetricsCategory());
}
}
private static boolean isSharedGid(int uid) {
return UserHandle.getAppIdFromSharedAppGid(uid) > 0;
}
private static boolean isSystemUid(int uid) {
final int appUid = UserHandle.getAppId(uid);
return appUid >= Process.SYSTEM_UID && appUid < Process.FIRST_APPLICATION_UID;
}
/**
* We want to coalesce some UIDs. For example, dex2oat runs under a shared gid that
* exists for all users of the same app. We detect this case and merge the power use
* for dex2oat to the device OWNER's use of the app.
*
* @return A sorted list of apps using power.
*/
private List<BatterySipper> getCoalescedUsageList(final List<BatterySipper> sippers) {
final SparseArray<BatterySipper> uidList = new SparseArray<>();
final ArrayList<BatterySipper> results = new ArrayList<>();
final int numSippers = sippers.size();
for (int i = 0; i < numSippers; i++) {
BatterySipper sipper = sippers.get(i);
if (sipper.getUid() > 0) {
int realUid = sipper.getUid();
// Check if this UID is a shared GID. If so, we combine it with the OWNER's
// actual app UID.
if (isSharedGid(sipper.getUid())) {
realUid = UserHandle.getUid(UserHandle.USER_SYSTEM,
UserHandle.getAppIdFromSharedAppGid(sipper.getUid()));
}
// Check if this UID is a system UID (mediaserver, logd, nfc, drm, etc).
if (isSystemUid(realUid)
&& !"mediaserver".equals(sipper.packageWithHighestDrain)) {
// Use the system UID for all UIDs running in their own sandbox that
// are not apps. We exclude mediaserver because we already are expected to
// report that as a separate item.
realUid = Process.SYSTEM_UID;
}
if (realUid != sipper.getUid()) {
// Replace the BatterySipper with a new one with the real UID set.
BatterySipper newSipper = new BatterySipper(sipper.drainType,
new FakeUid(realUid), 0.0);
newSipper.add(sipper);
newSipper.packageWithHighestDrain = sipper.packageWithHighestDrain;
newSipper.mPackages = sipper.mPackages;
sipper = newSipper;
}
int index = uidList.indexOfKey(realUid);
if (index < 0) {
// New entry.
uidList.put(realUid, sipper);
} else {
// Combine BatterySippers if we already have one with this UID.
final BatterySipper existingSipper = uidList.valueAt(index);
existingSipper.add(sipper);
if (existingSipper.packageWithHighestDrain == null
&& sipper.packageWithHighestDrain != null) {
existingSipper.packageWithHighestDrain = sipper.packageWithHighestDrain;
}
final int existingPackageLen = existingSipper.mPackages != null ?
existingSipper.mPackages.length : 0;
final int newPackageLen = sipper.mPackages != null ?
sipper.mPackages.length : 0;
if (newPackageLen > 0) {
String[] newPackages = new String[existingPackageLen + newPackageLen];
if (existingPackageLen > 0) {
System.arraycopy(existingSipper.mPackages, 0, newPackages, 0,
existingPackageLen);
}
System.arraycopy(sipper.mPackages, 0, newPackages, existingPackageLen,
newPackageLen);
existingSipper.mPackages = newPackages;
}
}
} else {
results.add(sipper);
}
}
final int numUidSippers = uidList.size();
for (int i = 0; i < numUidSippers; i++) {
results.add(uidList.valueAt(i));
}
// The sort order must have changed, so re-sort based on total power use.
mBatteryUtils.sortUsageList(results);
return results;
}
protected void refreshUi() {
final Context context = getContext();
if (context == null) {
return;
}
restartAnomalyDetectionIfPossible();
// reload BatteryInfo and updateUI
restartBatteryInfoLoader();
final long lastFullChargeTime = mBatteryUtils.calculateLastFullChargeTime(mStatsHelper,
System.currentTimeMillis());
updateScreenPreference();
updateLastFullChargePreference(lastFullChargeTime);
final CharSequence timeSequence = Utils.formatRelativeTime(context, lastFullChargeTime,
false);
final int resId = mShowAllApps ? R.string.power_usage_list_summary_device
: R.string.power_usage_list_summary;
mAppListGroup.setTitle(TextUtils.expandTemplate(getText(resId), timeSequence));
refreshAppListGroup();
}
private void refreshAppListGroup() {
final PowerProfile powerProfile = mStatsHelper.getPowerProfile();
final BatteryStats stats = mStatsHelper.getStats();
final double averagePower = powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
boolean addedSome = false;
final int dischargeAmount = USE_FAKE_DATA ? 5000
: stats != null ? stats.getDischargeAmount(mStatsType) : 0;
cacheRemoveAllPrefs(mAppListGroup);
mAppListGroup.setOrderingAsAdded(false);
if (averagePower >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP || USE_FAKE_DATA) {
final List<BatterySipper> usageList = getCoalescedUsageList(
USE_FAKE_DATA ? getFakeStats() : mStatsHelper.getUsageList());
double hiddenPowerMah = mShowAllApps ? 0 :
mBatteryUtils.removeHiddenBatterySippers(usageList);
mBatteryUtils.sortUsageList(usageList);
final int numSippers = usageList.size();
for (int i = 0; i < numSippers; i++) {
final BatterySipper sipper = usageList.get(i);
double totalPower = USE_FAKE_DATA ? 4000 : mStatsHelper.getTotalPower();
final double percentOfTotal = mBatteryUtils.calculateBatteryPercent(
sipper.totalPowerMah, totalPower, hiddenPowerMah, dischargeAmount);
if (((int) (percentOfTotal + .5)) < 1) {
continue;
}
if (shouldHideSipper(sipper)) {
continue;
}
final UserHandle userHandle = new UserHandle(UserHandle.getUserId(sipper.getUid()));
final BatteryEntry entry = new BatteryEntry(getActivity(), mHandler, mUm, sipper);
final Drawable badgedIcon = mUm.getBadgedIconForUser(entry.getIcon(),
userHandle);
final CharSequence contentDescription = mUm.getBadgedLabelForUser(entry.getLabel(),
userHandle);
final String key = extractKeyFromSipper(sipper);
PowerGaugePreference pref = (PowerGaugePreference) getCachedPreference(key);
if (pref == null) {
pref = new PowerGaugePreference(getPrefContext(), badgedIcon,
contentDescription, entry);
pref.setKey(key);
}
sipper.percent = percentOfTotal;
pref.setTitle(entry.getLabel());
pref.setOrder(i + 1);
pref.setPercent(percentOfTotal);
pref.shouldShowAnomalyIcon(false);
if (sipper.usageTimeMs == 0 && sipper.drainType == DrainType.APP) {
sipper.usageTimeMs = mBatteryUtils.getProcessTimeMs(
BatteryUtils.StatusType.FOREGROUND, sipper.uidObj, mStatsType);
}
setUsageSummary(pref, sipper);
addedSome = true;
mAppListGroup.addPreference(pref);
if (mAppListGroup.getPreferenceCount() - getCachedCount()
> (MAX_ITEMS_TO_LIST + 1)) {
break;
}
}
}
if (!addedSome) {
addNotAvailableMessage();
}
removeCachedPrefs(mAppListGroup);
BatteryEntry.startRequestQueue();
}
@VisibleForTesting
boolean shouldHideSipper(BatterySipper sipper) {
// Don't show over-counted and unaccounted in any condition
return sipper.drainType == DrainType.OVERCOUNTED
|| sipper.drainType == DrainType.UNACCOUNTED;
}
@VisibleForTesting
void refreshAnomalyIcon() {
for (int i = 0, size = mAnomalySparseArray.size(); i < size; i++) {
final String key = extractKeyFromUid(mAnomalySparseArray.keyAt(i));
final PowerGaugePreference pref = (PowerGaugePreference) mAppListGroup.findPreference(
key);
if (pref != null) {
pref.shouldShowAnomalyIcon(true);
}
}
}
@VisibleForTesting
void restartAnomalyDetectionIfPossible() {
if (getAnomalyDetectionPolicy().isAnomalyDetectionEnabled()) {
getLoaderManager().restartLoader(ANOMALY_LOADER, Bundle.EMPTY, mAnomalyLoaderCallbacks);
}
}
@VisibleForTesting
AnomalyDetectionPolicy getAnomalyDetectionPolicy() {
return new AnomalyDetectionPolicy(getContext());
}
@VisibleForTesting
BatterySipper findBatterySipperByType(List<BatterySipper> usageList, DrainType type) {
for (int i = 0, size = usageList.size(); i < size; i++) {
final BatterySipper sipper = usageList.get(i);
if (sipper.drainType == type) {
return sipper;
}
}
return null;
}
@VisibleForTesting
void updateScreenPreference() {
final BatterySipper sipper = findBatterySipperByType(
mStatsHelper.getUsageList(), DrainType.SCREEN);
final long usageTimeMs = sipper != null ? sipper.usageTimeMs : 0;
mScreenUsagePref.setSubtitle(Utils.formatElapsedTime(getContext(), usageTimeMs, false));
}
@VisibleForTesting
void updateLastFullChargePreference(long timeMs) {
final CharSequence timeSequence = Utils.formatRelativeTime(getContext(), timeMs, false);
mLastFullChargePref.setSubtitle(timeSequence);
}
@VisibleForTesting
void showBothEstimates() {
final Context context = getContext();
if (context == null
|| !mPowerFeatureProvider.isEnhancedBatteryPredictionEnabled(context)) {
return;
}
getLoaderManager().restartLoader(DEBUG_INFO_LOADER, Bundle.EMPTY,
mBatteryInfoDebugLoaderCallbacks);
}
@VisibleForTesting
double calculatePercentage(double powerUsage, double dischargeAmount) {
final double totalPower = mStatsHelper.getTotalPower();
return totalPower == 0 ? 0 :
((powerUsage / totalPower) * dischargeAmount);
}
@VisibleForTesting
void setUsageSummary(Preference preference, BatterySipper sipper) {
// Only show summary when usage time is longer than one minute
final long usageTimeMs = sipper.usageTimeMs;
if (usageTimeMs >= DateUtils.MINUTE_IN_MILLIS) {
final CharSequence timeSequence = Utils.formatElapsedTime(getContext(), usageTimeMs,
false);
preference.setSummary(
(sipper.drainType != DrainType.APP || mBatteryUtils.shouldHideSipper(sipper))
? timeSequence
: TextUtils.expandTemplate(getText(R.string.battery_used_for),
timeSequence));
}
}
@VisibleForTesting
String extractKeyFromSipper(BatterySipper sipper) {
if (sipper.uidObj != null) {
return extractKeyFromUid(sipper.getUid());
} else if (sipper.drainType == DrainType.USER) {
return sipper.drainType.toString() + sipper.userId;
} else if (sipper.drainType != DrainType.APP) {
return sipper.drainType.toString();
} else if (sipper.getPackages() != null) {
return TextUtils.concat(sipper.getPackages()).toString();
} else {
Log.w(TAG, "Inappropriate BatterySipper without uid and package names: " + sipper);
return "-1";
}
}
@VisibleForTesting
String extractKeyFromUid(int uid) {
return Integer.toString(uid);
}
@VisibleForTesting
void setBatteryLayoutPreference(LayoutPreference layoutPreference) {
mBatteryLayoutPref = layoutPreference;
}
@VisibleForTesting
void initFeatureProvider() {
final Context context = getContext();
mPowerFeatureProvider = FeatureFactory.getFactory(context)
.getPowerUsageFeatureProvider(context);
}
@VisibleForTesting
void updateAnomalySparseArray(List<Anomaly> anomalies) {
mAnomalySparseArray.clear();
for (int i = 0, size = anomalies.size(); i < size; i++) {
final Anomaly anomaly = anomalies.get(i);
if (mAnomalySparseArray.get(anomaly.uid) == null) {
mAnomalySparseArray.append(anomaly.uid, new ArrayList<>());
}
mAnomalySparseArray.get(anomaly.uid).add(anomaly);
}
}
@VisibleForTesting
void restartBatteryInfoLoader() {
getLoaderManager().restartLoader(BATTERY_INFO_LOADER, Bundle.EMPTY,
mBatteryInfoLoaderCallbacks);
if (mPowerFeatureProvider.isEstimateDebugEnabled()) {
// Unfortunately setting a long click listener on a view means it will no
// longer pass the regular click event to the parent, so we have to register
// a regular click listener as well.
View header = mBatteryLayoutPref.findViewById(R.id.summary1);
header.setOnLongClickListener(this);
header.setOnClickListener(this);
}
}
private static List<BatterySipper> getFakeStats() {
ArrayList<BatterySipper> stats = new ArrayList<>();
float use = 5;
for (DrainType type : DrainType.values()) {
if (type == DrainType.APP) {
continue;
}
stats.add(new BatterySipper(type, null, use));
use += 5;
}
for (int i = 0; i < 100; i++) {
stats.add(new BatterySipper(DrainType.APP,
new FakeUid(Process.FIRST_APPLICATION_UID + i), use));
}
stats.add(new BatterySipper(DrainType.APP,
new FakeUid(0), use));
// Simulate dex2oat process.
BatterySipper sipper = new BatterySipper(DrainType.APP,
new FakeUid(UserHandle.getSharedAppGid(Process.FIRST_APPLICATION_UID)), 10.0f);
sipper.packageWithHighestDrain = "dex2oat";
stats.add(sipper);
sipper = new BatterySipper(DrainType.APP,
new FakeUid(UserHandle.getSharedAppGid(Process.FIRST_APPLICATION_UID + 1)), 10.0f);
sipper.packageWithHighestDrain = "dex2oat";
stats.add(sipper);
sipper = new BatterySipper(DrainType.APP,
new FakeUid(UserHandle.getSharedAppGid(Process.LOG_UID)), 9.0f);
stats.add(sipper);
return stats;
}
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case BatteryEntry.MSG_UPDATE_NAME_ICON:
BatteryEntry entry = (BatteryEntry) msg.obj;
PowerGaugePreference pgp =
(PowerGaugePreference) findPreference(
Integer.toString(entry.sipper.uidObj.getUid()));
if (pgp != null) {
final int userId = UserHandle.getUserId(entry.sipper.getUid());
final UserHandle userHandle = new UserHandle(userId);
pgp.setIcon(mUm.getBadgedIconForUser(entry.getIcon(), userHandle));
pgp.setTitle(entry.name);
if (entry.sipper.drainType == DrainType.APP) {
pgp.setContentDescription(entry.name);
}
}
break;
case BatteryEntry.MSG_REPORT_FULLY_DRAWN:
Activity activity = getActivity();
if (activity != null) {
activity.reportFullyDrawn();
}
break;
}
super.handleMessage(msg);
}
};
@Override
public void onAnomalyHandled(Anomaly anomaly) {
mAnomalySummaryPreferenceController.hideHighUsagePreference();
}
@Override
public boolean onLongClick(View view) {
showBothEstimates();
view.setOnLongClickListener(null);
return true;
}
@Override
public void onClick(View view) {
performBatteryHeaderClick();
}
@Override
protected void restartBatteryStatsLoader() {
restartBatteryStatsLoader(true /* clearHeader */);
}
void restartBatteryStatsLoader(boolean clearHeader) {
super.restartBatteryStatsLoader();
if (clearHeader) {
mBatteryHeaderPreferenceController.quickUpdateHeaderPreference();
}
}
private static class SummaryProvider implements SummaryLoader.SummaryProvider {
private final Context mContext;
private final SummaryLoader mLoader;
private final BatteryBroadcastReceiver mBatteryBroadcastReceiver;
private SummaryProvider(Context context, SummaryLoader loader) {
mContext = context;
mLoader = loader;
mBatteryBroadcastReceiver = new BatteryBroadcastReceiver(mContext);
mBatteryBroadcastReceiver.setBatteryChangedListener(() -> {
BatteryInfo.getBatteryInfo(mContext, new BatteryInfo.Callback() {
@Override
public void onBatteryInfoLoaded(BatteryInfo info) {
mLoader.setSummary(PowerUsageSummaryLegacy.SummaryProvider.this, info.chargeLabel);
}
}, true /* shortString */);
});
}
@Override
public void setListening(boolean listening) {
if (listening) {
mBatteryBroadcastReceiver.register();
} else {
mBatteryBroadcastReceiver.unRegister();
}
}
}
public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
= new SummaryLoader.SummaryProviderFactory() {
@Override
public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
SummaryLoader summaryLoader) {
return new SummaryProvider(activity, summaryLoader);
}
};
}

View File

@@ -4,6 +4,7 @@ com.android.settings.bluetooth.BluetoothDeviceDetailsFragment
com.android.settings.bluetooth.BluetoothPairingDetail com.android.settings.bluetooth.BluetoothPairingDetail
com.android.settings.accounts.AccountDetailDashboardFragment com.android.settings.accounts.AccountDetailDashboardFragment
com.android.settings.fuelgauge.PowerUsageAnomalyDetails com.android.settings.fuelgauge.PowerUsageAnomalyDetails
com.android.settings.fuelgauge.PowerUsageSummaryLegacy
com.android.settings.fuelgauge.AdvancedPowerUsageDetail com.android.settings.fuelgauge.AdvancedPowerUsageDetail
com.android.settings.development.featureflags.FeatureFlagsDashboard com.android.settings.development.featureflags.FeatureFlagsDashboard
com.android.settings.development.qstile.DevelopmentTileConfigFragment com.android.settings.development.qstile.DevelopmentTileConfigFragment

View File

@@ -91,7 +91,7 @@ import java.util.List;
SettingsShadowResources.class, SettingsShadowResources.class,
SettingsShadowResources.SettingsShadowTheme.class, SettingsShadowResources.SettingsShadowTheme.class,
}) })
public class PowerUsageSummaryTest { public class PowerUsageSummaryLegacyTest {
private static final String[] PACKAGE_NAMES = {"com.app1", "com.app2"}; private static final String[] PACKAGE_NAMES = {"com.app1", "com.app2"};
private static final String STUB_STRING = "stub_string"; private static final String STUB_STRING = "stub_string";
private static final int UID = 123; private static final int UID = 123;
@@ -542,7 +542,7 @@ public class PowerUsageSummaryTest {
verify(mBatteryHeaderPreferenceController, never()).quickUpdateHeaderPreference(); verify(mBatteryHeaderPreferenceController, never()).quickUpdateHeaderPreference();
} }
public static class TestFragment extends PowerUsageSummary { public static class TestFragment extends PowerUsageSummaryLegacy {
private Context mContext; private Context mContext;
private boolean mStartActivityCalled; private boolean mStartActivityCalled;