Merge "[DO NOT MERGE] Fix flicker for Data Usage page" into tm-dev
diff --git a/res/xml/data_usage_list.xml b/res/xml/data_usage_list.xml
index 9ea6a91..644fca4 100644
--- a/res/xml/data_usage_list.xml
+++ b/res/xml/data_usage_list.xml
@@ -17,7 +17,8 @@
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
 
     <PreferenceCategory
-        android:key="usage_amount">
+        android:key="usage_amount"
+        android:title="@string/summary_placeholder">
 
         <com.android.settings.datausage.ChartDataUsagePreference
             android:key="chart_data" />
diff --git a/src/com/android/settings/datausage/CycleAdapter.java b/src/com/android/settings/datausage/CycleAdapter.java
index b41b6aa..2af4012 100644
--- a/src/com/android/settings/datausage/CycleAdapter.java
+++ b/src/com/android/settings/datausage/CycleAdapter.java
@@ -21,7 +21,6 @@
 import com.android.settingslib.widget.SettingsSpinnerAdapter;
 
 import java.util.List;
-import java.util.Objects;
 
 public class CycleAdapter extends SettingsSpinnerAdapter<CycleAdapter.CycleItem> {
 
@@ -67,7 +66,7 @@
      * Rebuild list based on network data. Always selects the newest item,
      * updating the inspection range on chartData.
      */
-    public boolean updateCycleList(List<? extends NetworkCycleData> cycleData) {
+    public void updateCycleList(List<? extends NetworkCycleData> cycleData) {
         mSpinner.setOnItemSelectedListener(mListener);
         // stash away currently selected cycle to try restoring below
         final CycleAdapter.CycleItem previousItem = (CycleAdapter.CycleItem)
@@ -83,16 +82,7 @@
         if (getCount() > 0) {
             final int position = findNearestPosition(previousItem);
             mSpinner.setSelection(position);
-
-            // only force-update cycle when changed; skipping preserves any
-            // user-defined inspection region.
-            final CycleAdapter.CycleItem selectedItem = getItem(position);
-            if (!Objects.equals(selectedItem, previousItem)) {
-                mListener.onItemSelected(null, null, position, 0);
-                return false;
-            }
         }
-        return true;
     }
 
     /**
diff --git a/src/com/android/settings/datausage/DataUsageList.java b/src/com/android/settings/datausage/DataUsageList.java
index 4a44a16..9d8fbb1 100644
--- a/src/com/android/settings/datausage/DataUsageList.java
+++ b/src/com/android/settings/datausage/DataUsageList.java
@@ -69,6 +69,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Panel showing data usage history across various networks, including options
@@ -111,7 +112,11 @@
 
     private ChartDataUsagePreference mChart;
     private List<NetworkCycleChartData> mCycleData;
+    // Caches the cycles for startAppDataUsage usage, which need be cleared when resumed.
     private ArrayList<Long> mCycles;
+    // Spinner will keep the selected cycle even after paused, this only keeps the displayed cycle,
+    // which need be cleared when resumed.
+    private CycleAdapter.CycleItem mLastDisplayedCycle;
     private UidDetailProvider mUidDetailProvider;
     private CycleAdapter mCycleAdapter;
     private Preference mUsageAmount;
@@ -199,13 +204,15 @@
 
         mLoadingViewController = new LoadingViewController(
                 getView().findViewById(R.id.loading_container), getListView());
-        mLoadingViewController.showLoadingViewDelayed();
     }
 
     @Override
     public void onResume() {
         super.onResume();
+        mLoadingViewController.showLoadingViewDelayed();
         mDataStateListener.start(mSubId);
+        mCycles = null;
+        mLastDisplayedCycle = null;
 
         // kick off loader for network history
         // TODO: consider chaining two loaders together instead of reloading
@@ -319,9 +326,46 @@
         }
 
         // generate cycle list based on policy and available history
-        if (mCycleAdapter.updateCycleList(mCycleData)) {
-            updateDetailData();
+        mCycleAdapter.updateCycleList(mCycleData);
+        updateSelectedCycle();
+    }
+
+    /**
+     * Updates the chart and detail data when initial loaded or selected cycle changed.
+     */
+    private void updateSelectedCycle() {
+        // Avoid from updating UI after #onStop.
+        if (!getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
+            return;
         }
+
+        // Avoid from updating UI when async query still on-going.
+        // This could happen when a request from #onMobileDataEnabledChange.
+        if (mCycleData == null) {
+            return;
+        }
+
+        final int position = mCycleSpinner.getSelectedItemPosition();
+        if (mCycleAdapter.getCount() == 0 || position < 0) {
+            return;
+        }
+        final CycleAdapter.CycleItem cycle = mCycleAdapter.getItem(position);
+        if (Objects.equals(cycle, mLastDisplayedCycle)) {
+            // Avoid duplicate update to avoid page flash.
+            return;
+        }
+        mLastDisplayedCycle = cycle;
+
+        if (LOGD) {
+            Log.d(TAG, "showing cycle " + cycle + ", [start=" + cycle.start + ", end="
+                    + cycle.end + "]");
+        }
+
+        // update chart to show selected cycle, and update detail data
+        // to match updated sweep bounds.
+        mChart.setNetworkCycleData(mCycleData.get(position));
+
+        updateDetailData();
     }
 
     /**
@@ -495,33 +539,10 @@
         return Math.max(largest, item.total);
     }
 
-    private OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
+    private final OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
         @Override
         public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
-            final CycleAdapter.CycleItem cycle = (CycleAdapter.CycleItem)
-                    mCycleSpinner.getSelectedItem();
-
-            if (LOGD) {
-                Log.d(TAG, "showing cycle " + cycle + ", start=" + cycle.start + ", end="
-                        + cycle.end + "]");
-            }
-
-            // Avoid from updating UI after #onStop.
-            if (!getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
-                return;
-            }
-
-            // Avoid from updating UI when async query still on-going.
-            // This could happen when a request from #onMobileDataEnabledChange.
-            if (mCycleData == null) {
-                return;
-            }
-
-            // update chart to show selected cycle, and update detail data
-            // to match updated sweep bounds.
-            mChart.setNetworkCycleData(mCycleData.get(position));
-
-            updateDetailData();
+            updateSelectedCycle();
         }
 
         @Override
diff --git a/src/com/android/settings/datausage/SpinnerPreference.java b/src/com/android/settings/datausage/SpinnerPreference.java
index c4b7a4e..c6b5f9f 100644
--- a/src/com/android/settings/datausage/SpinnerPreference.java
+++ b/src/com/android/settings/datausage/SpinnerPreference.java
@@ -14,6 +14,7 @@
 
 package com.android.settings.datausage;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.View;
@@ -28,6 +29,7 @@
 public class SpinnerPreference extends Preference implements CycleAdapter.SpinnerInterface {
 
     private CycleAdapter mAdapter;
+    @Nullable
     private AdapterView.OnItemSelectedListener mListener;
     private Object mCurrentObject;
     private int mPosition;
@@ -88,19 +90,24 @@
         view.findViewById(R.id.cycles_spinner).performClick();
     }
 
-    private final AdapterView.OnItemSelectedListener mOnSelectedListener
-            = new AdapterView.OnItemSelectedListener() {
-        @Override
-        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
-            if (mPosition == position) return;
-            mPosition = position;
-            mCurrentObject = mAdapter.getItem(position);
-            mListener.onItemSelected(parent, view, position, id);
-        }
+    private final AdapterView.OnItemSelectedListener mOnSelectedListener =
+            new AdapterView.OnItemSelectedListener() {
+                @Override
+                public void onItemSelected(
+                        AdapterView<?> parent, View view, int position, long id) {
+                    if (mPosition == position) return;
+                    mPosition = position;
+                    mCurrentObject = mAdapter.getItem(position);
+                    if (mListener != null) {
+                        mListener.onItemSelected(parent, view, position, id);
+                    }
+                }
 
-        @Override
-        public void onNothingSelected(AdapterView<?> parent) {
-            mListener.onNothingSelected(parent);
-        }
-    };
+                @Override
+                public void onNothingSelected(AdapterView<?> parent) {
+                    if (mListener != null) {
+                        mListener.onNothingSelected(parent);
+                    }
+                }
+            };
 }
diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java b/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java
index e4f5b1f..a13cf6c 100644
--- a/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java
+++ b/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java
@@ -94,6 +94,7 @@
                 mMobileDataEnabledListener);
         ReflectionHelpers.setField(mDataUsageList, "services", mNetworkServices);
         doReturn(mLoaderManager).when(mDataUsageList).getLoaderManager();
+        mDataUsageList.mLoadingViewController = mock(LoadingViewController.class);
     }
 
     @Test
@@ -207,8 +208,6 @@
 
     @Test
     public void onLoadFinished_networkCycleDataCallback_shouldShowCycleSpinner() {
-        final LoadingViewController loadingViewController = mock(LoadingViewController.class);
-        mDataUsageList.mLoadingViewController = loadingViewController;
         final Spinner spinner = getSpinner(getHeader());
         spinner.setVisibility(View.INVISIBLE);
         mDataUsageList.mCycleSpinner = spinner;