Merge "Add dialog for summary tip" into pi-dev

This commit is contained in:
TreeHugger Robot
2018-03-14 05:47:12 +00:00
committed by Android (Google) Code Review
10 changed files with 171 additions and 16 deletions

View File

@@ -4892,6 +4892,11 @@
<!-- CANCEL button for dialog to remove restriction for app [CHAR LIMIT=NONE] --> <!-- CANCEL button for dialog to remove restriction for app [CHAR LIMIT=NONE] -->
<string name="battery_tip_unrestrict_app_dialog_cancel">Not now</string> <string name="battery_tip_unrestrict_app_dialog_cancel">Not now</string>
<!-- Message for battery tip dialog to show the battery summary -->
<string name="battery_tip_dialog_summary_message">Based on your usage, your battery usually lasts about <xliff:g id="time_duration">%1$s</xliff:g> when fully charged.\n\nIf you need to extend your battery life, turn on Battery Saver.</string>
<!-- Message for battery tip dialog to show the battery summary -->
<string name="battery_tip_dialog_summary_message_no_estimation">If you need to extend your battery life, turn on Battery Saver</string>
<!-- Title for the smart battery manager preference [CHAR LIMIT=NONE] --> <!-- Title for the smart battery manager preference [CHAR LIMIT=NONE] -->
<string name="smart_battery_manager_title">Smart battery manager</string> <string name="smart_battery_manager_title">Smart battery manager</string>
<!-- Title for the smart battery toggle [CHAR LIMIT=NONE] --> <!-- Title for the smart battery toggle [CHAR LIMIT=NONE] -->

View File

