Only show Data Saver usage data since enabled if less than 30 days

Includes some refactoring in how the network stats data is reconciled
with time of day for presenting day labels. The previous TimeZoneOffset
approach really didn't work and that became apparent when charting
a single day.

Bug: 820286
Change-Id: If14b456aaef6ae5729a9292842364f4115d0a563
Reviewed-on: https://chromium-review.googlesource.com/1111080
Reviewed-by: Ben Greenstein <bengr@chromium.org>
Reviewed-by: Theresa <twellington@chromium.org>
Commit-Queue: Doug Arnett <dougarnett@chromium.org>
Cr-Commit-Position: refs/heads/master@{#576596}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionMainMenuItem.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionMainMenuItem.java
index ee07ca5..e60aef2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionMainMenuItem.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionMainMenuItem.java
@@ -57,7 +57,7 @@
 
             long chartStartDateInMillisSinceEpoch =
                     DataReductionProxySettings.getInstance().getDataReductionLastUpdateTime()
-                    - DateUtils.DAY_IN_MILLIS * ChartDataUsageView.DAYS_IN_CHART;
+                    - DateUtils.DAY_IN_MILLIS * ChartDataUsageView.MAXIMUM_DAYS_IN_CHART;
             long firstEnabledInMillisSinceEpoch = DataReductionProxySettings.getInstance()
                                                           .getDataReductionProxyFirstEnabledTime();
             long mostRecentTime = chartStartDateInMillisSinceEpoch > firstEnabledInMillisSinceEpoch
@@ -110,4 +110,4 @@
         Tracker tracker = TrackerFactory.getTrackerForProfile(Profile.getLastUsedProfile());
         tracker.notifyEvent(EventConstants.DATA_SAVER_DETAIL_OPENED);
     }
-}
\ No newline at end of file
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreference.java
index bccc386..ef27eda 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreference.java
@@ -8,7 +8,8 @@
 import static android.text.format.DateUtils.FORMAT_NO_YEAR;
 import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
 
-import static org.chromium.third_party.android.datausagechart.ChartDataUsageView.DAYS_IN_CHART;
+import static org.chromium.third_party.android.datausagechart.ChartDataUsageView.MAXIMUM_DAYS_IN_CHART;
+import static org.chromium.third_party.android.datausagechart.ChartDataUsageView.MINIMUM_DAYS_IN_CHART;
 
 import android.annotation.SuppressLint;
 import android.content.Context;
@@ -27,6 +28,7 @@
 import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
+import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
 import org.chromium.chrome.browser.util.FileSizeUtil;
@@ -45,7 +47,7 @@
 
     /**
      * Key used to save the date on which the site breakdown should be shown. If the user has
-     * historical data saver stats, the site breakdown cannot be shown for DAYS_IN_CHART.
+     * historical data saver stats, the site breakdown cannot be shown for MAXIMUM_DAYS_IN_CHART.
      */
     private static final String PREF_DATA_REDUCTION_SITE_BREAKDOWN_ALLOWED_DATE =
             "data_reduction_site_breakdown_allowed_date";
@@ -61,9 +63,13 @@
     private Button mResetStatisticsButton;
     private ChartDataUsageView mChartDataUsageView;
     private DataReductionSiteBreakdownView mDataReductionBreakdownView;
-    private long mLeftPosition;
-    private long mRightPosition;
-    private Long mCurrentTime;
+    private boolean mIsFirstDayChart;
+    /** Number of days that the chart will present. */
+    private int mNumDaysInChart;
+    /** Number of milliseconds from the beginning of the day. */
+    private long mTimeOfDayOffsetMillis;
+    private long mVisibleStartTimeMillis;
+    private long mVisibleEndTimeMillis;
     private CharSequence mSavingsTotalPhrase;
     private CharSequence mReceivedTotalPhrase;
     private String mStartDatePhrase;
@@ -84,9 +90,10 @@
                 DataReductionProxySettings.getInstance().getDataReductionLastUpdateTime();
 
         // If the site breakdown is enabled and there are historical stats within the last
