diff options
Diffstat (limited to 'src/com/android/settings/widget')
6 files changed, 318 insertions, 8 deletions
diff --git a/src/com/android/settings/widget/ChartAxis.java b/src/com/android/settings/widget/ChartAxis.java index 0b77ac6..2b21d28 100644 --- a/src/com/android/settings/widget/ChartAxis.java +++ b/src/com/android/settings/widget/ChartAxis.java @@ -22,6 +22,7 @@ package com.android.settings.widget; */ public interface ChartAxis { + public void setBounds(long min, long max); public void setSize(float size); public float convertToPoint(long value); diff --git a/src/com/android/settings/widget/ChartNetworkSeriesView.java b/src/com/android/settings/widget/ChartNetworkSeriesView.java index 1008761..d0a2742 100644 --- a/src/com/android/settings/widget/ChartNetworkSeriesView.java +++ b/src/com/android/settings/widget/ChartNetworkSeriesView.java @@ -35,7 +35,7 @@ import com.google.common.base.Preconditions; */ public class ChartNetworkSeriesView extends View { private static final String TAG = "ChartNetworkSeriesView"; - private static final boolean LOGD = false; + private static final boolean LOGD = true; private final ChartAxis mHoriz; private final ChartAxis mVert; @@ -80,6 +80,9 @@ public class ChartNetworkSeriesView extends View { public void bindNetworkStats(NetworkStatsHistory stats) { mStats = stats; + + mPathStroke.reset(); + mPathFill.reset(); } public void bindSweepRange(ChartSweepView sweep1, ChartSweepView sweep2) { @@ -99,7 +102,9 @@ public class ChartNetworkSeriesView extends View { * Erase any existing {@link Path} and generate series outline based on * currently bound {@link NetworkStatsHistory} data. */ - private void generatePath() { + public void generatePath() { + if (LOGD) Log.d(TAG, "generatePath()"); + mPathStroke.reset(); mPathFill.reset(); @@ -114,6 +119,9 @@ public class ChartNetworkSeriesView extends View { float lastX = 0; float lastY = 0; + // TODO: count fractional data from first bucket crossing start; + // currently it only accepts first full bucket. + long totalData = 0; for (int i = 0; i < mStats.bucketCount; i++) { diff --git a/src/com/android/settings/widget/ChartSweepView.java b/src/com/android/settings/widget/ChartSweepView.java index e3130ce..788caad 100644 --- a/src/com/android/settings/widget/ChartSweepView.java +++ b/src/com/android/settings/widget/ChartSweepView.java @@ -19,6 +19,7 @@ package com.android.settings.widget; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.DashPathEffect; import android.graphics.Paint; import android.graphics.Paint.Style; import android.view.MotionEvent; @@ -33,6 +34,7 @@ import com.google.common.base.Preconditions; public class ChartSweepView extends View { private final Paint mPaintSweep; + private final Paint mPaintSweepDisabled; private final Paint mPaintShadow; private final ChartAxis mAxis; @@ -59,6 +61,13 @@ public class ChartSweepView extends View { mPaintSweep.setStyle(Style.FILL_AND_STROKE); mPaintSweep.setAntiAlias(true); + mPaintSweepDisabled = new Paint(); + mPaintSweepDisabled.setColor(color); + mPaintSweepDisabled.setStrokeWidth(1.5f); + mPaintSweepDisabled.setStyle(Style.FILL_AND_STROKE); + mPaintSweepDisabled.setPathEffect(new DashPathEffect(new float[] { 5, 5 }, 0)); + mPaintSweepDisabled.setAntiAlias(true); + mPaintShadow = new Paint(); mPaintShadow.setColor(Color.BLACK); mPaintShadow.setStrokeWidth(6.0f); @@ -81,6 +90,10 @@ public class ChartSweepView extends View { return mAxis; } + public void setValue(long value) { + mValue = value; + } + public long getValue() { return mValue; } @@ -91,6 +104,8 @@ public class ChartSweepView extends View { @Override public boolean onTouchEvent(MotionEvent event) { + if (!isEnabled()) return false; + final View parent = (View) getParent(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { @@ -98,6 +113,8 @@ public class ChartSweepView extends View { return true; } case MotionEvent.ACTION_MOVE: { + getParent().requestDisallowInterceptTouchEvent(true); + if (mHorizontal) { setTranslationY(event.getRawY() - mTracking.getRawY()); final float point = (getTop() + getTranslationY() + (getHeight() / 2)) @@ -143,12 +160,14 @@ public class ChartSweepView extends View { mHorizontal = width > height; + final Paint linePaint = isEnabled() ? mPaintSweep : mPaintSweepDisabled; + if (mHorizontal) { final int centerY = height / 2; final int endX = width - height; canvas.drawLine(0, centerY, endX, centerY, mPaintShadow); - canvas.drawLine(0, centerY, endX, centerY, mPaintSweep); + canvas.drawLine(0, centerY, endX, centerY, linePaint); canvas.drawCircle(endX, centerY, 4.0f, mPaintShadow); canvas.drawCircle(endX, centerY, 4.0f, mPaintSweep); } else { @@ -156,7 +175,7 @@ public class ChartSweepView extends View { final int endY = height - width; canvas.drawLine(centerX, 0, centerX, endY, mPaintShadow); - canvas.drawLine(centerX, 0, centerX, endY, mPaintSweep); + canvas.drawLine(centerX, 0, centerX, endY, linePaint); canvas.drawCircle(centerX, endY, 4.0f, mPaintShadow); canvas.drawCircle(centerX, endY, 4.0f, mPaintSweep); } diff --git a/src/com/android/settings/widget/ChartView.java b/src/com/android/settings/widget/ChartView.java index bcb54f0..3e5fc50 100644 --- a/src/com/android/settings/widget/ChartView.java +++ b/src/com/android/settings/widget/ChartView.java @@ -22,6 +22,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import android.content.Context; import android.graphics.Rect; +import android.util.Log; import android.view.Gravity; import android.view.View; import android.widget.FrameLayout; @@ -37,8 +38,8 @@ public class ChartView extends FrameLayout { // TODO: extend something that supports two-dimensional scrolling - private final ChartAxis mHoriz; - private final ChartAxis mVert; + final ChartAxis mHoriz; + final ChartAxis mVert; private Rect mContent = new Rect(); @@ -54,8 +55,8 @@ public class ChartView extends FrameLayout { @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { - mContent.set(l + getPaddingLeft(), t + getPaddingTop(), r - getPaddingRight(), - b - getPaddingBottom()); + mContent.set(getPaddingLeft(), getPaddingTop(), r - l - getPaddingRight(), + b - t - getPaddingBottom()); final int width = mContent.width(); final int height = mContent.height(); diff --git a/src/com/android/settings/widget/DataUsageChartView.java b/src/com/android/settings/widget/DataUsageChartView.java new file mode 100644 index 0000000..defa953 --- /dev/null +++ b/src/com/android/settings/widget/DataUsageChartView.java @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.widget; + +import android.content.Context; +import android.graphics.Color; +import android.net.NetworkPolicy; +import android.net.NetworkStatsHistory; +import android.text.format.DateUtils; + +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 { + + private static final long KB_IN_BYTES = 1024; + private static final long MB_IN_BYTES = KB_IN_BYTES * 1024; + private static final long GB_IN_BYTES = MB_IN_BYTES * 1024; + + private ChartNetworkSeriesView mSeries; + + // TODO: limit sweeps at graph boundaries + private ChartSweepView mSweepTime1; + private ChartSweepView mSweepTime2; + private ChartSweepView mSweepDataWarn; + private ChartSweepView mSweepDataLimit; + + public interface DataUsageChartListener { + public void onInspectRangeChanged(); + public void onLimitsChanged(); + } + + private DataUsageChartListener mListener; + + private static ChartAxis buildTimeAxis() { + return new TimeAxis(); + } + + private static ChartAxis buildDataAxis() { + return new InvertedChartAxis(new DataAxis()); + } + + public DataUsageChartView(Context context) { + super(context, buildTimeAxis(), buildDataAxis()); + setPadding(20, 20, 20, 20); + + addView(new ChartGridView(context, mHoriz, mVert), buildChartParams()); + + mSeries = new ChartNetworkSeriesView(context, mHoriz, mVert); + addView(mSeries, buildChartParams()); + + mSweepTime1 = new ChartSweepView(context, mHoriz, 0L, Color.parseColor("#ffffff")); + mSweepTime2 = new ChartSweepView(context, mHoriz, 0L, Color.parseColor("#ffffff")); + mSweepDataWarn = new ChartSweepView(context, mVert, 0L, Color.parseColor("#f7931d")); + mSweepDataLimit = new ChartSweepView(context, mVert, 0L, Color.parseColor("#be1d2c")); + + addView(mSweepTime1, buildSweepParams()); + addView(mSweepTime2, buildSweepParams()); + addView(mSweepDataWarn, buildSweepParams()); + addView(mSweepDataLimit, buildSweepParams()); + + mSeries.bindSweepRange(mSweepTime1, mSweepTime2); + + mSweepTime1.addOnSweepListener(mSweepListener); + mSweepTime2.addOnSweepListener(mSweepListener); + + } + + public void setListener(DataUsageChartListener listener) { + mListener = listener; + } + + public void bindNetworkStats(NetworkStatsHistory stats) { + mSeries.bindNetworkStats(stats); + } + + public void bindNetworkPolicy(NetworkPolicy policy) { + if (policy.limitBytes != -1) { + mSweepDataLimit.setValue(policy.limitBytes); + mSweepDataLimit.setEnabled(true); + } else { + mSweepDataLimit.setValue(5 * GB_IN_BYTES); + mSweepDataLimit.setEnabled(false); + } + + mSweepDataWarn.setValue(policy.warningBytes); + } + + private OnSweepListener mSweepListener = new OnSweepListener() { + public void onSweep(ChartSweepView sweep, boolean sweepDone) { + // always update graph clip region + mSeries.invalidate(); + + // update detail list only when done sweeping + if (sweepDone && mListener != null) { + mListener.onInspectRangeChanged(); + } + } + }; + + /** + * Return current inspection range (start and end time) based on internal + * {@link ChartSweepView} positions. + */ + public long[] getInspectRange() { + final long sweep1 = mSweepTime1.getValue(); + final long sweep2 = mSweepTime2.getValue(); + final long start = Math.min(sweep1, sweep2); + final long end = Math.max(sweep1, sweep2); + return new long[] { start, end }; + } + + public long getWarningBytes() { + return mSweepDataWarn.getValue(); + } + + public long getLimitBytes() { + return mSweepDataLimit.getValue(); + } + + /** + * Set the exact time range that should be displayed, updating how + * {@link ChartNetworkSeriesView} paints. Moves inspection ranges to be the + * last "week" of available data, without triggering listener events. + */ + public void setVisibleRange(long start, long end, long dataBoundary) { + mHoriz.setBounds(start, end); + + // default sweeps to last week of data + final long halfRange = (end + start) / 2; + final long sweepMax = Math.min(end, dataBoundary); + final long sweepMin = Math.max(start, (sweepMax - DateUtils.WEEK_IN_MILLIS)); + + mSweepTime1.setValue(sweepMin); + mSweepTime2.setValue(sweepMax); + + requestLayout(); + mSeries.generatePath(); + } + + public static class TimeAxis implements ChartAxis { + private static final long TICK_INTERVAL = DateUtils.DAY_IN_MILLIS * 7; + + private long mMin; + private long mMax; + private float mSize; + + public TimeAxis() { + final long currentTime = System.currentTimeMillis(); + setBounds(currentTime - DateUtils.DAY_IN_MILLIS * 30, currentTime); + } + + /** {@inheritDoc} */ + public void setBounds(long min, long max) { + mMin = min; + mMax = max; + } + + /** {@inheritDoc} */ + public void setSize(float size) { + this.mSize = size; + } + + /** {@inheritDoc} */ + public float convertToPoint(long value) { + return (mSize * (value - mMin)) / (mMax - mMin); + } + + /** {@inheritDoc} */ + public long convertToValue(float point) { + return (long) (mMin + ((point * (mMax - mMin)) / mSize)); + } + + /** {@inheritDoc} */ + public CharSequence getLabel(long value) { + // TODO: convert to string + return Long.toString(value); + } + + /** {@inheritDoc} */ + public float[] getTickPoints() { + // tick mark for every week + 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)); + } + return tickPoints; + } + } + + public static class DataAxis implements ChartAxis { + private long mMin; + private long mMax; + private long mMinLog; + private long mMaxLog; + private float mSize; + + public DataAxis() { + // TODO: adapt ranges to show when history >5GB, and handle 4G + // interfaces with higher limits. + setBounds(1 * MB_IN_BYTES, 5 * GB_IN_BYTES); + } + + /** {@inheritDoc} */ + public void setBounds(long min, long max) { + mMin = min; + mMax = max; + mMinLog = (long) Math.log(mMin); + mMaxLog = (long) Math.log(mMax); + } + + /** {@inheritDoc} */ + public void setSize(float size) { + this.mSize = size; + } + + /** {@inheritDoc} */ + public float convertToPoint(long value) { + return (mSize * (value - mMin)) / (mMax - mMin); + + // TODO: finish tweaking log scale +// if (value > mMin) { +// return (float) ((mSize * (Math.log(value) - mMinLog)) / (mMaxLog - mMinLog)); +// } else { +// return 0; +// } + } + + /** {@inheritDoc} */ + public long convertToValue(float point) { + return (long) (mMin + ((point * (mMax - mMin)) / mSize)); + + // TODO: finish tweaking log scale +// return (long) Math.pow(Math.E, (mMinLog + ((point * (mMaxLog - mMinLog)) / mSize))); + } + + /** {@inheritDoc} */ + public CharSequence getLabel(long value) { + // TODO: convert to string + return Long.toString(value); + } + + /** {@inheritDoc} */ + public float[] getTickPoints() { + final float[] tickPoints = new float[16]; + + long value = mMax; + float mult = 0.8f; + for (int i = 0; i < tickPoints.length; i++) { + tickPoints[i] = convertToPoint(value); + value = (long) (value * mult); + mult *= 0.9; + } + return tickPoints; + } + } + +} diff --git a/src/com/android/settings/widget/InvertedChartAxis.java b/src/com/android/settings/widget/InvertedChartAxis.java index 2bda320..e7e7893 100644 --- a/src/com/android/settings/widget/InvertedChartAxis.java +++ b/src/com/android/settings/widget/InvertedChartAxis.java @@ -28,6 +28,11 @@ public class InvertedChartAxis implements ChartAxis { } /** {@inheritDoc} */ + public void setBounds(long min, long max) { + mWrapped.setBounds(min, max); + } + + /** {@inheritDoc} */ public void setSize(float size) { mSize = size; mWrapped.setSize(size); |