diff --git a/res/drawable-hdpi/data_grid_border.9.png b/res/drawable-hdpi/data_grid_border.9.png new file mode 100644 index 00000000000..e0110b699aa Binary files /dev/null and b/res/drawable-hdpi/data_grid_border.9.png differ diff --git a/res/drawable-hdpi/data_grid_primary.9.png b/res/drawable-hdpi/data_grid_primary.9.png new file mode 100644 index 00000000000..a2b7b82b623 Binary files /dev/null and b/res/drawable-hdpi/data_grid_primary.9.png differ diff --git a/res/drawable-hdpi/data_grid_secondary.9.png b/res/drawable-hdpi/data_grid_secondary.9.png new file mode 100644 index 00000000000..f13bf78e5d8 Binary files /dev/null and b/res/drawable-hdpi/data_grid_secondary.9.png differ diff --git a/res/drawable-hdpi/data_sweep_left_activated.9.png b/res/drawable-hdpi/data_sweep_left_activated.9.png new file mode 100644 index 00000000000..e91ccf5fb1a Binary files /dev/null and b/res/drawable-hdpi/data_sweep_left_activated.9.png differ diff --git a/res/drawable-hdpi/data_sweep_left_default.9.png b/res/drawable-hdpi/data_sweep_left_default.9.png new file mode 100644 index 00000000000..76f33a5b696 Binary files /dev/null and b/res/drawable-hdpi/data_sweep_left_default.9.png differ diff --git a/res/drawable-hdpi/data_sweep_limit_activated.9.png b/res/drawable-hdpi/data_sweep_limit_activated.9.png new file mode 100644 index 00000000000..47440372a8c Binary files /dev/null and b/res/drawable-hdpi/data_sweep_limit_activated.9.png differ diff --git a/res/drawable-hdpi/data_sweep_limit_default.9.png b/res/drawable-hdpi/data_sweep_limit_default.9.png new file mode 100644 index 00000000000..dea3a0b153c Binary files /dev/null and b/res/drawable-hdpi/data_sweep_limit_default.9.png differ diff --git a/res/drawable-hdpi/data_sweep_right_activated.9.png b/res/drawable-hdpi/data_sweep_right_activated.9.png new file mode 100644 index 00000000000..afb15d63618 Binary files /dev/null and b/res/drawable-hdpi/data_sweep_right_activated.9.png differ diff --git a/res/drawable-hdpi/data_sweep_right_default.9.png b/res/drawable-hdpi/data_sweep_right_default.9.png new file mode 100644 index 00000000000..9d3808f729f Binary files /dev/null and b/res/drawable-hdpi/data_sweep_right_default.9.png differ diff --git a/res/drawable-hdpi/data_sweep_warning_activated.9.png b/res/drawable-hdpi/data_sweep_warning_activated.9.png new file mode 100644 index 00000000000..81a7aa87506 Binary files /dev/null and b/res/drawable-hdpi/data_sweep_warning_activated.9.png differ diff --git a/res/drawable-hdpi/data_sweep_warning_default.9.png b/res/drawable-hdpi/data_sweep_warning_default.9.png new file mode 100644 index 00000000000..e2485febd43 Binary files /dev/null and b/res/drawable-hdpi/data_sweep_warning_default.9.png differ diff --git a/res/drawable/data_sweep_left.xml b/res/drawable/data_sweep_left.xml new file mode 100644 index 00000000000..739a74ecb95 --- /dev/null +++ b/res/drawable/data_sweep_left.xml @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/res/drawable/data_sweep_limit.xml b/res/drawable/data_sweep_limit.xml new file mode 100644 index 00000000000..29ecec8c939 --- /dev/null +++ b/res/drawable/data_sweep_limit.xml @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/res/drawable/data_sweep_right.xml b/res/drawable/data_sweep_right.xml new file mode 100644 index 00000000000..1a114699bc4 --- /dev/null +++ b/res/drawable/data_sweep_right.xml @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/res/drawable/data_sweep_warning.xml b/res/drawable/data_sweep_warning.xml new file mode 100644 index 00000000000..5cafe068bb2 --- /dev/null +++ b/res/drawable/data_sweep_warning.xml @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/res/layout/data_usage_chart.xml b/res/layout/data_usage_chart.xml new file mode 100644 index 00000000000..5fd640f51a2 --- /dev/null +++ b/res/layout/data_usage_chart.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + diff --git a/res/layout/data_usage_item.xml b/res/layout/data_usage_item.xml new file mode 100644 index 00000000000..6451e214f44 --- /dev/null +++ b/res/layout/data_usage_item.xml @@ -0,0 +1,39 @@ + + + + + + + + + + diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 7cff69c2e3d..06d26508658 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -49,4 +49,26 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 986d86789d2..5839934d518 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -3456,7 +3456,7 @@ found in the list of installed applications. Date of each month: - set + Set Limiting data usage @@ -3476,6 +3476,9 @@ found in the list of installed applications. The specified data usage limit has been reached.\n\nAdditional data use may incur carrier charges. - re-enable data + Re-enable data + + + %1$s %2$s\nwarning diff --git a/src/com/android/settings/DataUsageSummary.java b/src/com/android/settings/DataUsageSummary.java index ef2282aef97..692c75382ef 100644 --- a/src/com/android/settings/DataUsageSummary.java +++ b/src/com/android/settings/DataUsageSummary.java @@ -27,7 +27,6 @@ import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER; import static android.net.NetworkTemplate.MATCH_MOBILE_4G; import static android.net.NetworkTemplate.MATCH_MOBILE_ALL; import static android.net.NetworkTemplate.MATCH_WIFI; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import android.app.AlertDialog; @@ -68,7 +67,6 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemSelectedListener; @@ -204,6 +202,7 @@ public class DataUsageSummary extends Fragment { mDataEnabled.setOnCheckedChangeListener(mDataEnabledListener); mDisableAtLimit = new CheckBox(inflater.getContext()); + mDisableAtLimit.setClickable(false); mDisableAtLimitView = inflatePreference(inflater, mSwitches, mDisableAtLimit); mDisableAtLimitView.setOnClickListener(mDisableAtLimitListener); @@ -216,11 +215,8 @@ public class DataUsageSummary extends Fragment { mCycleSpinner.setAdapter(mCycleAdapter); mCycleSpinner.setOnItemSelectedListener(mCycleListener); - final int chartHeight = getResources().getDimensionPixelSize( - R.dimen.data_usage_chart_height); - mChart = new DataUsageChartView(context); + mChart = (DataUsageChartView) inflater.inflate(R.layout.data_usage_chart, mListView, false); mChart.setListener(mChartListener); - mChart.setLayoutParams(new AbsListView.LayoutParams(MATCH_PARENT, chartHeight)); mListView.addHeaderView(mChart, null, false); mAdapter = new DataUsageAdapter(); @@ -791,7 +787,7 @@ public class DataUsageSummary extends Fragment { public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = LayoutInflater.from(parent.getContext()).inflate( - android.R.layout.simple_list_item_2, parent, false); + R.layout.data_usage_item, parent, false); } final Context context = parent.getContext(); @@ -1080,7 +1076,7 @@ public class DataUsageSummary extends Fragment { /** * Set {@link android.R.id#title} for a preference view inflated with - * {@link #inflatePreference(LayoutInflater, View, View)}. + * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}. */ private static void setPreferenceTitle(View parent, int resId) { final TextView title = (TextView) parent.findViewById(android.R.id.title); diff --git a/src/com/android/settings/widget/ChartAxis.java b/src/com/android/settings/widget/ChartAxis.java index 2b21d285616..e761202d679 100644 --- a/src/com/android/settings/widget/ChartAxis.java +++ b/src/com/android/settings/widget/ChartAxis.java @@ -29,6 +29,7 @@ public interface ChartAxis { public long convertToValue(float point); public CharSequence getLabel(long value); + public CharSequence getShortLabel(long value); public float[] getTickPoints(); diff --git a/src/com/android/settings/widget/ChartGridView.java b/src/com/android/settings/widget/ChartGridView.java index be71890b969..7a83fbfbe36 100644 --- a/src/com/android/settings/widget/ChartGridView.java +++ b/src/com/android/settings/widget/ChartGridView.java @@ -17,12 +17,13 @@ package com.android.settings.widget; import android.content.Context; +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.drawable.Drawable; +import android.util.AttributeSet; import android.view.View; +import com.android.settings.R; import com.google.common.base.Preconditions; /** @@ -31,32 +32,42 @@ import com.google.common.base.Preconditions; */ public class ChartGridView extends View { - private final ChartAxis mHoriz; - private final ChartAxis mVert; + // TODO: eventually teach about drawing chart labels - private final Paint mPaintHoriz; - private final Paint mPaintVert; + private ChartAxis mHoriz; + private ChartAxis mVert; - public ChartGridView(Context context, ChartAxis horiz, ChartAxis vert) { - super(context); + private Drawable mPrimary; + private Drawable mSecondary; + private Drawable mBorder; - mHoriz = Preconditions.checkNotNull(horiz, "missing horiz"); - mVert = Preconditions.checkNotNull(vert, "missing vert"); + public ChartGridView(Context context) { + this(context, null, 0); + } + + public ChartGridView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ChartGridView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); setWillNotDraw(false); - // TODO: convert these colors to resources - mPaintHoriz = new Paint(); - mPaintHoriz.setColor(Color.parseColor("#667bb5")); - mPaintHoriz.setStrokeWidth(2.0f); - mPaintHoriz.setStyle(Style.STROKE); - mPaintHoriz.setAntiAlias(true); + final TypedArray a = context.obtainStyledAttributes( + attrs, R.styleable.ChartGridView, defStyle, 0); - mPaintVert = new Paint(); - mPaintVert.setColor(Color.parseColor("#28262c")); - mPaintVert.setStrokeWidth(1.0f); - mPaintVert.setStyle(Style.STROKE); - mPaintVert.setAntiAlias(true); + mPrimary = a.getDrawable(R.styleable.ChartGridView_primaryDrawable); + mSecondary = a.getDrawable(R.styleable.ChartGridView_secondaryDrawable); + mBorder = a.getDrawable(R.styleable.ChartGridView_borderDrawable); + // TODO: eventually read labelColor + + a.recycle(); + } + + void init(ChartAxis horiz, ChartAxis vert) { + mHoriz = Preconditions.checkNotNull(horiz, "missing horiz"); + mVert = Preconditions.checkNotNull(vert, "missing vert"); } @Override @@ -64,16 +75,28 @@ public class ChartGridView extends View { final int width = getWidth(); final int height = getHeight(); + final Drawable secondary = mSecondary; + final int secondaryHeight = mSecondary.getIntrinsicHeight(); + final float[] vertTicks = mVert.getTickPoints(); for (float y : vertTicks) { - canvas.drawLine(0, y, width, y, mPaintVert); + final int bottom = (int) Math.min(y + secondaryHeight, height); + secondary.setBounds(0, (int) y, width, bottom); + secondary.draw(canvas); } + final Drawable primary = mPrimary; + final int primaryWidth = mPrimary.getIntrinsicWidth(); + final int primaryHeight = mPrimary.getIntrinsicHeight(); + final float[] horizTicks = mHoriz.getTickPoints(); for (float x : horizTicks) { - canvas.drawLine(x, 0, x, height, mPaintHoriz); + final int right = (int) Math.min(x + primaryWidth, width); + primary.setBounds((int) x, 0, right, height); + primary.draw(canvas); } - canvas.drawRect(0, 0, width, height, mPaintHoriz); + mBorder.setBounds(0, 0, width, height); + mBorder.draw(canvas); } } diff --git a/src/com/android/settings/widget/ChartNetworkSeriesView.java b/src/com/android/settings/widget/ChartNetworkSeriesView.java index 780ca46583f..0a34565faca 100644 --- a/src/com/android/settings/widget/ChartNetworkSeriesView.java +++ b/src/com/android/settings/widget/ChartNetworkSeriesView.java @@ -17,6 +17,7 @@ package com.android.settings.widget; import android.content.Context; +import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; @@ -24,9 +25,11 @@ import android.graphics.Paint.Style; import android.graphics.Path; import android.graphics.RectF; import android.net.NetworkStatsHistory; +import android.util.AttributeSet; import android.util.Log; import android.view.View; +import com.android.settings.R; import com.google.common.base.Preconditions; /** @@ -37,35 +40,54 @@ public class ChartNetworkSeriesView extends View { private static final String TAG = "ChartNetworkSeriesView"; private static final boolean LOGD = true; - private final ChartAxis mHoriz; - private final ChartAxis mVert; + private ChartAxis mHoriz; + private ChartAxis mVert; private Paint mPaintStroke; private Paint mPaintFill; - private Paint mPaintFillDisabled; + private Paint mPaintFillSecondary; private NetworkStatsHistory mStats; private Path mPathStroke; private Path mPathFill; - private ChartSweepView mSweep1; - private ChartSweepView mSweep2; + private long mPrimaryLeft; + private long mPrimaryRight; - public ChartNetworkSeriesView(Context context, ChartAxis horiz, ChartAxis vert) { - super(context); + public ChartNetworkSeriesView(Context context) { + this(context, null, 0); + } - mHoriz = Preconditions.checkNotNull(horiz, "missing horiz"); - mVert = Preconditions.checkNotNull(vert, "missing vert"); + public ChartNetworkSeriesView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } - setChartColor(Color.parseColor("#24aae1"), Color.parseColor("#c050ade5"), - Color.parseColor("#88566abc")); + public ChartNetworkSeriesView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + final TypedArray a = context.obtainStyledAttributes( + attrs, R.styleable.ChartNetworkSeriesView, defStyle, 0); + + final int stroke = a.getColor(R.styleable.ChartNetworkSeriesView_strokeColor, Color.RED); + final int fill = a.getColor(R.styleable.ChartNetworkSeriesView_fillColor, Color.RED); + final int fillSecondary = a.getColor( + R.styleable.ChartNetworkSeriesView_fillColorSecondary, Color.RED); + + setChartColor(stroke, fill, fillSecondary); + + a.recycle(); mPathStroke = new Path(); mPathFill = new Path(); } - public void setChartColor(int stroke, int fill, int disabled) { + void init(ChartAxis horiz, ChartAxis vert) { + mHoriz = Preconditions.checkNotNull(horiz, "missing horiz"); + mVert = Preconditions.checkNotNull(vert, "missing vert"); + } + + public void setChartColor(int stroke, int fill, int fillSecondary) { mPaintStroke = new Paint(); mPaintStroke.setStrokeWidth(6.0f); mPaintStroke.setColor(stroke); @@ -77,10 +99,10 @@ public class ChartNetworkSeriesView extends View { mPaintFill.setStyle(Style.FILL); mPaintFill.setAntiAlias(true); - mPaintFillDisabled = new Paint(); - mPaintFillDisabled.setColor(disabled); - mPaintFillDisabled.setStyle(Style.FILL); - mPaintFillDisabled.setAntiAlias(true); + mPaintFillSecondary = new Paint(); + mPaintFillSecondary.setColor(fillSecondary); + mPaintFillSecondary.setStyle(Style.FILL); + mPaintFillSecondary.setAntiAlias(true); } public void bindNetworkStats(NetworkStatsHistory stats) { @@ -90,12 +112,10 @@ public class ChartNetworkSeriesView extends View { mPathFill.reset(); } - public void bindSweepRange(ChartSweepView sweep1, ChartSweepView sweep2) { - // TODO: generalize to support vertical sweeps - // TODO: enforce that both sweeps are along same dimension - - mSweep1 = Preconditions.checkNotNull(sweep1, "missing sweep1"); - mSweep2 = Preconditions.checkNotNull(sweep2, "missing sweep2"); + public void setPrimaryRange(long left, long right) { + mPrimaryLeft = left; + mPrimaryRight = right; + invalidate(); } @Override @@ -168,27 +188,20 @@ public class ChartNetworkSeriesView extends View { @Override protected void onDraw(Canvas canvas) { - - // clip to sweep area - final float sweep1 = mSweep1.getPoint(); - final float sweep2 = mSweep2.getPoint(); - final float sweepLeft = Math.min(sweep1, sweep2); - final float sweepRight = Math.max(sweep1, sweep2); - int save; save = canvas.save(); - canvas.clipRect(0, 0, sweepLeft, getHeight()); - canvas.drawPath(mPathFill, mPaintFillDisabled); + canvas.clipRect(0, 0, mPrimaryLeft, getHeight()); + canvas.drawPath(mPathFill, mPaintFillSecondary); canvas.restoreToCount(save); save = canvas.save(); - canvas.clipRect(sweepRight, 0, getWidth(), getHeight()); - canvas.drawPath(mPathFill, mPaintFillDisabled); + canvas.clipRect(mPrimaryRight, 0, getWidth(), getHeight()); + canvas.drawPath(mPathFill, mPaintFillSecondary); canvas.restoreToCount(save); save = canvas.save(); - canvas.clipRect(sweepLeft, 0, sweepRight, getHeight()); + canvas.clipRect(mPrimaryLeft, 0, mPrimaryRight, getHeight()); canvas.drawPath(mPathFill, mPaintFill); canvas.drawPath(mPathStroke, mPaintStroke); canvas.restoreToCount(save); diff --git a/src/com/android/settings/widget/ChartSweepView.java b/src/com/android/settings/widget/ChartSweepView.java index 788caad4f9a..881fde41bca 100644 --- a/src/com/android/settings/widget/ChartSweepView.java +++ b/src/com/android/settings/widget/ChartSweepView.java @@ -17,63 +17,79 @@ package com.android.settings.widget; 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.Rect; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.util.MathUtils; import android.view.MotionEvent; import android.view.View; +import android.widget.FrameLayout; +import com.android.settings.R; import com.google.common.base.Preconditions; /** * Sweep across a {@link ChartView} at a specific {@link ChartAxis} value, which * a user can drag. */ -public class ChartSweepView extends View { +public class ChartSweepView extends FrameLayout { - private final Paint mPaintSweep; - private final Paint mPaintSweepDisabled; - private final Paint mPaintShadow; + // TODO: paint label when requested - private final ChartAxis mAxis; + private Drawable mSweep; + private int mFollowAxis; + private boolean mShowLabel; + + private ChartAxis mAxis; private long mValue; + public static final int HORIZONTAL = 0; + public static final int VERTICAL = 1; + public interface OnSweepListener { public void onSweep(ChartSweepView sweep, boolean sweepDone); } private OnSweepListener mListener; - - private boolean mHorizontal; private MotionEvent mTracking; - public ChartSweepView(Context context, ChartAxis axis, long value, int color) { - super(context); + public ChartSweepView(Context context) { + this(context, null, 0); + } + public ChartSweepView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ChartSweepView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + final TypedArray a = context.obtainStyledAttributes( + attrs, R.styleable.ChartSweepView, defStyle, 0); + + setSweepDrawable(a.getDrawable(R.styleable.ChartSweepView_sweepDrawable)); + setFollowAxis(a.getInt(R.styleable.ChartSweepView_followAxis, -1)); + setShowLabel(a.getBoolean(R.styleable.ChartSweepView_showLabel, false)); + + a.recycle(); + + setClipToPadding(false); + setClipChildren(false); + setWillNotDraw(false); + } + + void init(ChartAxis axis) { mAxis = Preconditions.checkNotNull(axis, "missing axis"); - mValue = value; + } - mPaintSweep = new Paint(); - mPaintSweep.setColor(color); - mPaintSweep.setStrokeWidth(3.0f); - 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); - mPaintShadow.setStyle(Style.FILL_AND_STROKE); - mPaintShadow.setAntiAlias(true); + public int getFollowAxis() { + return mFollowAxis; + } + public void getExtraMargins(Rect rect) { + mSweep.getPadding(rect); } public void addOnSweepListener(OnSweepListener listener) { @@ -86,6 +102,56 @@ public class ChartSweepView extends View { } } + public void setSweepDrawable(Drawable sweep) { + if (mSweep != null) { + mSweep.setCallback(null); + unscheduleDrawable(mSweep); + } + + if (sweep != null) { + sweep.setCallback(this); + if (sweep.isStateful()) { + sweep.setState(getDrawableState()); + } + sweep.setVisible(getVisibility() == VISIBLE, false); + mSweep = sweep; + } else { + mSweep = null; + } + + invalidate(); + } + + public void setFollowAxis(int followAxis) { + mFollowAxis = followAxis; + } + + public void setShowLabel(boolean showLabel) { + mShowLabel = showLabel; + invalidate(); + } + + @Override + public void jumpDrawablesToCurrentState() { + super.jumpDrawablesToCurrentState(); + if (mSweep != null) { + mSweep.jumpToCurrentState(); + } + } + + @Override + public void setVisibility(int visibility) { + super.setVisibility(visibility); + if (mSweep != null) { + mSweep.setVisible(visibility == VISIBLE, false); + } + } + + @Override + protected boolean verifyDrawable(Drawable who) { + return who == mSweep || super.verifyDrawable(who); + } + public ChartAxis getAxis() { return mAxis; } @@ -115,14 +181,24 @@ public class ChartSweepView extends View { case MotionEvent.ACTION_MOVE: { getParent().requestDisallowInterceptTouchEvent(true); - if (mHorizontal) { - setTranslationY(event.getRawY() - mTracking.getRawY()); + if (mFollowAxis == VERTICAL) { + final float chartHeight = parent.getHeight() - parent.getPaddingTop() + - parent.getPaddingBottom(); + final float translationY = MathUtils.constrain( + event.getRawY() - mTracking.getRawY(), -getTop(), + chartHeight - getTop()); + setTranslationY(translationY); final float point = (getTop() + getTranslationY() + (getHeight() / 2)) - parent.getPaddingTop(); mValue = mAxis.convertToValue(point); dispatchOnSweep(false); } else { - setTranslationX(event.getRawX() - mTracking.getRawX()); + final float chartWidth = parent.getWidth() - parent.getPaddingLeft() + - parent.getPaddingRight(); + final float translationX = MathUtils.constrain( + event.getRawX() - mTracking.getRawX(), -getLeft(), + chartWidth - getLeft()); + setTranslationX(translationX); final float point = (getLeft() + getTranslationX() + (getWidth() / 2)) - parent.getPaddingLeft(); mValue = mAxis.convertToValue(point); @@ -144,41 +220,26 @@ public class ChartSweepView extends View { } } + @Override + protected void drawableStateChanged() { + super.drawableStateChanged(); + if (mSweep.isStateful()) { + mSweep.setState(getDrawableState()); + } + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - // need at least 50px in each direction for grippies - // TODO: provide this value through params - setMeasuredDimension(50, 50); + setMeasuredDimension(mSweep.getIntrinsicWidth(), mSweep.getIntrinsicHeight()); } @Override protected void onDraw(Canvas canvas) { - - // draw line across larger dimension final int width = getWidth(); final int height = getHeight(); - 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, linePaint); - canvas.drawCircle(endX, centerY, 4.0f, mPaintShadow); - canvas.drawCircle(endX, centerY, 4.0f, mPaintSweep); - } else { - final int centerX = width / 2; - final int endY = height - width; - - canvas.drawLine(centerX, 0, centerX, endY, mPaintShadow); - canvas.drawLine(centerX, 0, centerX, endY, linePaint); - canvas.drawCircle(centerX, endY, 4.0f, mPaintShadow); - canvas.drawCircle(centerX, endY, 4.0f, mPaintSweep); - } + mSweep.setBounds(0, 0, width, height); + mSweep.draw(canvas); } } diff --git a/src/com/android/settings/widget/ChartView.java b/src/com/android/settings/widget/ChartView.java index 3e5fc5051b1..d762631c851 100644 --- a/src/com/android/settings/widget/ChartView.java +++ b/src/com/android/settings/widget/ChartView.java @@ -16,13 +16,11 @@ package com.android.settings.widget; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static com.google.common.base.Preconditions.checkNotNull; import android.content.Context; import android.graphics.Rect; -import android.util.Log; +import android.util.AttributeSet; import android.view.Gravity; import android.view.View; import android.widget.FrameLayout; @@ -38,21 +36,31 @@ public class ChartView extends FrameLayout { // TODO: extend something that supports two-dimensional scrolling - final ChartAxis mHoriz; - final ChartAxis mVert; + ChartAxis mHoriz; + ChartAxis mVert; private Rect mContent = new Rect(); - public ChartView(Context context, ChartAxis horiz, ChartAxis vert) { - super(context); + public ChartView(Context context) { + this(context, null, 0); + } - mHoriz = checkNotNull(horiz, "missing horiz"); - mVert = checkNotNull(vert, "missing vert"); + public ChartView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ChartView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); setClipToPadding(false); setClipChildren(false); } + void init(ChartAxis horiz, ChartAxis vert) { + mHoriz = checkNotNull(horiz, "missing horiz"); + mVert = checkNotNull(vert, "missing vert"); + } + @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { mContent.set(getPaddingLeft(), getPaddingTop(), r - l - getPaddingRight(), @@ -66,6 +74,7 @@ public class ChartView extends FrameLayout { final Rect parentRect = new Rect(); final Rect childRect = new Rect(); + final Rect extraMargins = new Rect(); for (int i = 0; i < getChildCount(); i++) { final View child = getChildAt(i); @@ -82,23 +91,22 @@ public class ChartView extends FrameLayout { } else if (child instanceof ChartSweepView) { // sweep is always placed along specific dimension final ChartSweepView sweep = (ChartSweepView) child; - final ChartAxis axis = sweep.getAxis(); final float point = sweep.getPoint(); + sweep.getExtraMargins(extraMargins); - if (axis == mHoriz) { + if (sweep.getFollowAxis() == ChartSweepView.HORIZONTAL) { parentRect.left = parentRect.right = (int) point + getPaddingLeft(); - parentRect.bottom += child.getMeasuredWidth(); + parentRect.top -= extraMargins.top; + parentRect.bottom += extraMargins.bottom; Gravity.apply(params.gravity, child.getMeasuredWidth(), parentRect.height(), parentRect, childRect); - } else if (axis == mVert) { + } else { parentRect.top = parentRect.bottom = (int) point + getPaddingTop(); - parentRect.right += child.getMeasuredHeight(); + parentRect.left -= extraMargins.left; + parentRect.right += extraMargins.right; Gravity.apply(params.gravity, parentRect.width(), child.getMeasuredHeight(), parentRect, childRect); - - } else { - throw new IllegalStateException("unexpected axis"); } } @@ -106,16 +114,4 @@ public class ChartView extends FrameLayout { } } - public static LayoutParams buildChartParams() { - final LayoutParams params = new LayoutParams(MATCH_PARENT, MATCH_PARENT); - params.gravity = Gravity.LEFT | Gravity.BOTTOM; - return params; - } - - public static LayoutParams buildSweepParams() { - final LayoutParams params = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT); - params.gravity = Gravity.CENTER; - return params; - } - } diff --git a/src/com/android/settings/widget/DataUsageChartView.java b/src/com/android/settings/widget/DataUsageChartView.java index 6c62fa810ef..1c76291b46a 100644 --- a/src/com/android/settings/widget/DataUsageChartView.java +++ b/src/com/android/settings/widget/DataUsageChartView.java @@ -17,12 +17,14 @@ 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 android.util.AttributeSet; +import android.view.MotionEvent; import android.view.View; +import com.android.settings.R; import com.android.settings.widget.ChartSweepView.OnSweepListener; /** @@ -35,13 +37,16 @@ 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 + // TODO: limit sweeps at graph boundaries + + private ChartGridView mGrid; private ChartNetworkSeriesView mSeries; - // TODO: limit sweeps at graph boundaries - private ChartSweepView mSweepTime1; - private ChartSweepView mSweepTime2; - private ChartSweepView mSweepDataWarn; - private ChartSweepView mSweepDataLimit; + private ChartSweepView mSweepLeft; + private ChartSweepView mSweepRight; + private ChartSweepView mSweepWarning; + private ChartSweepView mSweepLimit; public interface DataUsageChartListener { public void onInspectRangeChanged(); @@ -51,46 +56,58 @@ public class DataUsageChartView extends ChartView { 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); - - mSweepDataWarn.addOnSweepListener(mWarningListener); - mSweepDataLimit.addOnSweepListener(mLimitListener); - - mSweepTime1.addOnSweepListener(mSweepListener); - mSweepTime2.addOnSweepListener(mSweepListener); - - mSweepDataWarn.setVisibility(View.INVISIBLE); - mSweepDataLimit.setVisibility(View.INVISIBLE); - + this(context, null, 0); } + public DataUsageChartView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public DataUsageChartView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(new TimeAxis(), new InvertedChartAxis(new DataAxis())); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + mGrid = (ChartGridView) findViewById(R.id.grid); + mSeries = (ChartNetworkSeriesView) findViewById(R.id.series); + + mSweepLeft = (ChartSweepView) findViewById(R.id.sweep_left); + mSweepRight = (ChartSweepView) findViewById(R.id.sweep_right); + mSweepLimit = (ChartSweepView) findViewById(R.id.sweep_limit); + mSweepWarning = (ChartSweepView) findViewById(R.id.sweep_warning); + + mSweepLeft.addOnSweepListener(mSweepListener); + mSweepRight.addOnSweepListener(mSweepListener); + mSweepWarning.addOnSweepListener(mWarningListener); + mSweepLimit.addOnSweepListener(mLimitListener); + + // tell everyone about our axis + mGrid.init(mHoriz, mVert); + mSeries.init(mHoriz, mVert); + mSweepLeft.init(mHoriz); + mSweepRight.init(mHoriz); + mSweepWarning.init(mVert); + mSweepLimit.init(mVert); + + setActivated(false); + } + + @Override + public void setActivated(boolean activated) { + super.setActivated(activated); + + mSweepLeft.setEnabled(activated); + mSweepRight.setEnabled(activated); + mSweepWarning.setEnabled(activated); + mSweepLimit.setEnabled(activated); + } + + @Deprecated public void setChartColor(int stroke, int fill, int disabled) { mSeries.setChartColor(stroke, fill, disabled); } @@ -105,36 +122,46 @@ public class DataUsageChartView extends ChartView { public void bindNetworkPolicy(NetworkPolicy policy) { if (policy == null) { - mSweepDataLimit.setVisibility(View.INVISIBLE); - mSweepDataWarn.setVisibility(View.INVISIBLE); + mSweepLimit.setVisibility(View.INVISIBLE); + mSweepWarning.setVisibility(View.INVISIBLE); return; } if (policy.limitBytes != NetworkPolicy.LIMIT_DISABLED) { - mSweepDataLimit.setVisibility(View.VISIBLE); - mSweepDataLimit.setValue(policy.limitBytes); - mSweepDataLimit.setEnabled(true); + mSweepLimit.setVisibility(View.VISIBLE); + mSweepLimit.setValue(policy.limitBytes); + mSweepLimit.setEnabled(true); } else { // TODO: set limit default based on axis maximum - mSweepDataLimit.setVisibility(View.VISIBLE); - mSweepDataLimit.setValue(5 * GB_IN_BYTES); - mSweepDataLimit.setEnabled(false); + mSweepLimit.setVisibility(View.VISIBLE); + mSweepLimit.setValue(5 * GB_IN_BYTES); + mSweepLimit.setEnabled(false); } if (policy.warningBytes != NetworkPolicy.WARNING_DISABLED) { - mSweepDataWarn.setVisibility(View.VISIBLE); - mSweepDataWarn.setValue(policy.warningBytes); + mSweepWarning.setVisibility(View.VISIBLE); + mSweepWarning.setValue(policy.warningBytes); } else { - mSweepDataWarn.setVisibility(View.INVISIBLE); + mSweepWarning.setVisibility(View.INVISIBLE); } requestLayout(); + + // TODO: eventually remove this; was to work around lack of sweep clamping + if (policy.limitBytes < -1 || policy.limitBytes > 5 * GB_IN_BYTES) { + policy.limitBytes = 5 * GB_IN_BYTES; + mLimitListener.onSweep(mSweepLimit, true); + } + if (policy.warningBytes < -1 || policy.warningBytes > 5 * GB_IN_BYTES) { + policy.warningBytes = 4 * GB_IN_BYTES; + mWarningListener.onSweep(mSweepWarning, true); + } + } private OnSweepListener mSweepListener = new OnSweepListener() { public void onSweep(ChartSweepView sweep, boolean sweepDone) { - // always update graph clip region - mSeries.invalidate(); + mSeries.setPrimaryRange(mSweepLeft.getValue(), mSweepRight.getValue()); // update detail list only when done sweeping if (sweepDone && mListener != null) { @@ -159,24 +186,39 @@ public class DataUsageChartView extends ChartView { } }; + @Override + public boolean onTouchEvent(MotionEvent event) { + if (isActivated()) return false; + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: { + return true; + } + case MotionEvent.ACTION_UP: { + setActivated(true); + return true; + } + default: { + return false; + } + } + } + /** * 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); + final long start = mSweepLeft.getValue(); + final long end = mSweepRight.getValue(); return new long[] { start, end }; } public long getWarningBytes() { - return mSweepDataWarn.getValue(); + return mSweepWarning.getValue(); } public long getLimitBytes() { - return mSweepDataLimit.getValue(); + return mSweepLimit.getValue(); } /** @@ -192,8 +234,9 @@ public class DataUsageChartView extends ChartView { final long sweepMax = Math.min(end, dataBoundary); final long sweepMin = Math.max(start, (sweepMax - DateUtils.WEEK_IN_MILLIS)); - mSweepTime1.setValue(sweepMin); - mSweepTime2.setValue(sweepMax); + mSweepLeft.setValue(sweepMin); + mSweepRight.setValue(sweepMax); + mSeries.setPrimaryRange(sweepMin, sweepMax); requestLayout(); mSeries.generatePath(); @@ -239,6 +282,12 @@ public class DataUsageChartView extends ChartView { return Long.toString(value); } + /** {@inheritDoc} */ + public CharSequence getShortLabel(long value) { + // TODO: convert to string + return Long.toString(value); + } + /** {@inheritDoc} */ public float[] getTickPoints() { // tick mark for every week @@ -299,6 +348,16 @@ public class DataUsageChartView extends ChartView { /** {@inheritDoc} */ public CharSequence getLabel(long value) { + + // TODO: use exploded string here + + + // TODO: convert to string + return Long.toString(value); + } + + /** {@inheritDoc} */ + public CharSequence getShortLabel(long value) { // TODO: convert to string return Long.toString(value); } diff --git a/src/com/android/settings/widget/InvertedChartAxis.java b/src/com/android/settings/widget/InvertedChartAxis.java index e7e7893eabe..a30d24cfa39 100644 --- a/src/com/android/settings/widget/InvertedChartAxis.java +++ b/src/com/android/settings/widget/InvertedChartAxis.java @@ -53,6 +53,11 @@ public class InvertedChartAxis implements ChartAxis { return mWrapped.getLabel(value); } + /** {@inheritDoc} */ + public CharSequence getShortLabel(long value) { + return mWrapped.getShortLabel(value); + } + /** {@inheritDoc} */ public float[] getTickPoints() { final float[] points = mWrapped.getTickPoints();