Merge "add a global control for badging" into oc-dev
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index caac95f..edf2b75 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -271,6 +271,7 @@
             Settings.PrintSettingsActivity.class.getName(),
             // Home page > Apps & Notifications
             Settings.UserSettingsActivity.class.getName(),
+            Settings.ConfigureNotificationSettingsActivity.class.getName(),
             Settings.ManageApplicationsActivity.class.getName(),
             Settings.PaymentSettingsActivity.class.getName(),
             // Home page > Security & screen lock
diff --git a/src/com/android/settings/fuelgauge/BatteryMeterView.java b/src/com/android/settings/fuelgauge/BatteryMeterView.java
index c450b90..969f886 100644
--- a/src/com/android/settings/fuelgauge/BatteryMeterView.java
+++ b/src/com/android/settings/fuelgauge/BatteryMeterView.java
@@ -38,6 +38,8 @@
     @VisibleForTesting
     ColorFilter mAccentColorFilter;
 
+    private int mLevel;
+
     public BatteryMeterView(Context context) {
         this(context, null, 0);
     }
@@ -64,6 +66,7 @@
     }
 
     public void setBatteryLevel(int level) {
+        mLevel = level;
         mDrawable.setBatteryLevel(level);
         if (level < mDrawable.getCriticalLevel()) {
             mDrawable.setBatteryColorFilter(mErrorColorFilter);
@@ -72,6 +75,10 @@
         }
     }
 
+    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 bcf830b..0309296 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.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 @@
     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";
@@ -105,6 +112,8 @@
             new FooterPreferenceMixin(this, getLifecycle());
 
     @VisibleForTesting
+    int mBatteryLevel;
+    @VisibleForTesting
     boolean mShowAllApps = false;
     @VisibleForTesting
     PowerGaugePreference mScreenUsagePref;
@@ -124,6 +133,8 @@
         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);
@@ -137,6 +148,14 @@
     }
 
     @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 @@
     @Override
     public void onResume() {
         super.onResume();
+
+        initHeaderPreference();
     }
 
     @Override
@@ -162,6 +183,12 @@
     }
 
     @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())) {
             performBatteryHeaderClick();
@@ -569,15 +596,46 @@
                 .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/src/com/android/settings/search2/SearchFragment.java b/src/com/android/settings/search2/SearchFragment.java
index 5ee22b5..2a5c961 100644
--- a/src/com/android/settings/search2/SearchFragment.java
+++ b/src/com/android/settings/search2/SearchFragment.java
@@ -291,6 +291,9 @@
      */
     @Override
     public void onIndexingFinished() {
+        if (getActivity() == null) {
+            return;
+        }
         if (mShowingSavedQuery) {
             mSavedQueryController.loadSavedQueries();
         } else {
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
index 3556d09..3d5d5dc 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 @@
 
 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 @@
     @Mock
     private LayoutPreference mBatteryLayoutPref;
     @Mock
-    private TextView mBatteryPercentText;
-    @Mock
     private TextView mSummary1;
     @Mock
     private BatteryInfo mBatteryInfo;
@@ -140,6 +139,7 @@
     @Mock
     private SettingsActivity mSettingsActivity;
 
+    private TextView mBatteryPercentText;
     private List<BatterySipper> mUsageList;
     private Context mRealContext;
     private TestFragment mFragment;
@@ -163,7 +163,7 @@
         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 @@
         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))
@@ -278,6 +279,50 @@
     }
 
     @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;
         mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
diff --git a/tests/robotests/src/com/android/settings/search2/SearchFragmentTest.java b/tests/robotests/src/com/android/settings/search2/SearchFragmentTest.java
index 2296370..2e91ec6 100644
--- a/tests/robotests/src/com/android/settings/search2/SearchFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/search2/SearchFragmentTest.java
@@ -349,4 +349,21 @@
 
         verify(fragment.mSavedQueryController).loadSavedQueries();
     }
+
+    @Test
+    public void onIndexingFinished_noActivity_shouldNotCrash() {
+        ActivityController<SearchActivity> activityController =
+            Robolectric.buildActivity(SearchActivity.class);
+        activityController.setup();
+        SearchFragment fragment = (SearchFragment) spy(activityController.get().getFragmentManager()
+            .findFragmentById(R.id.main_content));
+        when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class)))
+            .thenReturn(true);
+        fragment.mQuery = "bright";
+        ReflectionHelpers.setField(fragment, "mLoaderManager", null);
+        ReflectionHelpers.setField(fragment, "mHost", null);
+
+        fragment.onIndexingFinished();
+        // no crash
+    }
 }