diff options
Diffstat (limited to 'src/com/android/settings/widget')
-rw-r--r-- | src/com/android/settings/widget/ChartDataUsageView.java (renamed from src/com/android/settings/widget/DataUsageChartView.java) | 59 | ||||
-rw-r--r-- | src/com/android/settings/widget/ChartSweepView.java | 112 | ||||
-rw-r--r-- | src/com/android/settings/widget/ChartView.java | 54 |
3 files changed, 181 insertions, 44 deletions
diff --git a/src/com/android/settings/widget/DataUsageChartView.java b/src/com/android/settings/widget/ChartDataUsageView.java index f6ae5a0..9554368 100644 --- a/src/com/android/settings/widget/DataUsageChartView.java +++ b/src/com/android/settings/widget/ChartDataUsageView.java @@ -27,6 +27,7 @@ import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.AttributeSet; +import android.view.Gravity; import android.view.MotionEvent; import android.view.View; @@ -37,7 +38,7 @@ import com.android.settings.widget.ChartSweepView.OnSweepListener; * Specific {@link ChartView} that displays {@link ChartNetworkSeriesView} along * with {@link ChartSweepView} for inspection ranges and warning/limits. */ -public class DataUsageChartView extends ChartView { +public class ChartDataUsageView extends ChartView { private static final long KB_IN_BYTES = 1024; private static final long MB_IN_BYTES = KB_IN_BYTES * 1024; @@ -46,6 +47,8 @@ public class DataUsageChartView extends ChartView { private static final int MSG_UPDATE_AXIS = 100; private static final long DELAY_MILLIS = 250; + private static final boolean LIMIT_SWEEPS_TO_VALID_DATA = false; + private ChartGridView mGrid; private ChartNetworkSeriesView mSeries; private ChartNetworkSeriesView mDetailSeries; @@ -70,15 +73,15 @@ public class DataUsageChartView extends ChartView { private DataUsageChartListener mListener; - public DataUsageChartView(Context context) { + public ChartDataUsageView(Context context) { this(context, null, 0); } - public DataUsageChartView(Context context, AttributeSet attrs) { + public ChartDataUsageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } - public DataUsageChartView(Context context, AttributeSet attrs, int defStyle) { + public ChartDataUsageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(new TimeAxis(), new InvertedChartAxis(new DataAxis())); @@ -186,6 +189,7 @@ public class DataUsageChartView extends ChartView { updateVertAxisBounds(null); requestLayout(); + invalidate(); } /** @@ -194,7 +198,8 @@ public class DataUsageChartView extends ChartView { */ private void updateVertAxisBounds(ChartSweepView activeSweep) { final long max = mVertMax; - final long newMax; + + long newMax = 0; if (activeSweep != null) { final int adjustAxis = activeSweep.shouldAdjustAxis(); if (adjustAxis > 0) { @@ -206,14 +211,14 @@ public class DataUsageChartView extends ChartView { } 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); } + // always show known data and policy lines + final long maxSweep = Math.max(mSweepWarning.getValue(), mSweepLimit.getValue()); + final long maxVisible = Math.max(mSeries.getMaxVisible(), maxSweep) * 12 / 10; + final long maxDefault = Math.max(maxVisible, 2 * GB_IN_BYTES); + newMax = Math.max(maxDefault, newMax); + // only invalidate when vertMax actually changed if (newMax != mVertMax) { mVertMax = newMax; @@ -231,6 +236,16 @@ public class DataUsageChartView extends ChartView { if (activeSweep != null) { activeSweep.updateValueFromPosition(); } + + // layout other sweeps to match changed axis + // TODO: find cleaner way of doing this, such as requesting full + // layout and making activeSweep discard its tracking MotionEvent. + if (mSweepLimit != activeSweep) { + layoutSweep(mSweepLimit); + } + if (mSweepWarning != activeSweep) { + layoutSweep(mSweepWarning); + } } } @@ -346,9 +361,14 @@ public class DataUsageChartView extends ChartView { final long validStart = Math.max(visibleStart, getStatsStart()); final long validEnd = Math.min(visibleEnd, getStatsEnd()); - // prevent time sweeps from leaving valid data - mSweepLeft.setValidRange(validStart, validEnd); - mSweepRight.setValidRange(validStart, validEnd); + if (LIMIT_SWEEPS_TO_VALID_DATA) { + // prevent time sweeps from leaving valid data + mSweepLeft.setValidRange(validStart, validEnd); + mSweepRight.setValidRange(validStart, validEnd); + } else { + mSweepLeft.setValidRange(visibleStart, visibleEnd); + mSweepRight.setValidRange(visibleStart, visibleEnd); + } // default sweeps to last week of data final long halfRange = (visibleEnd + visibleStart) / 2; @@ -424,7 +444,7 @@ public class DataUsageChartView extends ChartView { final int tickCount = (int) ((mMax - mMin) / TICK_INTERVAL); final float[] tickPoints = new float[tickCount]; for (int i = 0; i < tickCount; i++) { - tickPoints[i] = convertToPoint(mMax - (TICK_INTERVAL * i)); + tickPoints[i] = convertToPoint(mMax - (TICK_INTERVAL * (i + 1))); } return tickPoints; } @@ -501,7 +521,14 @@ public class DataUsageChartView extends ChartView { /** {@inheritDoc} */ public float[] getTickPoints() { final long range = mMax - mMin; - final long tickJump = 256 * MB_IN_BYTES; + final long tickJump; + if (range < 6 * GB_IN_BYTES) { + tickJump = 256 * MB_IN_BYTES; + } else if (range < 12 * GB_IN_BYTES) { + tickJump = 512 * MB_IN_BYTES; + } else { + tickJump = 1 * GB_IN_BYTES; + } final int tickCount = (int) (range / tickJump); final float[] tickPoints = new float[tickCount]; diff --git a/src/com/android/settings/widget/ChartSweepView.java b/src/com/android/settings/widget/ChartSweepView.java index 81aeb84..0d91a76 100644 --- a/src/com/android/settings/widget/ChartSweepView.java +++ b/src/com/android/settings/widget/ChartSweepView.java @@ -21,6 +21,7 @@ import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.graphics.Paint.Style; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -42,8 +43,14 @@ import com.google.common.base.Preconditions; */ public class ChartSweepView extends View { + private static final boolean DRAW_OUTLINE = false; + private Drawable mSweep; private Rect mSweepPadding = new Rect(); + + /** Offset of content inside this view. */ + private Point mContentOffset = new Point(); + /** Offset of {@link #mSweep} inside this view. */ private Point mSweepOffset = new Point(); private Rect mMargins = new Rect(); @@ -66,6 +73,8 @@ public class ChartSweepView extends View { private ChartSweepView mValidAfterDynamic; private ChartSweepView mValidBeforeDynamic; + private Paint mOutlinePaint = new Paint(); + public static final int HORIZONTAL = 0; public static final int VERTICAL = 1; @@ -98,6 +107,10 @@ public class ChartSweepView extends View { setLabelTemplate(a.getResourceId(R.styleable.ChartSweepView_labelTemplate, 0)); setLabelColor(a.getColor(R.styleable.ChartSweepView_labelColor, Color.BLUE)); + mOutlinePaint.setColor(Color.RED); + mOutlinePaint.setStrokeWidth(1f); + mOutlinePaint.setStyle(Style.STROKE); + a.recycle(); setWillNotDraw(false); @@ -123,11 +136,11 @@ public class ChartSweepView extends View { if (mFollowAxis == VERTICAL) { final float targetHeight = mSweep.getIntrinsicHeight() - mSweepPadding.top - mSweepPadding.bottom; - return mSweepPadding.top + (targetHeight / 2); + return mSweepPadding.top + (targetHeight / 2) + mSweepOffset.y; } else { final float targetWidth = mSweep.getIntrinsicWidth() - mSweepPadding.left - mSweepPadding.right; - return mSweepPadding.left + (targetWidth / 2); + return mSweepPadding.left + (targetWidth / 2) + mSweepOffset.x; } } @@ -195,6 +208,7 @@ public class ChartSweepView extends View { paint.density = getResources().getDisplayMetrics().density; paint.setCompatibilityScaling(getResources().getCompatibilityInfo().applicationScale); paint.setColor(mLabelColor); + paint.setShadowLayer(4 * paint.density, 0, 0, Color.BLACK); mLabelTemplate = new SpannableStringBuilder(template); mLabelLayout = new DynamicLayout( @@ -283,6 +297,26 @@ public class ChartSweepView extends View { mValidBeforeDynamic = validBefore; } + /** + * Test if given {@link MotionEvent} is closer to another + * {@link ChartSweepView} compared to ourselves. + */ + public boolean isTouchCloserTo(MotionEvent eventInParent, ChartSweepView another) { + if (another == null) return false; + + if (mFollowAxis == HORIZONTAL) { + final float selfDist = Math.abs(eventInParent.getX() - (getX() + getTargetInset())); + final float anotherDist = Math.abs( + eventInParent.getX() - (another.getX() + another.getTargetInset())); + return anotherDist < selfDist; + } else { + final float selfDist = Math.abs(eventInParent.getY() - (getY() + getTargetInset())); + final float anotherDist = Math.abs( + eventInParent.getY() - (another.getY() + another.getTargetInset())); + return anotherDist < selfDist; + } + } + @Override public boolean onTouchEvent(MotionEvent event) { if (!isEnabled()) return false; @@ -294,9 +328,18 @@ public class ChartSweepView extends View { // only start tracking when in sweet spot final boolean accept; if (mFollowAxis == VERTICAL) { - accept = event.getX() > getWidth() - (mSweepPadding.right * 2); + accept = event.getX() > getWidth() - (mSweepPadding.right * 3); } else { - accept = event.getY() > getHeight() - (mSweepPadding.bottom * 2); + accept = event.getY() > getHeight() - (mSweepPadding.bottom * 3); + } + + final MotionEvent eventInParent = event.copy(); + eventInParent.offsetLocation(getLeft(), getTop()); + + // ignore event when closer to a neighbor + if (isTouchCloserTo(eventInParent, mValidAfterDynamic) + || isTouchCloserTo(eventInParent, mValidBeforeDynamic)) { + return false; } if (accept) { @@ -460,6 +503,7 @@ public class ChartSweepView extends View { final int templateHeight = mLabelLayout.getHeight(); mSweepOffset.x = 0; + mSweepOffset.y = 0; mSweepOffset.y = (int) ((templateHeight / 2) - getTargetInset()); setMeasuredDimension(mSweep.getIntrinsicWidth(), Math.max(sweepHeight, templateHeight)); @@ -485,6 +529,23 @@ public class ChartSweepView extends View { mMargins.bottom = mSweepPadding.bottom; } + mContentOffset.x = 0; + mContentOffset.y = 0; + + // make touch target area larger + if (mFollowAxis == HORIZONTAL) { + final int widthBefore = getMeasuredWidth(); + final int widthAfter = widthBefore * 3; + setMeasuredDimension(widthAfter, getMeasuredHeight()); + mContentOffset.offset((widthAfter - widthBefore) / 2, 0); + } else { + final int heightBefore = getMeasuredHeight(); + final int heightAfter = heightBefore * 3; + setMeasuredDimension(getMeasuredWidth(), heightAfter); + mContentOffset.offset(0, (heightAfter - heightBefore) / 2); + } + + mSweepOffset.offset(mContentOffset.x, mContentOffset.y); mMargins.offset(-mSweepOffset.x, -mSweepOffset.y); } @@ -493,9 +554,43 @@ public class ChartSweepView extends View { final int width = getWidth(); final int height = getHeight(); + if (DRAW_OUTLINE) { + canvas.drawRect(0, 0, width, height, mOutlinePaint); + } + + // when overlapping with neighbor, split difference and push label + float margin; + float labelOffset = 0; + if (mFollowAxis == VERTICAL) { + if (mValidAfterDynamic != null) { + margin = getLabelTop(mValidAfterDynamic) - getLabelBottom(this); + if (margin < 0) { + labelOffset = margin / 2; + } + } else if (mValidBeforeDynamic != null) { + margin = getLabelTop(this) - getLabelBottom(mValidBeforeDynamic); + if (margin < 0) { + labelOffset = -margin / 2; + } + } + } else { + // TODO: implement horizontal labels + } + + // when offsetting label, neighbor probably needs to offset too + if (labelOffset != 0) { + if (mValidAfterDynamic != null) mValidAfterDynamic.invalidate(); + if (mValidBeforeDynamic != null) mValidBeforeDynamic.invalidate(); + } + final int labelSize; if (isEnabled() && mLabelLayout != null) { - mLabelLayout.draw(canvas); + final int count = canvas.save(); + { + canvas.translate(mContentOffset.x, mContentOffset.y + labelOffset); + mLabelLayout.draw(canvas); + } + canvas.restoreToCount(count); labelSize = mLabelSize; } else { labelSize = 0; @@ -512,4 +607,11 @@ public class ChartSweepView extends View { mSweep.draw(canvas); } + public static float getLabelTop(ChartSweepView view) { + return view.getY() + view.mContentOffset.y; + } + + public static float getLabelBottom(ChartSweepView view) { + return getLabelTop(view) + view.mLabelLayout.getHeight(); + } } diff --git a/src/com/android/settings/widget/ChartView.java b/src/com/android/settings/widget/ChartView.java index e3a658a..f410d57 100644 --- a/src/com/android/settings/widget/ChartView.java +++ b/src/com/android/settings/widget/ChartView.java @@ -36,8 +36,6 @@ import com.android.settings.R; * and screen coordinates. */ public class ChartView extends FrameLayout { - private static final String TAG = "ChartView"; - // TODO: extend something that supports two-dimensional scrolling private static final int SWEEP_GRAVITY = Gravity.TOP | Gravity.LEFT; @@ -122,29 +120,39 @@ public class ChartView extends FrameLayout { child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom); } else if (child instanceof ChartSweepView) { - // sweep is always placed along specific dimension - final ChartSweepView sweep = (ChartSweepView) child; - final Rect sweepMargins = sweep.getMargins(); - - if (sweep.getFollowAxis() == ChartSweepView.VERTICAL) { - parentRect.top += sweepMargins.top + (int) sweep.getPoint(); - parentRect.bottom = parentRect.top; - parentRect.left += sweepMargins.left; - parentRect.right += sweepMargins.right; - Gravity.apply(SWEEP_GRAVITY, parentRect.width(), child.getMeasuredHeight(), - parentRect, childRect); - - } else { - parentRect.left += sweepMargins.left + (int) sweep.getPoint(); - parentRect.right = parentRect.left; - parentRect.top += sweepMargins.top; - parentRect.bottom += sweepMargins.bottom; - Gravity.apply(SWEEP_GRAVITY, child.getMeasuredWidth(), parentRect.height(), - parentRect, childRect); - } + layoutSweep((ChartSweepView) child, parentRect, childRect); + child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom); } + } + } + + protected void layoutSweep(ChartSweepView sweep) { + final Rect parentRect = new Rect(mContent); + final Rect childRect = new Rect(); + + layoutSweep(sweep, parentRect, childRect); + sweep.layout(childRect.left, childRect.top, childRect.right, childRect.bottom); + } - child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom); + protected void layoutSweep(ChartSweepView sweep, Rect parentRect, Rect childRect) { + final Rect sweepMargins = sweep.getMargins(); + + // sweep is always placed along specific dimension + if (sweep.getFollowAxis() == ChartSweepView.VERTICAL) { + parentRect.top += sweepMargins.top + (int) sweep.getPoint(); + parentRect.bottom = parentRect.top; + parentRect.left += sweepMargins.left; + parentRect.right += sweepMargins.right; + Gravity.apply(SWEEP_GRAVITY, parentRect.width(), sweep.getMeasuredHeight(), + parentRect, childRect); + + } else { + parentRect.left += sweepMargins.left + (int) sweep.getPoint(); + parentRect.right = parentRect.left; + parentRect.top += sweepMargins.top; + parentRect.bottom += sweepMargins.bottom; + Gravity.apply(SWEEP_GRAVITY, sweep.getMeasuredWidth(), parentRect.height(), + parentRect, childRect); } } |