diff options
author | Jeff Sharkey <jsharkey@android.com> | 2011-08-01 15:29:30 -0700 |
---|---|---|
committer | Jeff Sharkey <jsharkey@android.com> | 2011-08-01 16:36:38 -0700 |
commit | e2afc0f283f58ce60c107643978bfff25ec5d5c1 (patch) | |
tree | 4a5abc2ce9a3483e9ced7401c89b2ab7fbf32a9f /src/com/android | |
parent | a861ebffe0dc6eacc83343f97044da4c0370c1b2 (diff) | |
download | packages_apps_settings-e2afc0f283f58ce60c107643978bfff25ec5d5c1.zip packages_apps_settings-e2afc0f283f58ce60c107643978bfff25ec5d5c1.tar.gz packages_apps_settings-e2afc0f283f58ce60c107643978bfff25ec5d5c1.tar.bz2 |
Data usage axis grow/shrink, other fixes.
When dragging vertical sweeps near edges, grow or shrink axis scale
to give users access to larger limits. Triggers 10% for each 250ms
that user continues holding. Change axis math to support arbitrary
ranges beyond [0,5GB].
Show "empty" message when no application details found. Added strings
that didn't appear in default language. Better sweep margins using
dip instead of scale units. Format time ranges in local time instead
of UTC. Only show dashed estimate when it would reach near warning
or limit. Extend app usage series until "now" when buckets missing.
Bug: 5096685, 5092538, 5058158, 5058114, 5058024, 4643457
Change-Id: I45cf33f7f3baeba1bfa5b21f31cb0a12006f62fa
Diffstat (limited to 'src/com/android')
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); + } } |