summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/com/android/settings/DataUsageSummary.java21
-rw-r--r--src/com/android/settings/widget/ChartAxis.java12
-rw-r--r--src/com/android/settings/widget/ChartNetworkSeriesView.java62
-rw-r--r--src/com/android/settings/widget/ChartSweepView.java91
-rw-r--r--src/com/android/settings/widget/DataUsageChartView.java211
-rw-r--r--src/com/android/settings/widget/InvertedChartAxis.java5
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);
+ }
}