diff --git a/src/com/android/settings/fuelgauge/BatteryMeterView.java b/src/com/android/settings/fuelgauge/BatteryMeterView.java index c450b900cb2..969f886f1d6 100644 --- a/src/com/android/settings/fuelgauge/BatteryMeterView.java +++ b/src/com/android/settings/fuelgauge/BatteryMeterView.java @@ -38,6 +38,8 @@ public class BatteryMeterView extends ImageView { @VisibleForTesting ColorFilter mAccentColorFilter; + private int mLevel; + public BatteryMeterView(Context context) { this(context, null, 0); } @@ -64,6 +66,7 @@ public class BatteryMeterView extends ImageView { } public void setBatteryLevel(int level) { + mLevel = level; mDrawable.setBatteryLevel(level); if (level < mDrawable.getCriticalLevel()) { mDrawable.setBatteryColorFilter(mErrorColorFilter); @@ -72,6 +75,10 @@ public class BatteryMeterView extends ImageView { } } + public int getBatteryLevel() { + return mLevel; + } + public void setCharging(boolean charging) { mDrawable.setCharging(charging); } diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java index b39fcfc0d9b..0a622c71645 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java +++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java @@ -16,6 +16,8 @@ package com.android.settings.fuelgauge; +import android.animation.Animator; +import android.animation.ValueAnimator; import android.app.Activity; import android.app.LoaderManager; import android.content.Context; @@ -48,6 +50,7 @@ import android.util.SparseArray; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.view.animation.AnimationUtils; import android.widget.TextView; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -96,6 +99,10 @@ public class PowerUsageSummary extends PowerUsageBase implements private static final String KEY_BATTERY_HEADER = "battery_header"; 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 int BATTERY_ANIMATION_DURATION_MS_PER_LEVEL = 30; + + @VisibleForTesting + static final String ARG_BATTERY_LEVEL = "key_battery_level"; private static final String KEY_SCREEN_USAGE = "screen_usage"; private static final String KEY_TIME_SINCE_LAST_FULL_CHARGE = "last_full_charge"; @@ -119,6 +126,8 @@ public class PowerUsageSummary extends PowerUsageBase implements private final FooterPreferenceMixin mFooterPreferenceMixin = new FooterPreferenceMixin(this, getLifecycle()); + @VisibleForTesting + int mBatteryLevel; @VisibleForTesting boolean mShowAllApps = false; @VisibleForTesting @@ -209,6 +218,8 @@ public class PowerUsageSummary extends PowerUsageBase implements super.onCreate(icicle); setAnimationAllowed(true); + mBatteryLevel = getContext().getResources().getInteger( + com.android.internal.R.integer.config_criticalBatteryWarningLevel) + 1; mBatteryLayoutPref = (LayoutPreference) findPreference(KEY_BATTERY_HEADER); mAppListGroup = (PreferenceGroup) findPreference(KEY_APP_LIST); mScreenUsagePref = (PowerGaugePreference) findPreference(KEY_SCREEN_USAGE); @@ -227,6 +238,14 @@ public class PowerUsageSummary extends PowerUsageBase implements } } + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + if (savedInstanceState != null) { + mBatteryLevel = savedInstanceState.getInt(ARG_BATTERY_LEVEL); + } + } + @Override public int getMetricsCategory() { return MetricsEvent.FUELGAUGE_POWER_USAGE_SUMMARY; @@ -235,6 +254,8 @@ public class PowerUsageSummary extends PowerUsageBase implements @Override public void onResume() { super.onResume(); + + initHeaderPreference(); } @Override @@ -252,6 +273,12 @@ public class PowerUsageSummary extends PowerUsageBase implements } } + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt(ARG_BATTERY_LEVEL, mBatteryLevel); + } + @Override public boolean onPreferenceTreeClick(Preference preference) { if (mAnomalySummaryPreferenceController.onPreferenceTreeClick(preference)) { @@ -681,15 +708,46 @@ public class PowerUsageSummary extends PowerUsageBase implements .findViewById(R.id.battery_header_icon); final TextView timeText = (TextView) mBatteryLayoutPref.findViewById(R.id.battery_percent); final TextView summary1 = (TextView) mBatteryLayoutPref.findViewById(R.id.summary1); - timeText.setText(Utils.formatPercentage(info.batteryLevel)); if (info.remainingLabel == null ) { summary1.setText(info.statusLabel); } else { summary1.setText(info.remainingLabel); } - - batteryView.setBatteryLevel(info.batteryLevel); batteryView.setCharging(!info.discharging); + startBatteryHeaderAnimationIfNecessary(batteryView, timeText, mBatteryLevel, + info.batteryLevel); + } + + @VisibleForTesting + void initHeaderPreference() { + final BatteryMeterView batteryView = (BatteryMeterView) mBatteryLayoutPref + .findViewById(R.id.battery_header_icon); + final TextView timeText = (TextView) mBatteryLayoutPref.findViewById(R.id.battery_percent); + + batteryView.setBatteryLevel(mBatteryLevel); + timeText.setText(Utils.formatPercentage(mBatteryLevel)); + } + + @VisibleForTesting + void startBatteryHeaderAnimationIfNecessary(BatteryMeterView batteryView, TextView timeTextView, + int prevLevel, int currentLevel) { + mBatteryLevel = currentLevel; + final int diff = Math.abs(prevLevel - currentLevel); + if (diff != 0) { + final ValueAnimator animator = ValueAnimator.ofInt(prevLevel, currentLevel); + animator.setDuration(BATTERY_ANIMATION_DURATION_MS_PER_LEVEL * diff); + animator.setInterpolator(AnimationUtils.loadInterpolator(getContext(), + android.R.interpolator.fast_out_slow_in)); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + final Integer level = (Integer) animation.getAnimatedValue(); + batteryView.setBatteryLevel(level); + timeTextView.setText(Utils.formatPercentage(level)); + } + }); + animator.start(); + } } @VisibleForTesting diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java index 95e33bbd494..c068d118c54 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java @@ -131,8 +131,6 @@ public class PowerUsageSummaryTest { @Mock private LayoutPreference mBatteryLayoutPref; @Mock - private TextView mBatteryPercentText; - @Mock private TextView mSummary1; @Mock private BatteryInfo mBatteryInfo; @@ -147,6 +145,7 @@ public class PowerUsageSummaryTest { @Mock private ContentResolver mContentResolver; + private TextView mBatteryPercentText; private List mUsageList; private Context mRealContext; private TestFragment mFragment; @@ -171,7 +170,7 @@ public class PowerUsageSummaryTest { mLastFullChargePref = new PowerGaugePreference(mRealContext); mFragment = spy(new TestFragment(mContext)); mFragment.initFeatureProvider(); - mBatteryMeterView = new BatteryMeterView(mRealContext); + mBatteryMeterView = spy(new BatteryMeterView(mRealContext)); mBatteryMeterView.mDrawable = new BatteryMeterView.BatteryMeterDrawable(mRealContext, 0); when(mFragment.getActivity()).thenReturn(mSettingsActivity); @@ -193,6 +192,7 @@ public class PowerUsageSummaryTest { mCellBatterySipper.drainType = BatterySipper.DrainType.CELL; mCellBatterySipper.totalPowerMah = POWER_MAH; + mBatteryPercentText = new TextView(mRealContext); when(mBatteryLayoutPref.findViewById(R.id.summary1)).thenReturn(mSummary1); when(mBatteryLayoutPref.findViewById(R.id.battery_percent)).thenReturn(mBatteryPercentText); when(mBatteryLayoutPref.findViewById(R.id.battery_header_icon)) @@ -285,6 +285,50 @@ public class PowerUsageSummaryTest { testToggleAllApps(false); } + @Test + public void testInitHeaderPreference_initCorrectly() { + mFragment.mBatteryLevel = 100; + + mFragment.initHeaderPreference(); + + assertThat(mBatteryMeterView.getBatteryLevel()).isEqualTo(100); + assertThat(mBatteryPercentText.getText().toString()).isEqualTo("100%"); + } + + @Test + public void testStartBatteryHeaderAnimationIfNecessary_batteryLevelChanged_animationStarted() { + final int prevLevel = 100; + final int curLevel = 80; + + mFragment.startBatteryHeaderAnimationIfNecessary(mBatteryMeterView, mBatteryPercentText, + prevLevel, curLevel); + + assertThat(mBatteryMeterView.getBatteryLevel()).isEqualTo(curLevel); + assertThat(mBatteryPercentText.getText().toString()).isEqualTo("80%"); + } + + @Test + public void testOnSaveInstanceState_saveBatteryLevel() { + Bundle bundle = new Bundle(); + mFragment.mBatteryLevel = BATTERY_LEVEL; + // mock it to stop crash in getPreferenceScreen + doReturn(null).when(mFragment).getPreferenceScreen(); + + mFragment.onSaveInstanceState(bundle); + + assertThat(bundle.getInt(PowerUsageSummary.ARG_BATTERY_LEVEL)).isEqualTo(BATTERY_LEVEL); + } + + @Test + public void testOnActivityCreated_setBatteryLevel() { + Bundle bundle = new Bundle(); + bundle.putInt(PowerUsageSummary.ARG_BATTERY_LEVEL, BATTERY_LEVEL); + + mFragment.onActivityCreated(bundle); + + assertThat(mFragment.mBatteryLevel).isEqualTo(BATTERY_LEVEL); + } + @Test public void testExtractKeyFromSipper_typeAPPUidObjectNull_returnPackageNames() { mNormalBatterySipper.uidObj = null;