Add infra for battery tip

This cl adds the basic structure for battery tip:
1. BaseBatteryTip: Model class to represent the tip and build
preference and dialog
2. SummaryTip: Display a general battery summary(i.e. your battery
is good..)
3. BatteryTipLoader: AsyncLoader to load the battery tips.
4. BatteryTipPreferenceController: preference controller for
preference group to display battery tips

This cl also:
1. Remove the anomaly code in PowerUsageSummary and we will add it
to app restriction in future.
2. Add preference_category_no_title.xml to avoid the extra 32dp
blank at the top.

Bug: 70570352
Test: RunSettingsRoboTests
Change-Id: If91a553888e2eb91d55fb1d0d7bbea69652f144c
This commit is contained in:
jackqdyulei
2017-12-12 12:52:35 -08:00
parent 444989ad35
commit fde63fc351
13 changed files with 700 additions and 65 deletions

View File

@@ -0,0 +1,25 @@
<!--
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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#0F9D58"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM10
,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/>
</vector>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- Based off frameworks/base/core/res/res/layout/preference_category_material.xml however
set height to 0. This will be used in preference category without title in order to remove
extra 32dp blank -->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+android:id/title"
android:layout_width="match_parent"
android:layout_height="0dp"
android:textAppearance="@android:style/TextAppearance.Material.Body2"
android:textColor="?android:attr/colorAccent"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" />

View File

@@ -4738,6 +4738,11 @@
<!-- Summary for anomalies, meaning some apps behaving abnormally [CHAR LIMIT=80] --> <!-- Summary for anomalies, meaning some apps behaving abnormally [CHAR LIMIT=80] -->
<string name="battery_abnormal_apps_summary"><xliff:g id="number">%1$d</xliff:g> apps misbehaving</string> <string name="battery_abnormal_apps_summary"><xliff:g id="number">%1$d</xliff:g> apps misbehaving</string>
<!-- Title for the battery summary tip [CHAR LIMIT=NONE] -->
<string name="battery_tip_summary_title">Battery is in good shape</string>
<!-- Summary for the battery summary tip [CHAR LIMIT=NONE] -->
<string name="battery_tip_summary_summary">Apps are behaving normally</string>
<!-- Title for force stop dialog [CHAR LIMIT=30] --> <!-- Title for force stop dialog [CHAR LIMIT=30] -->
<string name="dialog_stop_title">Stop app?</string> <string name="dialog_stop_title">Stop app?</string>
<!-- Message body for force stop dialog [CHAR LIMIT=NONE] --> <!-- Message body for force stop dialog [CHAR LIMIT=NONE] -->

View File

@@ -26,10 +26,9 @@
android:selectable="true" android:selectable="true"
android:layout="@layout/battery_header"/> android:layout="@layout/battery_header"/>
<Preference <PreferenceCategory
android:key="high_usage" android:key="battery_tip"
android:icon="@drawable/ic_battery_alert_24dp" android:layout="@layout/preference_category_no_title"/>
android:title="@string/power_high_usage_title"/>
<PreferenceCategory <PreferenceCategory
android:key="device_usage_list"> android:key="device_usage_list">

View File