-        // DAYS_IN_CHART days, don't show the breakdown for another DAYS_IN_CHART days from the last
-        // update time. Otherwise, the site breakdown can be shown starting now.
-        long timeChartCanBeShown = lastUpdateTimeMillis + DAYS_IN_CHART * DateUtils.DAY_IN_MILLIS;
+        // MAXIMUM_DAYS_IN_CHART days, don't show the breakdown for another MAXIMUM_DAYS_IN_CHART
+        // days from the last update time. Otherwise, the site breakdown can be shown starting now.
+        long timeChartCanBeShown =
+                lastUpdateTimeMillis + MAXIMUM_DAYS_IN_CHART * DateUtils.DAY_IN_MILLIS;
         long now = System.currentTimeMillis();
         ContextUtils.getAppSharedPreferences()
                 .edit()
@@ -108,24 +115,60 @@
     /**
      * Updates the preference screen to convey current statistics on data reduction.
      */
-    public void updateReductionStatistics() {
+    public void updateReductionStatistics(long currentTimeMillis) {
         long original[] = DataReductionProxySettings.getInstance().getOriginalNetworkStatsHistory();
         long received[] = DataReductionProxySettings.getInstance().getReceivedNetworkStatsHistory();
 
-        mCurrentTime = DataReductionProxySettings.getInstance().getDataReductionLastUpdateTime();
-        mRightPosition = mCurrentTime + DateUtils.HOUR_IN_MILLIS
-                - TimeZone.getDefault().getOffset(mCurrentTime);
-        mLeftPosition = mCurrentTime - DateUtils.DAY_IN_MILLIS * DAYS_IN_CHART;
-        mOriginalNetworkStatsHistory = getNetworkStatsHistory(original, DAYS_IN_CHART);
-        mReceivedNetworkStatsHistory = getNetworkStatsHistory(received, DAYS_IN_CHART);
+        long dataSaverEnabledDayMillis =
+                (DataReductionProxySettings.getInstance().getDataReductionProxyFirstEnabledTime()
+                        / DateUtils.DAY_IN_MILLIS)
+                * DateUtils.DAY_IN_MILLIS;
+        long dataSaverEnabledTimeMillis =
+                DataReductionProxySettings.getInstance().getDataReductionProxyFirstEnabledTime();
+        long startOfToday = currentTimeMillis - currentTimeMillis % DateUtils.DAY_IN_MILLIS
+                - TimeZone.getDefault().getOffset(currentTimeMillis);
+        long statsLastUpdateTimeMillis =
+                DataReductionProxySettings.getInstance().getDataReductionLastUpdateTime();
+        if (statsLastUpdateTimeMillis == 0) {
+            // Use startOfToday if stats were recently reset.
+            statsLastUpdateTimeMillis = startOfToday;
+        }
+        Long numDaysSinceStatsUpdated = (statsLastUpdateTimeMillis < startOfToday)
+                ? (startOfToday - statsLastUpdateTimeMillis) / DateUtils.DAY_IN_MILLIS
+                : 0;
+
+        // Capture the offset from the start of today until the current time for determining the
+        // day label to present later (handles timezone adjustment).
+        mTimeOfDayOffsetMillis = currentTimeMillis - startOfToday;
+
+        // Determine the start and end time of the network stats to chart.
+        Long daysEnabled =
+                (currentTimeMillis - dataSaverEnabledDayMillis) / DateUtils.DAY_IN_MILLIS + 1;
+        mIsFirstDayChart = false;
+        mNumDaysInChart = MAXIMUM_DAYS_IN_CHART;
+        if (daysEnabled < MINIMUM_DAYS_IN_CHART) {
+            mIsFirstDayChart = true;
+            mNumDaysInChart = MINIMUM_DAYS_IN_CHART;
+        } else if (daysEnabled < MAXIMUM_DAYS_IN_CHART) {
+            mNumDaysInChart = daysEnabled.intValue();
+        }
+        mOriginalNetworkStatsHistory = getNetworkStatsHistory(original, mNumDaysInChart);
+        mReceivedNetworkStatsHistory = getNetworkStatsHistory(received, mNumDaysInChart);
+
+        // Determine the visible start and end points based on the available data and when it was
+        // last updated.
+        mVisibleStartTimeMillis = mOriginalNetworkStatsHistory.getStart()
+                + numDaysSinceStatsUpdated.intValue() * DateUtils.DAY_IN_MILLIS
+                + DateUtils.DAY_IN_MILLIS;
+        mVisibleEndTimeMillis = mOriginalNetworkStatsHistory.getEnd()
+                + numDaysSinceStatsUpdated.intValue() * DateUtils.DAY_IN_MILLIS;
 
         if (mDataReductionBreakdownView != null
-                && System.currentTimeMillis()
-                        > ContextUtils.getAppSharedPreferences().getLong(
-                                  PREF_DATA_REDUCTION_SITE_BREAKDOWN_ALLOWED_DATE,
-                                  Long.MAX_VALUE)) {
+                && currentTimeMillis > ContextUtils.getAppSharedPreferences().getLong(
+                                               PREF_DATA_REDUCTION_SITE_BREAKDOWN_ALLOWED_DATE,
+                                               Long.MAX_VALUE)) {
             DataReductionProxySettings.getInstance().queryDataUsage(
-                    DAYS_IN_CHART, new Callback<List<DataReductionDataUseItem>>() {
+                    mNumDaysInChart, new Callback<List<DataReductionDataUseItem>>() {
                         @Override
                         public void onResult(List<DataReductionDataUseItem> result) {
                             mSiteBreakdownItems = result;
@@ -136,6 +179,15 @@
         }
     }
 
+    /**
+     * Returns the number of days that the data usage graph should display based on the last
+     * update to the statistics.
+     */
+    @VisibleForTesting
+    int getNumDaysInChart() {
+        return mNumDaysInChart;
+    }
+
     private static NetworkStatsHistory getNetworkStatsHistory(long[] history, int days) {
         if (days > history.length) days = history.length;
         NetworkStatsHistory networkStatsHistory = new NetworkStatsHistory(
@@ -192,18 +244,16 @@
         if (mOriginalNetworkStatsHistory == null) {
             // This will query data usage. Only set mSiteBreakdownItems if the statistics are not
             // being queried.
-            updateReductionStatistics();
+            updateReductionStatistics(System.currentTimeMillis());
         } else if (mSiteBreakdownItems != null) {
             mDataReductionBreakdownView.setAndDisplayDataUseItems(mSiteBreakdownItems);
         }
         setDetailText();
 
         mChartDataUsageView = (ChartDataUsageView) view.findViewById(R.id.chart);
-        mChartDataUsageView.bindOriginalNetworkStats(mOriginalNetworkStatsHistory);
-        mChartDataUsageView.bindCompressedNetworkStats(mReceivedNetworkStatsHistory);
-        mChartDataUsageView.setVisibleRange(
-                mCurrentTime - DateUtils.DAY_IN_MILLIS * DAYS_IN_CHART,
-                mCurrentTime + DateUtils.HOUR_IN_MILLIS, mLeftPosition, mRightPosition);
+        mChartDataUsageView.bindNetworkStats(
+                mOriginalNetworkStatsHistory, mReceivedNetworkStatsHistory);
+        mChartDataUsageView.setVisibleRange(mVisibleStartTimeMillis, mVisibleEndTimeMillis);
 
         if (DataReductionProxySettings.getInstance().isDataReductionProxyUnreachable()) {
             // Leave breadcrumb in log for user feedback report.
@@ -241,7 +291,7 @@
                             DataReductionProxySettings.getInstance().clearDataSavingStatistics(
                                     DataReductionProxySavingsClearedReason
                                             .USER_ACTION_SETTINGS_MENU);
-                            updateReductionStatistics();
+                            updateReductionStatistics(now);
                             setDetailText();
                             notifyChanged();
                             DataReductionProxyUma.dataReductionProxyUIAction(
@@ -275,9 +325,10 @@
     // TODO(crbug.com/635567): Fix this properly.
     @SuppressLint("DefaultLocale")
     private void updateDetailData() {
-        final long start = mLeftPosition;
-        // Include up to the last second of the currently selected day.
-        final long end = mRightPosition;
+        // To determine the correct day labels, adjust the network stats time values by their
+        // offset from the client's current time.
+        final long startDay = mVisibleStartTimeMillis + mTimeOfDayOffsetMillis;
+        final long endDay = mVisibleEndTimeMillis + mTimeOfDayOffsetMillis;
         final Context context = getContext();
 
         final long compressedTotalBytes = mReceivedNetworkStatsHistory.getTotalBytes();
@@ -285,8 +336,14 @@
         final long originalTotalBytes = mOriginalNetworkStatsHistory.getTotalBytes();
         final long savingsTotalBytes = originalTotalBytes - compressedTotalBytes;
         mSavingsTotalPhrase = FileSizeUtil.formatFileSize(context, savingsTotalBytes);
-        mStartDatePhrase = formatDate(context, start);
-        mEndDatePhrase = formatDate(context, end);
+        if (mIsFirstDayChart) {
+            // Only show the current date on the left hand side for the single-day-chart.
+            mStartDatePhrase = formatDate(context, endDay);
+            mEndDatePhrase = null;
+        } else {
+            mStartDatePhrase = formatDate(context, startDay);
+            mEndDatePhrase = formatDate(context, endDay);
+        }
 
         DataReductionProxyUma.dataReductionProxyUserViewedSavings(
                 compressedTotalBytes, originalTotalBytes);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreferenceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreferenceTest.java
index b8610dd..7a383ff 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreferenceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreferenceTest.java
@@ -4,6 +4,9 @@
 
 package org.chromium.chrome.browser.preferences.datareduction;
 
+import static org.chromium.third_party.android.datausagechart.ChartDataUsageView.MAXIMUM_DAYS_IN_CHART;
+import static org.chromium.third_party.android.datausagechart.ChartDataUsageView.MINIMUM_DAYS_IN_CHART;
+
 import android.content.Context;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.annotation.UiThreadTest;
@@ -153,4 +156,98 @@
                                   PREF_DATA_REDUCTION_SITE_BREAKDOWN_ALLOWED_DATE, -1)
                 <= afterTime);
     }
+
+    /**
+     * Tests the timespan of the usage graph when Data Saver was enabled today.
+     */
+    @Test
+    @SmallTest
+    @UiThreadTest
+    @Feature({"DataReduction"})
+    public void testUpdateReductionStatisticsEnabledToday() throws Throwable {
+        DataReductionStatsPreference pref = new DataReductionStatsPreference(mContext, null);
+        long now = System.currentTimeMillis();
+        long lastUpdateTime = now - DateUtils.DAY_IN_MILLIS;
+        long dataSaverEnableTime = now - DateUtils.HOUR_IN_MILLIS;
+        mSettings.setDataReductionLastUpdateTime(lastUpdateTime);
+        ContextUtils.getAppSharedPreferences()
+                .edit()
+                .putLong(DataReductionProxySettings.DATA_REDUCTION_FIRST_ENABLED_TIME,
+                        dataSaverEnableTime)
+                .apply();
+        pref.updateReductionStatistics(now);
+
+        Assert.assertEquals(MINIMUM_DAYS_IN_CHART, pref.getNumDaysInChart());
+    }
+
+    /**
+     * Tests the timespan of the usage graph when Data Saver was enabled yesterday.
+     */
+    @Test
+    @SmallTest
+    @UiThreadTest
+    @Feature({"DataReduction"})
+    public void testUpdateReductionStatisticsEnabledYesterday() throws Throwable {
+        DataReductionStatsPreference pref = new DataReductionStatsPreference(mContext, null);
+        long now = System.currentTimeMillis();
+        long lastUpdateTime = now - DateUtils.DAY_IN_MILLIS;
+        long dataSaverEnableTime = now - DateUtils.DAY_IN_MILLIS;
+        mSettings.setDataReductionLastUpdateTime(lastUpdateTime);
+        ContextUtils.getAppSharedPreferences()
+                .edit()
+                .putLong(DataReductionProxySettings.DATA_REDUCTION_FIRST_ENABLED_TIME,
+                        dataSaverEnableTime)
+                .apply();
+        pref.updateReductionStatistics(now);
+
+        Assert.assertEquals(MINIMUM_DAYS_IN_CHART, pref.getNumDaysInChart());
+    }
+
+    /**
+     * Tests the timespan of the usage graph when Data Saver was enabled 31 days ago.
+     */
+    @Test
+    @SmallTest
+    @UiThreadTest
+    @Feature({"DataReduction"})
+    public void testUpdateReductionStatisticsEnabled31DaysAgo() throws Throwable {
+        DataReductionStatsPreference pref = new DataReductionStatsPreference(mContext, null);
+        long now = System.currentTimeMillis();
+        long lastUpdateTime = now - DateUtils.DAY_IN_MILLIS;
+        long dataSaverEnableTime = now - 31 * DateUtils.DAY_IN_MILLIS;
+        mSettings.setDataReductionLastUpdateTime(lastUpdateTime);
+        ContextUtils.getAppSharedPreferences()
+                .edit()
+                .putLong(DataReductionProxySettings.DATA_REDUCTION_FIRST_ENABLED_TIME,
+                        dataSaverEnableTime)
+                .apply();
+        pref.updateReductionStatistics(now);
+
+        Assert.assertEquals(MAXIMUM_DAYS_IN_CHART, pref.getNumDaysInChart());
+    }
+
+    /**
+     * Tests the timespan of the usage graph when the stats have not been
+     * updated recently.
+     */
+    @Test
+    @SmallTest
+    @UiThreadTest
+    @Feature({"DataReduction"})
+    public void testUpdateReductionStatisticsStatsNotUpdatedRecently() throws Throwable {
+        DataReductionStatsPreference pref = new DataReductionStatsPreference(mContext, null);
+        long now = System.currentTimeMillis();
+        long lastUpdateTime = now - 7 * DateUtils.DAY_IN_MILLIS;
+        int numDaysDataSaverEnabled = 10;
+        long dataSaverEnableTime = now - numDaysDataSaverEnabled * DateUtils.DAY_IN_MILLIS;
+        mSettings.setDataReductionLastUpdateTime(lastUpdateTime);
+        ContextUtils.getAppSharedPreferences()
+                .edit()
+                .putLong(DataReductionProxySettings.DATA_REDUCTION_FIRST_ENABLED_TIME,
+                        dataSaverEnableTime)
+                .apply();
+        pref.updateReductionStatistics(now);
+
+        Assert.assertEquals(numDaysDataSaverEnabled + 1, pref.getNumDaysInChart());
+    }
 }
diff --git a/third_party/android_data_chart/java/src/org/chromium/third_party/android/datausagechart/ChartDataUsageView.java b/third_party/android_data_chart/java/src/org/chromium/third_party/android/datausagechart/ChartDataUsageView.java
index 28e4761..cbe4e27 100644
--- a/third_party/android_data_chart/java/src/org/chromium/third_party/android/datausagechart/ChartDataUsageView.java
+++ b/third_party/android_data_chart/java/src/org/chromium/third_party/android/datausagechart/ChartDataUsageView.java
@@ -37,7 +37,8 @@
  * This is derived from com.android.settings.widget.ChartDataUsageView.
  */
 public class ChartDataUsageView extends ChartView {
-    public static final int DAYS_IN_CHART = 30;
+    public static final int MAXIMUM_DAYS_IN_CHART = 30;
+    public static final int MINIMUM_DAYS_IN_CHART = 2;
 
     private static final long MB_IN_BYTES = 1024 * 1024;
     private static final long GB_IN_BYTES = 1024 * 1024 * 1024;
@@ -88,28 +89,23 @@
         mCompressedSeries.init(mHoriz, mVert);
     }
 
-    public void bindOriginalNetworkStats(NetworkStatsHistory stats) {
-        mOriginalSeries.bindNetworkStats(stats);
-        // Compensate for time zone adjustments when setting the end time.
-        mHistory = stats;
-        updateVertAxisBounds();
-        updateEstimateVisible();
-        updatePrimaryRange();
-        requestLayout();
-    }
-
-    public void bindCompressedNetworkStats(NetworkStatsHistory stats) {
-        mCompressedSeries.bindNetworkStats(stats);
-        mCompressedSeries.setVisibility(stats != null ? View.VISIBLE : View.GONE);
+    public void bindNetworkStats(
+            NetworkStatsHistory originalStats, NetworkStatsHistory compressedStats) {
+        mOriginalSeries.bindNetworkStats(originalStats);
+        mCompressedSeries.bindNetworkStats(compressedStats);
+        mCompressedSeries.setVisibility(compressedStats != null ? View.VISIBLE : View.GONE);
+        mHistory = originalStats;
         if (mHistory != null) {
-            // Compensate for time zone adjustments when setting the end time.
-            mOriginalSeries.setEndTime(mHistory.getEnd()
-                    - TimeZone.getDefault().getOffset(mHistory.getEnd()));
-            mCompressedSeries.setEndTime(mHistory.getEnd()
-                    - TimeZone.getDefault().getOffset(mHistory.getEnd()));
+            // Update end time to match end of data.
+            mOriginalSeries.setEndTime(mHistory.getEnd());
+            mCompressedSeries.setEndTime(mHistory.getEnd());
         }
+        // Clear any existing max range estimate.
         updateEstimateVisible();
+        // Update the primary ranges of the series.
         updatePrimaryRange();
+        // Determine the vertical max from the updated primary range for this original series.
+        updateVertAxisBounds();
         requestLayout();
     }
 
@@ -119,9 +115,9 @@
     private void updateVertAxisBounds() {
         long newMax = 0;
 
-        // always show known data and policy lines
-        final long maxSeries = Math.max(mOriginalSeries.getMaxVisible(),
-                mCompressedSeries.getMaxVisible());
+        // Determine the maximum value of the series data (which is scoped to the data to chart).
+        final long maxSeries =
+                Math.max(mOriginalSeries.getMaxStats(), mCompressedSeries.getMaxStats());
         final long maxVisible = Math.max(maxSeries, 0) * 12 / 10;
         final long maxDefault = Math.max(maxVisible, 1 * MB_IN_BYTES);
         newMax = Math.max(maxDefault, newMax);
@@ -153,40 +149,29 @@
 
     /**
      * Set the exact time range that should be displayed, updating how
-     * {@link ChartNetworkSeriesView} paints. Moves inspection ranges to be the
-     * last "week" of available data, without triggering listener events.
+     * {@link ChartNetworkSeriesView} paints. Moves inspection ranges
+     * without triggering listener events.
      */
-    public void setVisibleRange(long visibleStart, long visibleEnd, long start,
-            long end) {
-        long timeZoneOffset = TimeZone.getDefault().getOffset(end);
+    public void setVisibleRange(long visibleStart, long visibleEnd) {
         final boolean changed = mHoriz.setBounds(visibleStart, visibleEnd);
         mOriginalSeries.setBounds(visibleStart, visibleEnd);
         mCompressedSeries.setBounds(visibleStart, visibleEnd);
+        mLeft = visibleStart;
+        mRight = visibleEnd;
 
-        final long validEnd = visibleEnd;
-
-        long max = validEnd;
-        long min = Math.max(
-                visibleStart, (max - DateUtils.DAY_IN_MILLIS * DAYS_IN_CHART));
-        if (visibleEnd - DateUtils.HOUR_IN_MILLIS
-                - DateUtils.DAY_IN_MILLIS * DAYS_IN_CHART != start
-                || visibleEnd != end + timeZoneOffset) {
-            min = start;
-            max = end;
-        }
-
-        mLeft = min;
-        mRight = max;
-
-        requestLayout();
         if (changed) {
             mOriginalSeries.invalidatePath();
             mCompressedSeries.invalidatePath();
         }
 
-        updateVertAxisBounds();
+        // Clear any existing max range estimate.
         updateEstimateVisible();
+        // Update the primary range for the data series.
         updatePrimaryRange();
+        // Determine the vertical max from the updated primary range.
+        updateVertAxisBounds();
+        // Now request layout.
+        requestLayout();
     }
 
     private void updatePrimaryRange() {
@@ -215,7 +200,7 @@
 
         public TimeAxis() {
             final long currentTime = System.currentTimeMillis();
-            setBounds(currentTime - DateUtils.DAY_IN_MILLIS * DAYS_IN_CHART, currentTime);
+            setBounds(currentTime - DateUtils.DAY_IN_MILLIS * MAXIMUM_DAYS_IN_CHART, currentTime);
         }
 
         /**
diff --git a/third_party/android_data_chart/java/src/org/chromium/third_party/android/datausagechart/ChartNetworkSeriesView.java b/third_party/android_data_chart/java/src/org/chromium/third_party/android/datausagechart/ChartNetworkSeriesView.java
index 33a9e6b..50a9da6 100644
--- a/third_party/android_data_chart/java/src/org/chromium/third_party/android/datausagechart/ChartNetworkSeriesView.java
+++ b/third_party/android_data_chart/java/src/org/chromium/third_party/android/datausagechart/ChartNetworkSeriesView.java
@@ -139,6 +139,9 @@
     public void setBounds(long start, long end) {
         mStart = start;
         mEnd = end;
+        if (end > mEndTime) {
+            mEndTime = end;
+        }
     }
 
     /**
@@ -171,7 +174,7 @@
         mPathValid = true;
 
         // bail when not enough stats to render
-        if (mStats == null || mStats.size() < 2) {
+        if (mStats == null || mStats.size() < 1) {
             return;
         }
 
@@ -276,6 +279,16 @@
         }
     }
 
+    /** Returns the total network byte count in the current stats. */
+    public long getMaxStats() {
+        if (mStats == null) {
+            return 0;
+        }
+        final NetworkStatsHistory.Entry entry =
+                mStats.getValues(mStats.getStart(), mStats.getEnd(), null);
+        return entry.rxBytes + entry.txBytes;
+    }
+
     @Override
     protected void onDraw(Canvas canvas) {
         int save;