@@ -32,11 +32,13 @@ import com.android.settings.SettingsActivity;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settings.core.InstrumentedPreferenceFragment; import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.fuelgauge.Estimate;
import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController.BatteryTipListener; import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController.BatteryTipListener;
import com.android.settings.fuelgauge.batterytip.actions.BatteryTipAction; import com.android.settings.fuelgauge.batterytip.actions.BatteryTipAction;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip; import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip;
import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip; import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
import com.android.settings.fuelgauge.batterytip.tips.SummaryTip;
import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip; import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip;
import com.android.settingslib.utils.StringUtil; import com.android.settingslib.utils.StringUtil;
@@ -72,9 +74,18 @@ public class BatteryTipDialogFragment extends InstrumentedDialogFragment impleme
switch (mBatteryTip.getType()) { switch (mBatteryTip.getType()) {
case BatteryTip.TipType.SUMMARY: case BatteryTip.TipType.SUMMARY:
case BatteryTip.TipType.LOW_BATTERY: final long averageTimeMs = ((SummaryTip) mBatteryTip).getAverageTimeMs();
//TODO(b/70570352): add dialog final String message = context.getString(
return null; averageTimeMs == Estimate.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN
? R.string.battery_tip_dialog_summary_message_no_estimation
: R.string.battery_tip_dialog_summary_message,
StringUtil.formatElapsedTime(context, averageTimeMs,
false /* withSeconds */));
return new AlertDialog.Builder(context)
.setMessage(message)
.setPositiveButton(android.R.string.ok, null)
.create();
case BatteryTip.TipType.HIGH_DEVICE_USAGE: case BatteryTip.TipType.HIGH_DEVICE_USAGE:
final HighUsageTip highUsageTip = (HighUsageTip) mBatteryTip; final HighUsageTip highUsageTip = (HighUsageTip) mBatteryTip;
final RecyclerView view = (RecyclerView) LayoutInflater.from(context).inflate( final RecyclerView view = (RecyclerView) LayoutInflater.from(context).inflate(

View File

@@ -22,6 +22,7 @@ import android.support.annotation.VisibleForTesting;
import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.fuelgauge.BatteryInfo; import com.android.settings.fuelgauge.BatteryInfo;
import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.Estimate;
import com.android.settings.fuelgauge.batterytip.detectors.EarlyWarningDetector; import com.android.settings.fuelgauge.batterytip.detectors.EarlyWarningDetector;
import com.android.settings.fuelgauge.batterytip.detectors.HighUsageDetector; import com.android.settings.fuelgauge.batterytip.detectors.HighUsageDetector;
import com.android.settings.fuelgauge.batterytip.detectors.LowBatteryDetector; import com.android.settings.fuelgauge.batterytip.detectors.LowBatteryDetector;
@@ -70,7 +71,7 @@ public class BatteryTipLoader extends AsyncLoader<List<BatteryTip>> {
tips.add(new HighUsageDetector(context, policy, mBatteryStatsHelper).detect()); tips.add(new HighUsageDetector(context, policy, mBatteryStatsHelper).detect());
tips.add(new SmartBatteryDetector(policy, context.getContentResolver()).detect()); tips.add(new SmartBatteryDetector(policy, context.getContentResolver()).detect());
tips.add(new EarlyWarningDetector(policy, context).detect()); tips.add(new EarlyWarningDetector(policy, context).detect());
tips.add(new SummaryDetector(policy).detect()); tips.add(new SummaryDetector(policy, batteryInfo.averageTimeToDischarge).detect());
tips.add(new RestrictAppDetector(context, policy).detect()); tips.add(new RestrictAppDetector(context, policy).detect());
Collections.sort(tips); Collections.sort(tips);
@@ -83,7 +84,8 @@ public class BatteryTipLoader extends AsyncLoader<List<BatteryTip>> {
private List<BatteryTip> getFakeData() { private List<BatteryTip> getFakeData() {
final List<BatteryTip> tips = new ArrayList<>(); final List<BatteryTip> tips = new ArrayList<>();
tips.add(new SummaryTip(BatteryTip.StateType.NEW)); tips.add(new SummaryTip(BatteryTip.StateType.NEW,
Estimate.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN));
tips.add(new LowBatteryTip(BatteryTip.StateType.NEW)); tips.add(new LowBatteryTip(BatteryTip.StateType.NEW));
return tips; return tips;

View File

@@ -27,6 +27,7 @@ import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.SettingsActivity; import com.android.settings.SettingsActivity;
import com.android.settings.core.BasePreferenceController; import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.InstrumentedPreferenceFragment; import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.fuelgauge.Estimate;
import com.android.settings.fuelgauge.batterytip.actions.BatteryTipAction; import com.android.settings.fuelgauge.batterytip.actions.BatteryTipAction;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.SummaryTip; import com.android.settings.fuelgauge.batterytip.tips.SummaryTip;
@@ -82,7 +83,8 @@ public class BatteryTipPreferenceController extends BasePreferenceController {
mPreferenceGroup = (PreferenceGroup) screen.findPreference(getPreferenceKey()); mPreferenceGroup = (PreferenceGroup) screen.findPreference(getPreferenceKey());
// Add summary tip in advance to avoid UI flakiness // Add summary tip in advance to avoid UI flakiness
final SummaryTip summaryTip = new SummaryTip(BatteryTip.StateType.NEW); final SummaryTip summaryTip = new SummaryTip(BatteryTip.StateType.NEW,
Estimate.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN);
mPreferenceGroup.addPreference(summaryTip.buildPreference(mPrefContext)); mPreferenceGroup.addPreference(summaryTip.buildPreference(mPrefContext));
} }

View File

@@ -26,9 +26,11 @@ import com.android.settings.fuelgauge.batterytip.tips.SummaryTip;
*/ */
public class SummaryDetector implements BatteryTipDetector { public class SummaryDetector implements BatteryTipDetector {
private BatteryTipPolicy mPolicy; private BatteryTipPolicy mPolicy;
private long mAverageTimeMs;
public SummaryDetector(BatteryTipPolicy policy) { public SummaryDetector(BatteryTipPolicy policy, long averageTimeMs) {
mPolicy = policy; mPolicy = policy;
mAverageTimeMs = averageTimeMs;
} }
@Override @Override
@@ -37,6 +39,6 @@ public class SummaryDetector implements BatteryTipDetector {
final int state = mPolicy.summaryEnabled final int state = mPolicy.summaryEnabled
? BatteryTip.StateType.NEW ? BatteryTip.StateType.NEW
: BatteryTip.StateType.INVISIBLE; : BatteryTip.StateType.INVISIBLE;
return new SummaryTip(state); return new SummaryTip(state, mAverageTimeMs);
} }
} }

View File

@@ -19,6 +19,7 @@ package com.android.settings.fuelgauge.batterytip.tips;
import android.content.Context; import android.content.Context;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.VisibleForTesting;
import com.android.internal.logging.nano.MetricsProto; import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R; import com.android.settings.R;
@@ -28,13 +29,17 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
* Tip to show general summary about battery life * Tip to show general summary about battery life
*/ */
public class SummaryTip extends BatteryTip { public class SummaryTip extends BatteryTip {
private long mAverageTimeMs;
public SummaryTip(@StateType int state) { public SummaryTip(@StateType int state, long averageTimeMs) {
super(TipType.SUMMARY, state, false /* showDialog */); super(TipType.SUMMARY, state, true /* showDialog */);
mAverageTimeMs = averageTimeMs;
} }
private SummaryTip(Parcel in) { @VisibleForTesting
SummaryTip(Parcel in) {
super(in); super(in);
mAverageTimeMs = in.readLong();
} }
@Override @Override
@@ -57,12 +62,22 @@ public class SummaryTip extends BatteryTip {
mState = tip.mState; mState = tip.mState;
} }
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeLong(mAverageTimeMs);
}
@Override @Override
public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) { public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) {
metricsFeatureProvider.action(context, MetricsProto.MetricsEvent.ACTION_SUMMARY_TIP, metricsFeatureProvider.action(context, MetricsProto.MetricsEvent.ACTION_SUMMARY_TIP,
mState); mState);
} }
public long getAverageTimeMs() {
return mAverageTimeMs;
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public BatteryTip createFromParcel(Parcel in) { public BatteryTip createFromParcel(Parcel in) {
return new SummaryTip(in); return new SummaryTip(in);

View File

@@ -18,6 +18,7 @@ package com.android.settings.fuelgauge.batterytip;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.robolectric.Shadows.shadowOf; import static org.robolectric.Shadows.shadowOf;
@@ -26,9 +27,11 @@ import android.content.Context;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.fuelgauge.Estimate;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip; import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip;
import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip; import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
import com.android.settings.fuelgauge.batterytip.tips.SummaryTip;
import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip; import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip;
import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.ShadowUtils; import com.android.settings.testutils.shadow.ShadowUtils;
@@ -54,6 +57,7 @@ public class BatteryTipDialogFragmentTest {
private static final String PACKAGE_NAME = "com.android.app"; private static final String PACKAGE_NAME = "com.android.app";
private static final String DISPLAY_NAME = "app"; private static final String DISPLAY_NAME = "app";
private static final long SCREEN_TIME_MS = DateUtils.HOUR_IN_MILLIS; private static final long SCREEN_TIME_MS = DateUtils.HOUR_IN_MILLIS;
private static final long AVERAGE_TIME_MS = DateUtils.HOUR_IN_MILLIS;
private BatteryTipDialogFragment mDialogFragment; private BatteryTipDialogFragment mDialogFragment;
private Context mContext; private Context mContext;
@@ -61,6 +65,7 @@ public class BatteryTipDialogFragmentTest {
private RestrictAppTip mRestrictedOneAppTip; private RestrictAppTip mRestrictedOneAppTip;
private RestrictAppTip mRestrictAppsTip; private RestrictAppTip mRestrictAppsTip;
private UnrestrictAppTip mUnrestrictAppTip; private UnrestrictAppTip mUnrestrictAppTip;
private SummaryTip mSummaryTip;
@Before @Before
public void setUp() { public void setUp() {
@@ -85,6 +90,8 @@ public class BatteryTipDialogFragmentTest {
new ArrayList<>(restrictApps)); new ArrayList<>(restrictApps));
mUnrestrictAppTip = new UnrestrictAppTip(BatteryTip.StateType.NEW, appInfo); mUnrestrictAppTip = new UnrestrictAppTip(BatteryTip.StateType.NEW, appInfo);
mSummaryTip = spy(new SummaryTip(BatteryTip.StateType.NEW,
Estimate.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN));
} }
@Test @Test
@@ -151,4 +158,32 @@ public class BatteryTipDialogFragmentTest {
assertThat(shadowDialog.getMessage()) assertThat(shadowDialog.getMessage())
.isEqualTo(mContext.getString(R.string.battery_tip_unrestrict_app_dialog_message)); .isEqualTo(mContext.getString(R.string.battery_tip_unrestrict_app_dialog_message));
} }
@Test
public void testOnCreateDialog_summaryTipWithEstimation_fireDialogWithEstimation() {
doReturn(AVERAGE_TIME_MS).when(mSummaryTip).getAverageTimeMs();
mDialogFragment = BatteryTipDialogFragment.newInstance(mSummaryTip);
FragmentTestUtil.startFragment(mDialogFragment);
final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
ShadowAlertDialog shadowDialog = shadowOf(dialog);
assertThat(shadowDialog.getMessage()).isEqualTo(
"Based on your usage, your battery usually lasts about 1h when fully charged"
+ ".\n\nIf you need to extend your battery life, turn on Battery Saver.");
}
@Test
public void testOnCreateDialog_summaryTipWithoutEstimation_fireDialogWithoutEstimation() {
mDialogFragment = BatteryTipDialogFragment.newInstance(mSummaryTip);
FragmentTestUtil.startFragment(mDialogFragment);
final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
ShadowAlertDialog shadowDialog = shadowOf(dialog);
assertThat(shadowDialog.getMessage()).isEqualTo(
"If you need to extend your battery life, turn on Battery Saver");
}
} }

View File

@@ -15,8 +15,11 @@
*/ */
package com.android.settings.fuelgauge.batterytip; package com.android.settings.fuelgauge.batterytip;
import static com.android.settings.fuelgauge.batterytip.tips.BatteryTip.TipType.SMART_BATTERY_MANAGER; import static com.android.settings.fuelgauge.batterytip.tips.BatteryTip.TipType
.SMART_BATTERY_MANAGER;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
@@ -30,6 +33,7 @@ import android.support.v7.preference.PreferenceCategory;
import android.support.v7.preference.PreferenceGroup; import android.support.v7.preference.PreferenceGroup;
import android.support.v7.preference.PreferenceManager; import android.support.v7.preference.PreferenceManager;
import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.PreferenceScreen;
import android.text.format.DateUtils;
import com.android.internal.logging.nano.MetricsProto; import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.SettingsActivity; import com.android.settings.SettingsActivity;
@@ -54,6 +58,8 @@ public class BatteryTipPreferenceControllerTest {
private static final String KEY_PREF = "battery_tip"; private static final String KEY_PREF = "battery_tip";
private static final String KEY_TIP = "key_battery_tip"; private static final String KEY_TIP = "key_battery_tip";
private static final long AVERAGE_TIME_MS = DateUtils.HOUR_IN_MILLIS;
@Mock @Mock
private BatteryTipPreferenceController.BatteryTipListener mBatteryTipListener; private BatteryTipPreferenceController.BatteryTipListener mBatteryTipListener;
@Mock @Mock
@@ -87,9 +93,9 @@ public class BatteryTipPreferenceControllerTest {
mFeatureFactory = FakeFeatureFactory.setupForTest(); mFeatureFactory = FakeFeatureFactory.setupForTest();
mOldBatteryTips = new ArrayList<>(); mOldBatteryTips = new ArrayList<>();
mOldBatteryTips.add(new SummaryTip(BatteryTip.StateType.NEW)); mOldBatteryTips.add(new SummaryTip(BatteryTip.StateType.NEW, AVERAGE_TIME_MS));
mNewBatteryTips = new ArrayList<>(); mNewBatteryTips = new ArrayList<>();
mNewBatteryTips.add(new SummaryTip(BatteryTip.StateType.INVISIBLE)); mNewBatteryTips.add(new SummaryTip(BatteryTip.StateType.INVISIBLE, AVERAGE_TIME_MS));
mBatteryTipPreferenceController = new BatteryTipPreferenceController(mContext, KEY_PREF, mBatteryTipPreferenceController = new BatteryTipPreferenceController(mContext, KEY_PREF,
mSettingsActivity, null, mBatteryTipListener); mSettingsActivity, null, mBatteryTipListener);

View File

@@ -19,6 +19,8 @@ package com.android.settings.fuelgauge.batterytip.detectors;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import android.text.format.DateUtils;
import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy; import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.SettingsRobolectricTestRunner;
@@ -33,6 +35,7 @@ import org.robolectric.util.ReflectionHelpers;
public class SummaryDetectorTest { public class SummaryDetectorTest {
private BatteryTipPolicy mPolicy; private BatteryTipPolicy mPolicy;
private static final long AVERAGE_TIME_MS = DateUtils.HOUR_IN_MILLIS;
@Before @Before
public void setUp() { public void setUp() {
@@ -44,14 +47,14 @@ public class SummaryDetectorTest {
@Test @Test
public void testDetect_disabledByPolicy_tipInvisible() { public void testDetect_disabledByPolicy_tipInvisible() {
ReflectionHelpers.setField(mPolicy, "summaryEnabled", false); ReflectionHelpers.setField(mPolicy, "summaryEnabled", false);
SummaryDetector detector = new SummaryDetector(mPolicy); SummaryDetector detector = new SummaryDetector(mPolicy, AVERAGE_TIME_MS);
assertThat(detector.detect().isVisible()).isFalse(); assertThat(detector.detect().isVisible()).isFalse();
} }
@Test @Test
public void testDetect_notDisabled_tipVisible() { public void testDetect_notDisabled_tipVisible() {
SummaryDetector detector = new SummaryDetector(mPolicy); SummaryDetector detector = new SummaryDetector(mPolicy, AVERAGE_TIME_MS);
assertThat(detector.detect().isVisible()).isTrue(); assertThat(detector.detect().isVisible()).isTrue();
} }

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2018 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 static org.mockito.Mockito.verify;
import android.content.Context;
import android.os.Parcel;
import android.text.format.DateUtils;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
@RunWith(SettingsRobolectricTestRunner.class)
public class SummaryTipTest {
private static final long AVERAGE_TIME_MS = DateUtils.HOUR_IN_MILLIS;
@Mock
private MetricsFeatureProvider mMetricsFeatureProvider;
private Context mContext;
private SummaryTip mSummaryTip;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mSummaryTip =
new SummaryTip(BatteryTip.StateType.NEW, AVERAGE_TIME_MS);
}
@Test
public void testParcelable() {
Parcel parcel = Parcel.obtain();
mSummaryTip.writeToParcel(parcel, mSummaryTip.describeContents());
parcel.setDataPosition(0);
final SummaryTip parcelTip = new SummaryTip(parcel);
assertThat(parcelTip.getAverageTimeMs()).isEqualTo(AVERAGE_TIME_MS);
}
@Test
public void testLog() {
mSummaryTip.log(mContext, mMetricsFeatureProvider);
verify(mMetricsFeatureProvider).action(mContext,
MetricsProto.MetricsEvent.ACTION_SUMMARY_TIP, BatteryTip.StateType.NEW);
}
}