@@ -55,10 +55,9 @@ import com.android.settings.display.BatteryPercentagePreferenceController;
import com.android.settings.display.TimeoutPreferenceController; import com.android.settings.display.TimeoutPreferenceController;
import com.android.settings.fuelgauge.anomaly.Anomaly; import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy; import com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy;
import com.android.settings.fuelgauge.anomaly.AnomalyDialogFragment.AnomalyDialogListener; import com.android.settings.fuelgauge.batterytip.BatteryTipLoader;
import com.android.settings.fuelgauge.anomaly.AnomalyLoader; import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController;
import com.android.settings.fuelgauge.anomaly.AnomalySummaryPreferenceController; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.anomaly.AnomalyUtils;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.AbstractPreferenceController;
@@ -72,14 +71,15 @@ import java.util.List;
* Displays a list of apps and subsystems that consume power, ordered by how much power was * Displays a list of apps and subsystems that consume power, ordered by how much power was
* consumed since the last time it was unplugged. * consumed since the last time it was unplugged.
*/ */
public class PowerUsageSummary extends PowerUsageBase implements public class PowerUsageSummary extends PowerUsageBase implements OnLongClickListener,
AnomalyDialogListener, OnLongClickListener, OnClickListener { OnClickListener, BatteryTipPreferenceController.BatteryTipListener {
static final String TAG = "PowerUsageSummary"; static final String TAG = "PowerUsageSummary";
private static final boolean DEBUG = false; private static final boolean DEBUG = false;
private static final String KEY_APP_LIST = "app_list"; private static final String KEY_APP_LIST = "app_list";
private static final String KEY_BATTERY_HEADER = "battery_header"; private static final String KEY_BATTERY_HEADER = "battery_header";
private static final String KEY_BATTERY_TIP = "battery_tip";
private static final String KEY_SHOW_ALL_APPS = "show_all_apps"; private static final String KEY_SHOW_ALL_APPS = "show_all_apps";
private static final String KEY_SCREEN_USAGE = "screen_usage"; private static final String KEY_SCREEN_USAGE = "screen_usage";
@@ -89,12 +89,11 @@ public class PowerUsageSummary extends PowerUsageBase implements
private static final String KEY_SCREEN_TIMEOUT = "screen_timeout_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_AMBIENT_DISPLAY = "ambient_display_battery";
private static final String KEY_BATTERY_SAVER_SUMMARY = "battery_saver_summary"; private static final String KEY_BATTERY_SAVER_SUMMARY = "battery_saver_summary";
private static final String KEY_HIGH_USAGE = "high_usage";
@VisibleForTesting @VisibleForTesting
static final int ANOMALY_LOADER = 1; static final int BATTERY_INFO_LOADER = 1;
@VisibleForTesting @VisibleForTesting
static final int BATTERY_INFO_LOADER = 2; static final int BATTERY_TIP_LOADER = 2;
private static final int MENU_STATS_TYPE = Menu.FIRST; private static final int MENU_STATS_TYPE = Menu.FIRST;
@VisibleForTesting @VisibleForTesting
static final int MENU_HIGH_POWER_APPS = Menu.FIRST + 3; static final int MENU_HIGH_POWER_APPS = Menu.FIRST + 3;
@@ -126,36 +125,9 @@ public class PowerUsageSummary extends PowerUsageBase implements
@VisibleForTesting @VisibleForTesting
BatteryHeaderPreferenceController mBatteryHeaderPreferenceController; BatteryHeaderPreferenceController mBatteryHeaderPreferenceController;
private BatteryAppListPreferenceController mBatteryAppListPreferenceController; private BatteryAppListPreferenceController mBatteryAppListPreferenceController;
private AnomalySummaryPreferenceController mAnomalySummaryPreferenceController; private BatteryTipPreferenceController mBatteryTipPreferenceController;
private int mStatsType = BatteryStats.STATS_SINCE_CHARGED; private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;
private LoaderManager.LoaderCallbacks<List<Anomaly>> mAnomalyLoaderCallbacks =
new LoaderManager.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);
mBatteryAppListPreferenceController.refreshAnomalyIcon(mAnomalySparseArray);
}
@Override
public void onLoaderReset(Loader<List<Anomaly>> loader) {
}
};
@VisibleForTesting @VisibleForTesting
LoaderManager.LoaderCallbacks<BatteryInfo> mBatteryInfoLoaderCallbacks = LoaderManager.LoaderCallbacks<BatteryInfo> mBatteryInfoLoaderCallbacks =
new LoaderManager.LoaderCallbacks<BatteryInfo>() { new LoaderManager.LoaderCallbacks<BatteryInfo>() {
@@ -217,6 +189,26 @@ public class PowerUsageSummary extends PowerUsageBase implements
} }
}; };
private LoaderManager.LoaderCallbacks<List<BatteryTip>> mBatteryTipsCallbacks =
new LoaderManager.LoaderCallbacks<List<BatteryTip>>() {
@Override
public Loader<List<BatteryTip>> onCreateLoader(int id, Bundle args) {
return new BatteryTipLoader(getContext(), mStatsHelper);
}
@Override
public void onLoadFinished(Loader<List<BatteryTip>> loader,
List<BatteryTip> data) {
mBatteryTipPreferenceController.updateBatteryTips(data);
}
@Override
public void onLoaderReset(Loader<List<BatteryTip>> loader) {
}
};
@Override @Override
public void onCreate(Bundle icicle) { public void onCreate(Bundle icicle) {
super.onCreate(icicle); super.onCreate(icicle);
@@ -229,8 +221,6 @@ public class PowerUsageSummary extends PowerUsageBase implements
mLastFullChargePref = (PowerGaugePreference) findPreference( mLastFullChargePref = (PowerGaugePreference) findPreference(
KEY_TIME_SINCE_LAST_FULL_CHARGE); KEY_TIME_SINCE_LAST_FULL_CHARGE);
mFooterPreferenceMixin.createFooterPreference().setTitle(R.string.battery_footer_summary); mFooterPreferenceMixin.createFooterPreference().setTitle(R.string.battery_footer_summary);
mAnomalySummaryPreferenceController = new AnomalySummaryPreferenceController(
(SettingsActivity) getActivity(), this, MetricsEvent.FUELGAUGE_POWER_USAGE_SUMMARY);
mBatteryUtils = BatteryUtils.getInstance(getContext()); mBatteryUtils = BatteryUtils.getInstance(getContext());
mAnomalySparseArray = new SparseArray<>(); mAnomalySparseArray = new SparseArray<>();
@@ -251,9 +241,6 @@ public class PowerUsageSummary extends PowerUsageBase implements
@Override @Override
public boolean onPreferenceTreeClick(Preference preference) { public boolean onPreferenceTreeClick(Preference preference) {
if (mAnomalySummaryPreferenceController.onPreferenceTreeClick(preference)) {
return true;
}
if (KEY_BATTERY_HEADER.equals(preference.getKey())) { if (KEY_BATTERY_HEADER.equals(preference.getKey())) {
performBatteryHeaderClick(); performBatteryHeaderClick();
return true; return true;
@@ -282,6 +269,9 @@ public class PowerUsageSummary extends PowerUsageBase implements
mBatteryAppListPreferenceController = new BatteryAppListPreferenceController(context, mBatteryAppListPreferenceController = new BatteryAppListPreferenceController(context,
KEY_APP_LIST, lifecycle, activity, this); KEY_APP_LIST, lifecycle, activity, this);
controllers.add(mBatteryAppListPreferenceController); controllers.add(mBatteryAppListPreferenceController);
mBatteryTipPreferenceController = new BatteryTipPreferenceController(context,
KEY_BATTERY_TIP, this);
controllers.add(mBatteryTipPreferenceController);
controllers.add(new AutoBrightnessPreferenceController(context, KEY_AUTO_BRIGHTNESS)); controllers.add(new AutoBrightnessPreferenceController(context, KEY_AUTO_BRIGHTNESS));
controllers.add(new TimeoutPreferenceController(context, KEY_SCREEN_TIMEOUT)); controllers.add(new TimeoutPreferenceController(context, KEY_SCREEN_TIMEOUT));
controllers.add(new BatterySaverController(context, getLifecycle())); controllers.add(new BatterySaverController(context, getLifecycle()));
@@ -382,7 +372,7 @@ public class PowerUsageSummary extends PowerUsageBase implements
return; return;
} }
restartAnomalyDetectionIfPossible(); restartBatteryTipLoader();
// reload BatteryInfo and updateUI // reload BatteryInfo and updateUI
restartBatteryInfoLoader(); restartBatteryInfoLoader();
@@ -398,10 +388,8 @@ public class PowerUsageSummary extends PowerUsageBase implements
} }
@VisibleForTesting @VisibleForTesting
void restartAnomalyDetectionIfPossible() { void restartBatteryTipLoader() {
if (getAnomalyDetectionPolicy().isAnomalyDetectionEnabled()) { getLoaderManager().restartLoader(BATTERY_TIP_LOADER, Bundle.EMPTY, mBatteryTipsCallbacks);
getLoaderManager().restartLoader(ANOMALY_LOADER, Bundle.EMPTY, mAnomalyLoaderCallbacks);
}
} }
@VisibleForTesting @VisibleForTesting
@@ -484,11 +472,6 @@ public class PowerUsageSummary extends PowerUsageBase implements
} }
} }
@Override
public void onAnomalyHandled(Anomaly anomaly) {
mAnomalySummaryPreferenceController.hideHighUsagePreference();
}
@Override @Override
public boolean onLongClick(View view) { public boolean onLongClick(View view) {
showBothEstimates(); showBothEstimates();
@@ -513,6 +496,11 @@ public class PowerUsageSummary extends PowerUsageBase implements
} }
} }
@Override
public void onBatteryTipHandled(BatteryTip batteryTip) {
restartBatteryTipLoader();
}
private static class SummaryProvider implements SummaryLoader.SummaryProvider { private static class SummaryProvider implements SummaryLoader.SummaryProvider {
private final Context mContext; private final Context mContext;
private final SummaryLoader mLoader; private final SummaryLoader mLoader;
@@ -555,7 +543,6 @@ public class PowerUsageSummary extends PowerUsageBase implements
@Override @Override
public List<String> getNonIndexableKeys(Context context) { public List<String> getNonIndexableKeys(Context context) {
List<String> niks = super.getNonIndexableKeys(context); List<String> niks = super.getNonIndexableKeys(context);
niks.add(KEY_HIGH_USAGE);
niks.add(KEY_BATTERY_SAVER_SUMMARY); niks.add(KEY_BATTERY_SAVER_SUMMARY);
// Duplicates in display // Duplicates in display
niks.add(KEY_AUTO_BRIGHTNESS); niks.add(KEY_AUTO_BRIGHTNESS);

View File

@@ -0,0 +1,58 @@
/*
* 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.batterytip;
import android.content.Context;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.SummaryTip;
import com.android.settingslib.utils.AsyncLoader;
import java.util.ArrayList;
import java.util.List;
/**
* Loader to compute and return a battery tip list. It will always return a full length list even
* though some tips may have state {@code BaseBatteryTip.StateType.INVISIBLE}.
*/
public class BatteryTipLoader extends AsyncLoader<List<BatteryTip>> {
private static final String TAG = "BatteryTipLoader";
private static final boolean USE_FAKE_DATA = false;
private BatteryStatsHelper mBatteryStatsHelper;
public BatteryTipLoader(Context context, BatteryStatsHelper batteryStatsHelper) {
super(context);
mBatteryStatsHelper = batteryStatsHelper;
}
@Override
public List<BatteryTip> loadInBackground() {
List<BatteryTip> tips = new ArrayList<>();
//TODO(b/70570352): add battery tip detectors
tips.add(new SummaryTip(BatteryTip.StateType.NEW));
return tips;
}
@Override
protected void onDiscardResult(List<BatteryTip> result) {
}
}

View File

@@ -0,0 +1,125 @@
/*
* 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.batterytip;
import android.content.Context;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceGroup;
import android.support.v7.preference.PreferenceScreen;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.SummaryTip;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Controller in charge of the battery tip group
*/
public class BatteryTipPreferenceController extends BasePreferenceController {
private BatteryTipListener mBatteryTipListener;
private List<BatteryTip> mBatteryTips;
private Map<String, BatteryTip> mBatteryTipMap;
@VisibleForTesting
PreferenceGroup mPreferenceGroup;
@VisibleForTesting
Context mPrefContext;
public BatteryTipPreferenceController(Context context, String preferenceKey) {
this(context, preferenceKey, null);
}
public BatteryTipPreferenceController(Context context, String preferenceKey,
BatteryTipListener batteryTipListener) {
super(context, preferenceKey);
mBatteryTipListener = batteryTipListener;
mBatteryTipMap = new HashMap<>();
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPrefContext = screen.getContext();
mPreferenceGroup = (PreferenceGroup) screen.findPreference(getPreferenceKey());
// Add summary tip in advance to avoid UI flakiness
final SummaryTip summaryTip = new SummaryTip(BatteryTip.StateType.NEW);
mPreferenceGroup.addPreference(summaryTip.buildPreference(mPrefContext));
}
public void updateBatteryTips(List<BatteryTip> batteryTips) {
if (mBatteryTips == null) {
mBatteryTips = batteryTips;
} else {
// mBatteryTips and batteryTips always have the same length and same sequence.
for (int i = 0, size = batteryTips.size(); i < size; i++) {
mBatteryTips.get(i).updateState(batteryTips.get(i));
}
}
//TODO(b/70570352): try to reuse the existing preference rather than remove and add.
mPreferenceGroup.removeAll();
for (int i = 0, size = batteryTips.size(); i < size; i++) {
final BatteryTip batteryTip = mBatteryTips.get(i);
if (batteryTip.getState() != BatteryTip.StateType.INVISIBLE) {
final Preference preference = batteryTip.buildPreference(mPrefContext);
mBatteryTipMap.put(preference.getKey(), batteryTip);
mPreferenceGroup.addPreference(preference);
}
}
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
final BatteryTip batteryTip = mBatteryTipMap.get(preference.getKey());
if (batteryTip != null) {
if (batteryTip.shouldShowDialog()) {
// build and show the dialog
} else {
batteryTip.action();
if (mBatteryTipListener != null) {
mBatteryTipListener.onBatteryTipHandled(batteryTip);
}
}
return true;
}
return super.handlePreferenceTreeClick(preference);
}
/**
* Listener to give the control back to target fragment
*/
public interface BatteryTipListener {
/**
* This method is invoked once battery tip is handled, then target fragment could do
* extra work.
*
* @param batteryTip that has been handled
*/
void onBatteryTipHandled(BatteryTip batteryTip);
}
}

View File

@@ -0,0 +1,117 @@
/*
* 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.batterytip.tips;
import android.app.Dialog;
import android.content.Context;
import android.support.annotation.IdRes;
import android.support.annotation.IntDef;
import android.support.v7.preference.Preference;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Base model for a battery tip(e.g. suggest user to turn on battery saver)
*
* Each {@link BatteryTip} contains basic data(e.g. title, summary, icon) as well as the
* pre-defined action(e.g. turn on battery saver)
*/
public abstract class BatteryTip {
@Retention(RetentionPolicy.SOURCE)
@IntDef({StateType.NEW,
StateType.HANDLED,
StateType.INVISIBLE})
public @interface StateType {
int NEW = 0;
int HANDLED = 1;
int INVISIBLE = 2;
}
@Retention(RetentionPolicy.SOURCE)
@IntDef({TipType.SUMMARY,
TipType.BATTERY_SAVER,
TipType.HIGH_DEVICE_USAGE,
TipType.SMART_BATTERY_MANAGER,
TipType.APP_RESTRICTION,
TipType.REDUCED_BATTERY,
TipType.LOW_BATTERY})
public @interface TipType {
int SUMMARY = 0;
int BATTERY_SAVER = 1;
int HIGH_DEVICE_USAGE = 2;
int SMART_BATTERY_MANAGER = 3;
int APP_RESTRICTION = 4;
int REDUCED_BATTERY = 5;
int LOW_BATTERY = 6;
}
private static final String KEY_PREFIX = "key_battery_tip";
@TipType
protected int mType;
@StateType
protected int mState;
protected boolean mShowDialog;
public abstract CharSequence getTitle(Context context);
public abstract CharSequence getSummary(Context context);
@IdRes
public abstract int getIconId();
/**
* Update the current {@link #mState} using the new {@code tip}.
* @param tip used to update
*/
public abstract void updateState(BatteryTip tip);
/**
* Execute the action for this {@link BatteryTip}
*/
public abstract void action();
/**
* Build the dialog to display either the info about {@link BatteryTip} or confirmation
* about the action.
*/
public abstract Dialog buildDialog();
public Preference buildPreference(Context context) {
Preference preference = new Preference(context);
preference.setKey(getKey());
preference.setTitle(getTitle(context));
preference.setSummary(getSummary(context));
preference.setIcon(getIconId());
return preference;
}
public boolean shouldShowDialog() {
return mShowDialog;
}
public String getKey() {
return KEY_PREFIX + mType;
}
@StateType
public int getState() {
return mState;
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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.batterytip.tips;
import android.app.Dialog;
import android.content.Context;
import com.android.settings.R;
/**
* Tip to show general summary about battery life
*/
public class SummaryTip extends BatteryTip {
public SummaryTip(@StateType int state) {
mShowDialog = false;
mState = state;
}
@Override
public CharSequence getTitle(Context context) {
return context.getString(R.string.battery_tip_summary_title);
}
@Override
public CharSequence getSummary(Context context) {
return context.getString(R.string.battery_tip_summary_summary);
}
@Override
public int getIconId() {
return R.drawable.ic_check_circle_green_24dp;
}
@Override
public void updateState(BatteryTip tip) {
mState = tip.mState;
}
@Override
public void action() {
// do nothing
}
@Override
public Dialog buildDialog() {
//TODO(b/70570352): create the dialog for summary tip and add test
return null;
}
}

View File

@@ -452,8 +452,8 @@ public class PowerUsageSummaryLegacyTest {
mFragment.restartAnomalyDetectionIfPossible(); mFragment.restartAnomalyDetectionIfPossible();
verify(mLoaderManager).restartLoader(eq(PowerUsageSummary.ANOMALY_LOADER), eq(Bundle.EMPTY), verify(mLoaderManager).restartLoader(eq(PowerUsageSummaryLegacy.ANOMALY_LOADER),
any()); eq(Bundle.EMPTY), any());
} }
@Test @Test

View File

@@ -339,15 +339,14 @@ public class PowerUsageSummaryTest {
} }
@Test @Test
public void testInitAnomalyDetectionIfPossible_detectionEnabled_init() { public void testRestartBatteryTipLoader() {
//TODO: add policy logic here when BatteryTipPolicy is implemented
doReturn(mLoaderManager).when(mFragment).getLoaderManager(); doReturn(mLoaderManager).when(mFragment).getLoaderManager();
doReturn(mAnomalyDetectionPolicy).when(mFragment).getAnomalyDetectionPolicy();
when(mAnomalyDetectionPolicy.isAnomalyDetectionEnabled()).thenReturn(true);
mFragment.restartAnomalyDetectionIfPossible(); mFragment.restartBatteryTipLoader();
verify(mLoaderManager).restartLoader(eq(PowerUsageSummary.ANOMALY_LOADER), eq(Bundle.EMPTY), verify(mLoaderManager).restartLoader(eq(PowerUsageSummary.BATTERY_TIP_LOADER),
any()); eq(Bundle.EMPTY), any());
} }
@Test @Test

View File

@@ -0,0 +1,132 @@
/*
* 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.batterytip;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceCategory;
import android.support.v7.preference.PreferenceGroup;
import android.support.v7.preference.PreferenceManager;
import android.support.v7.preference.PreferenceScreen;
import com.android.settings.TestConfig;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.SummaryTip;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
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 org.robolectric.annotation.Config;
import java.util.ArrayList;
import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class BatteryTipPreferenceControllerTest {
private static final String KEY_PREF = "battery_tip";
private static final String KEY_TIP = "key_battery_tip";
@Mock
private BatteryTipPreferenceController.BatteryTipListener mBatteryTipListener;
@Mock
private PreferenceScreen mPreferenceScreen;
@Mock
private BatteryTip mBatteryTip;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private PreferenceManager mPreferenceManager;
private Context mContext;
private PreferenceGroup mPreferenceGroup;
private BatteryTipPreferenceController mBatteryTipPreferenceController;
private List<BatteryTip> mOldBatteryTips;
private List<BatteryTip> mNewBatteryTips;
private Preference mPreference;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mPreferenceGroup = spy(new PreferenceCategory(mContext));
doReturn(mContext).when(mPreferenceScreen).getContext();
doReturn(mPreferenceManager).when(mPreferenceGroup).getPreferenceManager();
doReturn(mPreferenceGroup).when(mPreferenceScreen).findPreference(KEY_PREF);
mPreference = new Preference(mContext);
mPreference.setKey(KEY_TIP);
mOldBatteryTips = new ArrayList<>();
mOldBatteryTips.add(new SummaryTip(BatteryTip.StateType.NEW));
mNewBatteryTips = new ArrayList<>();
mNewBatteryTips.add(new SummaryTip(BatteryTip.StateType.INVISIBLE));
mBatteryTipPreferenceController = new BatteryTipPreferenceController(mContext, KEY_PREF,
mBatteryTipListener);
mBatteryTipPreferenceController.mPreferenceGroup = mPreferenceGroup;
mBatteryTipPreferenceController.mPrefContext = mContext;
}
@Test
public void testDisplayPreference_addSummaryTip() {
mBatteryTipPreferenceController.displayPreference(mPreferenceScreen);
assertOnlyContainsSummaryTip(mPreferenceGroup);
}
@Test
public void updateBatteryTips_updateTwice_firstShowSummaryTipThenRemoveIt() {
// Display summary tip because its state is new
mBatteryTipPreferenceController.updateBatteryTips(mOldBatteryTips);
assertOnlyContainsSummaryTip(mPreferenceGroup);
// Remove summary tip because its new state is invisible
mBatteryTipPreferenceController.updateBatteryTips(mNewBatteryTips);
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(0);
}
@Test
public void testHandlePreferenceTreeClick_noDialog_invokeAction() {
List<BatteryTip> batteryTips = new ArrayList<>();
batteryTips.add(mBatteryTip);
doReturn(mPreference).when(mBatteryTip).buildPreference(any());
doReturn(false).when(mBatteryTip).shouldShowDialog();
doReturn(KEY_TIP).when(mBatteryTip).getKey();
mBatteryTipPreferenceController.updateBatteryTips(batteryTips);
mBatteryTipPreferenceController.handlePreferenceTreeClick(mPreference);
verify(mBatteryTip).action();
}
private void assertOnlyContainsSummaryTip(final PreferenceGroup preferenceGroup) {
assertThat(preferenceGroup.getPreferenceCount()).isEqualTo(1);
final Preference preference = preferenceGroup.getPreference(0);
assertThat(preference.getTitle()).isEqualTo("Battery is in good shape");
assertThat(preference.getSummary()).isEqualTo("Apps are behaving normally");
}
}

View File

@@ -0,0 +1,97 @@
/*
* 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.batterytip.tips;
import static com.google.common.truth.Truth.assertThat;
import android.app.Dialog;
import android.content.Context;
import android.support.annotation.IdRes;
import android.support.v7.preference.Preference;
import com.android.settings.R;
import com.android.settings.TestConfig;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class BatteryTipTest {
private static final String TITLE = "title";
private static final String SUMMARY = "summary";
@IdRes
private static final int ICON_ID = R.drawable.ic_fingerprint;
private Context mContext;
private TestBatteryTip mBatteryTip;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mBatteryTip = new TestBatteryTip();
}
@Test
public void testBuildPreference() {
final Preference preference = mBatteryTip.buildPreference(mContext);
assertThat(preference.getTitle()).isEqualTo(TITLE);
assertThat(preference.getSummary()).isEqualTo(SUMMARY);
assertThat(preference.getIcon()).isEqualTo(mContext.getDrawable(ICON_ID));
}
/**
* Used to test the non abstract methods in {@link TestBatteryTip}
*/
public class TestBatteryTip extends BatteryTip {
@Override
public String getTitle(Context context) {
return TITLE;
}
@Override
public String getSummary(Context context) {
return SUMMARY;
}
@Override
public int getIconId() {
return ICON_ID;
}
@Override
public void updateState(BatteryTip tip) {
// do nothing
}
@Override
public void action() {
// do nothing
}
@Override
public Dialog buildDialog() {
return null;
}
}
}