From a3c528f64db7aa96c258bdc2f6dad0a8d6897461 Mon Sep 17 00:00:00 2001 From: jackqdyulei Date: Fri, 21 Apr 2017 13:12:06 -0700 Subject: [PATCH] Add animation for battery asyncLoader. When battery asyncLoader is loading batterystats, show a battery placeholder(critical level) and animate to real status once data is loaded. Bug: 35917699 Test: RunSettingsRoboTests Change-Id: Ie4ff73ff2cc0b9e17b4541e3284bf2ac34da42c4 Merged-In: Ie4ff73ff2cc0b9e17b4541e3284bf2ac34da42c4 --- .../settings/fuelgauge/BatteryMeterView.java | 7 ++ .../settings/fuelgauge/PowerUsageSummary.java | 64 ++++++++++++++++++- .../fuelgauge/PowerUsageSummaryTest.java | 51 ++++++++++++++- 3 files changed, 116 insertions(+), 6 deletions(-) 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 bcf830bc442..0309296bd14 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.content.Context; import android.content.Intent; @@ -41,6 +43,7 @@ import android.util.TypedValue; 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; @@ -84,6 +87,10 @@ public class PowerUsageSummary extends PowerUsageBase { 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"; @@ -104,6 +111,8 @@ public class PowerUsageSummary extends PowerUsageBase { private final FooterPreferenceMixin mFooterPreferenceMixin = new FooterPreferenceMixin(this, getLifecycle()); + @VisibleForTesting + int mBatteryLevel; @VisibleForTesting boolean mShowAllApps = false; @VisibleForTesting @@ -124,6 +133,8 @@ public class PowerUsageSummary extends PowerUsageBase { 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); @@ -136,6 +147,14 @@ public class PowerUsageSummary extends PowerUsageBase { initFeatureProvider(); } + @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; @@ -144,6 +163,8 @@ public class PowerUsageSummary extends PowerUsageBase { @Override public void onResume() { super.onResume(); + + initHeaderPreference(); } @Override @@ -161,6 +182,12 @@ public class PowerUsageSummary extends PowerUsageBase { } } + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt(ARG_BATTERY_LEVEL, mBatteryLevel); + } + @Override public boolean onPreferenceTreeClick(Preference preference) { if (KEY_BATTERY_HEADER.equals(preference.getKey())) { @@ -569,15 +596,46 @@ public class PowerUsageSummary extends PowerUsageBase { .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 3556d09a53f..3d5d5dce6f2 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java @@ -17,6 +17,7 @@ package com.android.settings.fuelgauge; import android.content.Context; import android.content.Intent; +import android.os.Bundle; import android.os.PowerManager; import android.os.Process; import android.text.TextUtils; @@ -128,8 +129,6 @@ public class PowerUsageSummaryTest { @Mock private LayoutPreference mBatteryLayoutPref; @Mock - private TextView mBatteryPercentText; - @Mock private TextView mSummary1; @Mock private BatteryInfo mBatteryInfo; @@ -140,6 +139,7 @@ public class PowerUsageSummaryTest { @Mock private SettingsActivity mSettingsActivity; + private TextView mBatteryPercentText; private List mUsageList; private Context mRealContext; private TestFragment mFragment; @@ -163,7 +163,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); @@ -185,6 +185,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)) @@ -277,6 +278,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;