diff options
Diffstat (limited to 'src')
6 files changed, 317 insertions, 85 deletions
diff --git a/src/com/android/settings/DataUsageSummary.java b/src/com/android/settings/DataUsageSummary.java index d87080f..8581421 100644 --- a/src/com/android/settings/DataUsageSummary.java +++ b/src/com/android/settings/DataUsageSummary.java @@ -182,6 +182,7 @@ public class DataUsageSummary extends Fragment { private DataUsageChartView mChart; private TextView mUsageSummary; + private TextView mEmpty; private View mAppDetail; private TextView mAppTitle; @@ -305,6 +306,7 @@ public class DataUsageSummary extends Fragment { } mUsageSummary = (TextView) mHeader.findViewById(R.id.usage_summary); + mEmpty = (TextView) mHeader.findViewById(android.R.id.empty); // only assign layout transitions once first layout is finished mListView.getViewTreeObserver().addOnGlobalLayoutListener(mFirstLayoutListener); @@ -986,7 +988,7 @@ public class DataUsageSummary extends Fragment { final long totalBytes = entry != null ? entry.rxBytes + entry.txBytes : 0; final String totalPhrase = Formatter.formatFileSize(context, totalBytes); - final String rangePhrase = formatDateRangeUtc(context, start, end); + final String rangePhrase = formatDateRange(context, start, end, null); mUsageSummary.setText( getString(R.string.data_usage_total_during_range, totalPhrase, rangePhrase)); @@ -1002,11 +1004,18 @@ public class DataUsageSummary extends Fragment { /** {@inheritDoc} */ public void onLoadFinished(Loader<NetworkStats> loader, NetworkStats data) { mAdapter.bindStats(data); + updateEmptyVisible(); } /** {@inheritDoc} */ public void onLoaderReset(Loader<NetworkStats> loader) { mAdapter.bindStats(null); + updateEmptyVisible(); + } + + private void updateEmptyVisible() { + final boolean isEmpty = mAdapter.isEmpty() && !isAppDetailMode(); + mEmpty.setVisibility(isEmpty ? View.VISIBLE : View.GONE); } }; @@ -1063,7 +1072,7 @@ public class DataUsageSummary extends Fragment { } public CycleItem(Context context, long start, long end) { - this.label = formatDateRangeUtc(context, start, end); + this.label = formatDateRange(context, start, end, Time.TIMEZONE_UTC); this.start = start; this.end = end; } @@ -1078,7 +1087,7 @@ public class DataUsageSummary extends Fragment { private static final java.util.Formatter sFormatter = new java.util.Formatter( sBuilder, Locale.getDefault()); - private static String formatDateRangeUtc(Context context, long start, long end) { + private static String formatDateRange(Context context, long start, long end, String timezone) { synchronized (sBuilder) { int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH; if (Time.getJulianDay(start, 0) == Time.getJulianDay(end, 0)) { @@ -1087,8 +1096,8 @@ public class DataUsageSummary extends Fragment { } sBuilder.setLength(0); - return DateUtils.formatDateRange( - context, sFormatter, start, end, flags, Time.TIMEZONE_UTC).toString(); + return DateUtils + .formatDateRange(context, sFormatter, start, end, flags, timezone).toString(); } } @@ -1197,7 +1206,7 @@ public class DataUsageSummary extends Fragment { @Override public long getItemId(int position) { - return position; + return mItems.get(position).uid; } @Override diff --git a/src/com/android/settings/widget/ChartAxis.java b/src/com/android/settings/widget/ChartAxis.java index 463541f..4e0da1d 100644 --- a/src/com/android/settings/widget/ChartAxis.java +++ b/src/com/android/settings/widget/ChartAxis.java @@ -25,14 +25,26 @@ import android.text.SpannableStringBuilder; */ public interface ChartAxis { + /** Set range of raw values this axis should cover. */ public void setBounds(long min, long max); + /** Set range of screen points this axis should cover. */ public void setSize(float size); + /** Convert raw value into screen point. */ public float convertToPoint(long value); + /** Convert screen point into raw value. */ public long convertToValue(float point); + /** Build label that describes given raw value. */ public void buildLabel(Resources res, SpannableStringBuilder builder, long value); + /** Return list of tick points for drawing a grid. */ public float[] getTickPoints(); + /** + * Test if given raw value should cause the axis to grow or shrink; + * returning positive value to grow and negative to shrink. + */ + public int shouldAdjustAxis(long value); + } diff --git a/src/com/android/settings/widget/ChartNetworkSeriesView.java b/src/com/android/settings/widget/ChartNetworkSeriesView.java index 51c3c2c..481f7cc 100644 --- a/src/com/android/settings/widget/ChartNetworkSeriesView.java +++ b/src/com/android/settings/widget/ChartNetworkSeriesView.java @@ -23,6 +23,7 @@ import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.DashPathEffect; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Path; @@ -41,7 +42,7 @@ import com.google.common.base.Preconditions; */ public class ChartNetworkSeriesView extends View { private static final String TAG = "ChartNetworkSeriesView"; - private static final boolean LOGD = true; + private static final boolean LOGD = false; private ChartAxis mHoriz; private ChartAxis mVert; @@ -60,6 +61,14 @@ public class ChartNetworkSeriesView extends View { private long mPrimaryLeft; private long mPrimaryRight; + /** Series will be extended to reach this end time. */ + private long mEndTime = Long.MIN_VALUE; + + private boolean mEstimateVisible = false; + + private long mMax; + private long mMaxEstimate; + public ChartNetworkSeriesView(Context context) { this(context, null, 0); } @@ -116,6 +125,7 @@ public class ChartNetworkSeriesView extends View { mPaintEstimate.setColor(fillSecondary); mPaintEstimate.setStyle(Style.STROKE); mPaintEstimate.setAntiAlias(true); + mPaintEstimate.setPathEffect(new DashPathEffect(new float[] { 10, 10 }, 1)); } public void bindNetworkStats(NetworkStatsHistory stats) { @@ -149,6 +159,7 @@ public class ChartNetworkSeriesView extends View { public void generatePath() { if (LOGD) Log.d(TAG, "generatePath()"); + mMax = 0; mPathStroke.reset(); mPathFill.reset(); mPathEstimate.reset(); @@ -174,8 +185,8 @@ public class ChartNetworkSeriesView extends View { for (int i = 0; i < mStats.size(); i++) { entry = mStats.getValues(i, entry); - lastTime = entry.bucketStart; - final float x = mHoriz.convertToPoint(entry.bucketStart); + lastTime = entry.bucketStart + entry.bucketDuration; + final float x = mHoriz.convertToPoint(lastTime); final float y = mVert.convertToPoint(totalData); // skip until we find first stats on screen @@ -199,6 +210,16 @@ public class ChartNetworkSeriesView extends View { lastY = y; } + // when data falls short, extend to requested end time + if (lastTime < mEndTime) { + lastX = mHoriz.convertToPoint(mEndTime); + + if (started) { + mPathStroke.lineTo(lastX, lastY); + mPathFill.lineTo(lastX, lastY); + } + } + if (LOGD) { final RectF bounds = new RectF(); mPathFill.computeBounds(bounds, true); @@ -210,6 +231,8 @@ public class ChartNetworkSeriesView extends View { mPathFill.lineTo(lastX, height); mPathFill.lineTo(firstX, height); + mMax = totalData; + // build estimated data mPathEstimate.moveTo(lastX, lastY); @@ -234,10 +257,29 @@ public class ChartNetworkSeriesView extends View { totalData += (longWindow * 7 + shortWindow * 3) / 10; lastX = mHoriz.convertToPoint(lastTime + futureTime); - final float y = mVert.convertToPoint(totalData); + lastY = mVert.convertToPoint(totalData); - mPathEstimate.lineTo(lastX, y); + mPathEstimate.lineTo(lastX, lastY); } + + mMaxEstimate = totalData; + } + + public void setEndTime(long endTime) { + mEndTime = endTime; + } + + public void setEstimateVisible(boolean estimateVisible) { + mEstimateVisible = estimateVisible; + invalidate(); + } + + public long getMaxEstimate() { + return mMaxEstimate; + } + + public long getMaxVisible() { + return mEstimateVisible ? mMaxEstimate : mMax; } @Override @@ -247,10 +289,12 @@ public class ChartNetworkSeriesView extends View { final float primaryLeftPoint = mHoriz.convertToPoint(mPrimaryLeft); final float primaryRightPoint = mHoriz.convertToPoint(mPrimaryRight); - save = canvas.save(); - canvas.clipRect(0, 0, getWidth(), getHeight()); - canvas.drawPath(mPathEstimate, mPaintEstimate); - canvas.restoreToCount(save); + if (mEstimateVisible) { + save = canvas.save(); + canvas.clipRect(0, 0, getWidth(), getHeight()); + canvas.drawPath(mPathEstimate, mPaintEstimate); + canvas.restoreToCount(save); + } save = canvas.save(); canvas.clipRect(0, 0, primaryLeftPoint, getHeight()); diff --git a/src/com/android/settings/widget/ChartSweepView.java b/src/com/android/settings/widget/ChartSweepView.java index 99c35bd..b5e044f 100644 --- a/src/com/android/settings/widget/ChartSweepView.java +++ b/src/com/android/settings/widget/ChartSweepView.java @@ -29,6 +29,7 @@ import android.text.Layout.Alignment; import android.text.SpannableStringBuilder; import android.text.TextPaint; import android.util.AttributeSet; +import android.util.Log; import android.util.MathUtils; import android.view.MotionEvent; import android.view.View; @@ -48,6 +49,7 @@ public class ChartSweepView extends FrameLayout { private Point mSweepOffset = new Point(); private Rect mMargins = new Rect(); + private float mNeighborMargin; private int mFollowAxis; @@ -65,7 +67,6 @@ public class ChartSweepView extends FrameLayout { private long mValidBefore; private ChartSweepView mValidAfterDynamic; private ChartSweepView mValidBeforeDynamic; - private long mValidBufferArea; public static final int HORIZONTAL = 0; public static final int VERTICAL = 1; @@ -93,6 +94,7 @@ public class ChartSweepView extends FrameLayout { setSweepDrawable(a.getDrawable(R.styleable.ChartSweepView_sweepDrawable)); setFollowAxis(a.getInt(R.styleable.ChartSweepView_followAxis, -1)); + setNeighborMargin(a.getDimensionPixelSize(R.styleable.ChartSweepView_neighborMargin, 0)); setLabelSize(a.getDimensionPixelSize(R.styleable.ChartSweepView_labelSize, 0)); setLabelTemplate(a.getResourceId(R.styleable.ChartSweepView_labelTemplate, 0)); @@ -271,16 +273,18 @@ public class ChartSweepView extends FrameLayout { mValidBefore = validBefore; } + public void setNeighborMargin(float neighborMargin) { + mNeighborMargin = neighborMargin; + } + /** * Set valid range this sweep can move within, defined by the given * {@link ChartSweepView}. The most restrictive combination of all valid * ranges is used. */ - public void setValidRangeDynamic( - ChartSweepView validAfter, ChartSweepView validBefore, long bufferArea) { + public void setValidRangeDynamic(ChartSweepView validAfter, ChartSweepView validBefore) { mValidAfterDynamic = validAfter; mValidBeforeDynamic = validBefore; - mValidBufferArea = bufferArea; } @Override @@ -316,9 +320,7 @@ public class ChartSweepView extends FrameLayout { getParent().requestDisallowInterceptTouchEvent(true); // content area of parent - final Rect parentContent = new Rect(parent.getPaddingLeft(), parent.getPaddingTop(), - parent.getWidth() - parent.getPaddingRight(), - parent.getHeight() - parent.getPaddingBottom()); + final Rect parentContent = getParentContentRect(); final Rect clampRect = computeClampRect(parentContent); if (mFollowAxis == VERTICAL) { @@ -358,6 +360,33 @@ public class ChartSweepView extends FrameLayout { } } + /** + * Update {@link #mValue} based on current position, including any + * {@link #onTouchEvent(MotionEvent)} in progress. Typically used when + * {@link ChartAxis} changes during sweep adjustment. + */ + public void updateValueFromPosition() { + final Rect parentContent = getParentContentRect(); + if (mFollowAxis == VERTICAL) { + final float effectiveY = getY() - mMargins.top - parentContent.top; + setValue(mAxis.convertToValue(effectiveY)); + } else { + final float effectiveX = getX() - mMargins.left - parentContent.left; + setValue(mAxis.convertToValue(effectiveX)); + } + } + + public int shouldAdjustAxis() { + return mAxis.shouldAdjustAxis(getValue()); + } + + private Rect getParentContentRect() { + final View parent = (View) getParent(); + return new Rect(parent.getPaddingLeft(), parent.getPaddingTop(), + parent.getWidth() - parent.getPaddingRight(), + parent.getHeight() - parent.getPaddingBottom()); + } + @Override public void addOnLayoutChangeListener(OnLayoutChangeListener listener) { // ignored to keep LayoutTransition from animating us @@ -368,18 +397,14 @@ public class ChartSweepView extends FrameLayout { // ignored to keep LayoutTransition from animating us } - private long getValidAfterValue() { + private long getValidAfterDynamic() { final ChartSweepView dynamic = mValidAfterDynamic; - final boolean dynamicEnabled = dynamic != null && dynamic.isEnabled(); - return Math.max(mValidAfter, - dynamicEnabled ? dynamic.getValue() + mValidBufferArea : Long.MIN_VALUE); + return dynamic != null && dynamic.isEnabled() ? dynamic.getValue() : Long.MIN_VALUE; } - private long getValidBeforeValue() { + private long getValidBeforeDynamic() { final ChartSweepView dynamic = mValidBeforeDynamic; - final boolean dynamicEnabled = dynamic != null && dynamic.isEnabled(); - return Math.min(mValidBefore, - dynamicEnabled ? dynamic.getValue() - mValidBufferArea : Long.MAX_VALUE); + return dynamic != null && dynamic.isEnabled() ? dynamic.getValue() : Long.MAX_VALUE; } /** @@ -388,22 +413,36 @@ public class ChartSweepView extends FrameLayout { * style rules. */ private Rect computeClampRect(Rect parentContent) { - final Rect clampRect = new Rect(parentContent); + // create two rectangles, and pick most restrictive combination + final Rect rect = buildClampRect(parentContent, mValidAfter, mValidBefore, 0f); + final Rect dynamicRect = buildClampRect( + parentContent, getValidAfterDynamic(), getValidBeforeDynamic(), mNeighborMargin); - float validAfterPoint = mAxis.convertToPoint(getValidAfterValue()); - float validBeforePoint = mAxis.convertToPoint(getValidBeforeValue()); - if (validAfterPoint > validBeforePoint) { - float swap = validBeforePoint; - validBeforePoint = validAfterPoint; - validAfterPoint = swap; + rect.intersect(dynamicRect); + return rect; + } + + private Rect buildClampRect( + Rect parentContent, long afterValue, long beforeValue, float margin) { + if (mAxis instanceof InvertedChartAxis) { + long temp = beforeValue; + beforeValue = afterValue; + afterValue = temp; } + final boolean afterValid = afterValue != Long.MIN_VALUE && afterValue != Long.MAX_VALUE; + final boolean beforeValid = beforeValue != Long.MIN_VALUE && beforeValue != Long.MAX_VALUE; + + final float afterPoint = mAxis.convertToPoint(afterValue) + margin; + final float beforePoint = mAxis.convertToPoint(beforeValue) - margin; + + final Rect clampRect = new Rect(parentContent); if (mFollowAxis == VERTICAL) { - clampRect.bottom = clampRect.top + (int) validBeforePoint; - clampRect.top += validAfterPoint; + if (beforeValid) clampRect.bottom = clampRect.top + (int) beforePoint; + if (afterValid) clampRect.top += afterPoint; } else { - clampRect.right = clampRect.left + (int) validBeforePoint; - clampRect.left += validAfterPoint; + if (beforeValid) clampRect.right = clampRect.left + (int) beforePoint; + if (afterValid) clampRect.left += afterPoint; } return clampRect; } diff --git a/src/com/android/settings/widget/DataUsageChartView.java b/src/com/android/settings/widget/DataUsageChartView.java index 839171e..b2ad844 100644 --- a/src/com/android/settings/widget/DataUsageChartView.java +++ b/src/com/android/settings/widget/DataUsageChartView.java @@ -16,12 +16,12 @@ package com.android.settings.widget; -import static android.text.format.DateUtils.HOUR_IN_MILLIS; - import android.content.Context; import android.content.res.Resources; import android.net.NetworkPolicy; import android.net.NetworkStatsHistory; +import android.os.Handler; +import android.os.Message; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.TextUtils; @@ -43,7 +43,8 @@ public class DataUsageChartView extends ChartView { private static final long MB_IN_BYTES = KB_IN_BYTES * 1024; private static final long GB_IN_BYTES = MB_IN_BYTES * 1024; - // TODO: enforce that sweeps cant cross each other + private static final int MSG_UPDATE_AXIS = 100; + private static final long DELAY_MILLIS = 250; private ChartGridView mGrid; private ChartNetworkSeriesView mSeries; @@ -56,6 +57,11 @@ public class DataUsageChartView extends ChartView { private ChartSweepView mSweepWarning; private ChartSweepView mSweepLimit; + private Handler mHandler; + + /** Current maximum value of {@link #mVert}. */ + private long mVertMax; + public interface DataUsageChartListener { public void onInspectRangeChanged(); public void onWarningChanged(); @@ -75,6 +81,18 @@ public class DataUsageChartView extends ChartView { public DataUsageChartView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(new TimeAxis(), new InvertedChartAxis(new DataAxis())); + + mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + final ChartSweepView sweep = (ChartSweepView) msg.obj; + updateVertAxisBounds(sweep); + updateEstimateVisible(); + + // we keep dispatching repeating updates until sweep is dropped + sendUpdateAxisDelayed(sweep, true); + } + }; } @Override @@ -92,19 +110,15 @@ public class DataUsageChartView extends ChartView { mSweepWarning = (ChartSweepView) findViewById(R.id.sweep_warning); // prevent sweeps from crossing each other - mSweepLeft.setValidRangeDynamic(null, mSweepRight, HOUR_IN_MILLIS); - mSweepRight.setValidRangeDynamic(mSweepLeft, null, HOUR_IN_MILLIS); - - // TODO: assign these ranges as user changes data axis - mSweepWarning.setValidRange(0L, 5 * GB_IN_BYTES); - mSweepWarning.setValidRangeDynamic(null, mSweepLimit, MB_IN_BYTES); - mSweepLimit.setValidRange(0L, 5 * GB_IN_BYTES); - mSweepLimit.setValidRangeDynamic(mSweepWarning, null, MB_IN_BYTES); + mSweepLeft.setValidRangeDynamic(null, mSweepRight); + mSweepRight.setValidRangeDynamic(mSweepLeft, null); + mSweepWarning.setValidRangeDynamic(null, mSweepLimit); + mSweepLimit.setValidRangeDynamic(mSweepWarning, null); - mSweepLeft.addOnSweepListener(mSweepListener); - mSweepRight.addOnSweepListener(mSweepListener); - mSweepWarning.addOnSweepListener(mWarningListener); - mSweepLimit.addOnSweepListener(mLimitListener); + mSweepLeft.addOnSweepListener(mHorizListener); + mSweepRight.addOnSweepListener(mHorizListener); + mSweepWarning.addOnSweepListener(mVertListener); + mSweepLimit.addOnSweepListener(mVertListener); // tell everyone about our axis mGrid.init(mHoriz, mVert); @@ -125,6 +139,8 @@ public class DataUsageChartView extends ChartView { public void bindNetworkStats(NetworkStatsHistory stats) { mSeries.bindNetworkStats(stats); mHistory = stats; + updateVertAxisBounds(null); + updateEstimateVisible(); updatePrimaryRange(); requestLayout(); } @@ -132,6 +148,11 @@ public class DataUsageChartView extends ChartView { public void bindDetailNetworkStats(NetworkStatsHistory stats) { mDetailSeries.bindNetworkStats(stats); mDetailSeries.setVisibility(stats != null ? View.VISIBLE : View.GONE); + if (mHistory != null) { + mDetailSeries.setEndTime(mHistory.getEnd()); + } + updateVertAxisBounds(null); + updateEstimateVisible(); updatePrimaryRange(); requestLayout(); } @@ -139,17 +160,20 @@ public class DataUsageChartView extends ChartView { public void bindNetworkPolicy(NetworkPolicy policy) { if (policy == null) { mSweepLimit.setVisibility(View.INVISIBLE); + mSweepLimit.setValue(-1); mSweepWarning.setVisibility(View.INVISIBLE); + mSweepWarning.setValue(-1); return; } if (policy.limitBytes != NetworkPolicy.LIMIT_DISABLED) { mSweepLimit.setVisibility(View.VISIBLE); - mSweepLimit.setValue(policy.limitBytes); mSweepLimit.setEnabled(true); + mSweepLimit.setValue(policy.limitBytes); } else { mSweepLimit.setVisibility(View.VISIBLE); mSweepLimit.setEnabled(false); + mSweepLimit.setValue(-1); } if (policy.warningBytes != NetworkPolicy.WARNING_DISABLED) { @@ -157,12 +181,81 @@ public class DataUsageChartView extends ChartView { mSweepWarning.setValue(policy.warningBytes); } else { mSweepWarning.setVisibility(View.INVISIBLE); + mSweepWarning.setValue(-1); } + updateVertAxisBounds(null); requestLayout(); } - private OnSweepListener mSweepListener = new OnSweepListener() { + /** + * Update {@link #mVert} to both show data from {@link NetworkStatsHistory} + * and controls from {@link NetworkPolicy}. + */ + private void updateVertAxisBounds(ChartSweepView activeSweep) { + final long max = mVertMax; + final long newMax; + if (activeSweep != null) { + final int adjustAxis = activeSweep.shouldAdjustAxis(); + if (adjustAxis > 0) { + // hovering around upper edge, grow axis + newMax = max * 11 / 10; + } else if (adjustAxis < 0) { + // hovering around lower edge, shrink axis + newMax = max * 9 / 10; + } else { + newMax = max; + } + + } else { + // try showing all known data and policy + final long maxSweep = Math.max(mSweepWarning.getValue(), mSweepLimit.getValue()); + final long maxVisible = Math.max(mSeries.getMaxVisible(), maxSweep) * 12 / 10; + newMax = Math.max(maxVisible, 2 * GB_IN_BYTES); + } + + // only invalidate when vertMax actually changed + if (newMax != mVertMax) { + mVertMax = newMax; + + mVert.setBounds(0L, newMax); + mSweepWarning.setValidRange(0L, newMax); + mSweepLimit.setValidRange(0L, newMax); + + mSeries.generatePath(); + mDetailSeries.generatePath(); + + mGrid.invalidate(); + mSeries.invalidate(); + mDetailSeries.invalidate(); + + // since we just changed axis, make sweep recalculate its value + if (activeSweep != null) { + activeSweep.updateValueFromPosition(); + } + } + } + + /** + * Control {@link ChartNetworkSeriesView#setEstimateVisible(boolean)} based + * on how close estimate comes to {@link #mSweepWarning}. + */ + private void updateEstimateVisible() { + final long maxEstimate = mSeries.getMaxEstimate(); + + // show estimate when near warning/limit + long interestLine = Long.MAX_VALUE; + if (mSweepWarning.isEnabled()) { + interestLine = mSweepWarning.getValue(); + } else if (mSweepLimit.isEnabled()) { + interestLine = mSweepLimit.getValue(); + } + + final boolean estimateVisible = (maxEstimate >= interestLine * 7 / 10); + mSeries.setEstimateVisible(estimateVisible); + } + + private OnSweepListener mHorizListener = new OnSweepListener() { public void onSweep(ChartSweepView sweep, boolean sweepDone) { updatePrimaryRange(); @@ -173,18 +266,31 @@ public class DataUsageChartView extends ChartView { } }; - private OnSweepListener mWarningListener = new OnSweepListener() { - public void onSweep(ChartSweepView sweep, boolean sweepDone) { - if (sweepDone && mListener != null) { - mListener.onWarningChanged(); - } + private void sendUpdateAxisDelayed(ChartSweepView sweep, boolean force) { + if (force || !mHandler.hasMessages(MSG_UPDATE_AXIS, sweep)) { + mHandler.sendMessageDelayed( + mHandler.obtainMessage(MSG_UPDATE_AXIS, sweep), DELAY_MILLIS); } - }; + } + + private void clearUpdateAxisDelayed(ChartSweepView sweep) { + mHandler.removeMessages(MSG_UPDATE_AXIS, sweep); + } - private OnSweepListener mLimitListener = new OnSweepListener() { + private OnSweepListener mVertListener = new OnSweepListener() { public void onSweep(ChartSweepView sweep, boolean sweepDone) { - if (sweepDone && mListener != null) { - mListener.onLimitChanged(); + if (sweepDone) { + clearUpdateAxisDelayed(sweep); + updateEstimateVisible(); + + if (sweep == mSweepWarning && mListener != null) { + mListener.onWarningChanged(); + } else if (sweep == mSweepLimit && mListener != null) { + mListener.onLimitChanged(); + } + } else { + // while moving, kick off delayed grow/shrink axis updates + sendUpdateAxisDelayed(sweep, false); } } }; @@ -252,11 +358,14 @@ public class DataUsageChartView extends ChartView { mSweepLeft.setValue(sweepMin); mSweepRight.setValue(sweepMax); - updatePrimaryRange(); requestLayout(); mSeries.generatePath(); mSeries.invalidate(); + + updateVertAxisBounds(null); + updateEstimateVisible(); + updatePrimaryRange(); } private void updatePrimaryRange() { @@ -321,6 +430,12 @@ public class DataUsageChartView extends ChartView { } return tickPoints; } + + /** {@inheritDoc} */ + public int shouldAdjustAxis(long value) { + // time axis never adjusts + return 0; + } } public static class DataAxis implements ChartAxis { @@ -328,12 +443,6 @@ public class DataUsageChartView extends ChartView { private long mMax; private float mSize; - public DataAxis() { - // TODO: adapt ranges to show when history >5GB, and handle 4G - // interfaces with higher limits. - setBounds(0, 5 * GB_IN_BYTES); - } - /** {@inheritDoc} */ public void setBounds(long min, long max) { mMin = min; @@ -347,19 +456,19 @@ public class DataUsageChartView extends ChartView { /** {@inheritDoc} */ public float convertToPoint(long value) { - // TODO: this assumes range of [0,5]GB + // derived polynomial fit to make lower values more visible + final double normalized = ((double) value - mMin) / (mMax - mMin); final double fraction = Math.pow( - 10, 0.36884343106175160321 * Math.log10(value) + -3.62828151137812282556); - return (float) fraction * mSize; + 10, 0.36884343106175121463 * Math.log10(normalized) + -0.04328199452018252624); + return (float) (fraction * mSize); } /** {@inheritDoc} */ public long convertToValue(float point) { - final double y = point / mSize; - // TODO: this assumes range of [0,5]GB - final double fraction = 6.869341163271789302 * Math.pow(10, 9) - * Math.pow(y, 2.71117746931646030774); - return (long) fraction; + final double normalized = point / mSize; + final double fraction = 1.3102228476089056629 + * Math.pow(normalized, 2.7111774693164631640); + return (long) (mMin + (fraction * (mMax - mMin))); } private static final Object sSpanSize = new Object(); @@ -393,17 +502,31 @@ public class DataUsageChartView extends ChartView { /** {@inheritDoc} */ public float[] getTickPoints() { - final float[] tickPoints = new float[16]; + final long range = mMax - mMin; + final long tickJump = 256 * MB_IN_BYTES; - final long jump = ((mMax - mMin) / tickPoints.length); + final int tickCount = (int) (range / tickJump); + final float[] tickPoints = new float[tickCount]; long value = mMin; for (int i = 0; i < tickPoints.length; i++) { tickPoints[i] = convertToPoint(value); - value += jump; + value += tickJump; } return tickPoints; } + + /** {@inheritDoc} */ + public int shouldAdjustAxis(long value) { + final float point = convertToPoint(value); + if (point < mSize * 0.1) { + return -1; + } else if (point > mSize * 0.85) { + return 1; + } else { + return 0; + } + } } private static int[] findOrCreateSpan( diff --git a/src/com/android/settings/widget/InvertedChartAxis.java b/src/com/android/settings/widget/InvertedChartAxis.java index e589da9..96aec7b 100644 --- a/src/com/android/settings/widget/InvertedChartAxis.java +++ b/src/com/android/settings/widget/InvertedChartAxis.java @@ -64,4 +64,9 @@ public class InvertedChartAxis implements ChartAxis { } return points; } + + /** {@inheritDoc} */ + public int shouldAdjustAxis(long value) { + return mWrapped.shouldAdjustAxis(value); + } } |