Iterate on data usage chart UI.

Switched to inflating chart views from XML, using attributes for
configuration.  Start using drawable assets for chart components
instead of manually painting.  Include hand-cut assets, and animate
between states when touched to invoke.

Clamp sweeps to valid chart ranges and prepare for sweep labels.

Bug: 4768483, 4598460
Change-Id: Ic660c35bec826eb5e3f6a1dde3cc04d8c437ef2b
This commit is contained in:
Jeff Sharkey
2011-06-23 00:39:38 -07:00
parent aef3981e86
commit 52c3f4461b
27 changed files with 608 additions and 223 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 851 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 790 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 711 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 833 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 799 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 813 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 847 B

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:exitFadeDuration="@android:integer/config_mediumAnimTime">
<item android:state_activated="true" android:drawable="@drawable/data_sweep_left_activated" />
<item android:state_activated="false" android:drawable="@drawable/data_sweep_left_default" />
</selector>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:exitFadeDuration="@android:integer/config_mediumAnimTime">
<item android:state_activated="true" android:drawable="@drawable/data_sweep_limit_activated" />
<item android:state_activated="false" android:drawable="@drawable/data_sweep_limit_default" />
</selector>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:exitFadeDuration="@android:integer/config_mediumAnimTime">
<item android:state_activated="true" android:drawable="@drawable/data_sweep_right_activated" />
<item android:state_activated="false" android:drawable="@drawable/data_sweep_right_default" />
</selector>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:exitFadeDuration="@android:integer/config_mediumAnimTime">
<item android:state_activated="true" android:drawable="@drawable/data_sweep_warning_activated" />
<item android:state_activated="false" android:drawable="@drawable/data_sweep_warning_default" />
</selector>

View File

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<com.android.settings.widget.DataUsageChartView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
android:layout_width="match_parent"
android:layout_height="220dip"
android:padding="16dip">
<com.android.settings.widget.ChartGridView
android:id="@+id/grid"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="left|bottom"
settings:primaryDrawable="@drawable/data_grid_primary"
settings:secondaryDrawable="@drawable/data_grid_secondary"
settings:borderDrawable="@drawable/data_grid_border"
settings:labelColor="#24aae1" />
<com.android.settings.widget.ChartNetworkSeriesView
android:id="@+id/series"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="left|bottom"
settings:strokeColor="#24aae1"
settings:fillColor="#c050ade5"
settings:fillColorSecondary="#88566abc" />
<com.android.settings.widget.ChartSweepView
android:id="@+id/sweep_left"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
settings:sweepDrawable="@drawable/data_sweep_left"
settings:followAxis="horizontal"
settings:showLabel="false" />
<com.android.settings.widget.ChartSweepView
android:id="@+id/sweep_right"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
settings:sweepDrawable="@drawable/data_sweep_right"
settings:followAxis="horizontal"
settings:showLabel="false" />
<com.android.settings.widget.ChartSweepView
android:id="@+id/sweep_limit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
settings:sweepDrawable="@drawable/data_sweep_limit"
settings:followAxis="vertical"
settings:showLabel="true" />
<com.android.settings.widget.ChartSweepView
android:id="@+id/sweep_warning"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
settings:sweepDrawable="@drawable/data_sweep_warning"
settings:followAxis="vertical"
settings:showLabel="true" />
</com.android.settings.widget.DataUsageChartView>

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="48dip"
android:orientation="vertical">
<TextView
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="6dip"
android:layout_marginTop="6dip"
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="@android:id/text2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="6dip"
android:layout_marginBottom="6dip"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>

View File

@@ -49,4 +49,26 @@
<!-- Minimum tick width for each slice in the bar chart. --> <!-- Minimum tick width for each slice in the bar chart. -->
<attr name="minTickWidth" format="dimension" /> <attr name="minTickWidth" format="dimension" />
</declare-styleable> </declare-styleable>
<declare-styleable name="ChartSweepView">
<attr name="sweepDrawable" format="reference" />
<attr name="followAxis">
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
</attr>
<attr name="showLabel" format="boolean" />
</declare-styleable>
<declare-styleable name="ChartGridView">
<attr name="primaryDrawable" format="reference" />
<attr name="secondaryDrawable" format="reference" />
<attr name="borderDrawable" format="reference" />
<attr name="labelColor" format="color" />
</declare-styleable>
<declare-styleable name="ChartNetworkSeriesView">
<attr name="strokeColor" format="color" />
<attr name="fillColor" format="color" />
<attr name="fillColorSecondary" format="color" />
</declare-styleable>
</resources> </resources>

View File

@@ -3456,7 +3456,7 @@ found in the list of installed applications.</string>
<!-- Subtitle of dialog for editing data usage cycle reset date. [CHAR LIMIT=32] --> <!-- Subtitle of dialog for editing data usage cycle reset date. [CHAR LIMIT=32] -->
<string name="data_usage_cycle_editor_subtitle">Date of each month:</string> <string name="data_usage_cycle_editor_subtitle">Date of each month:</string>
<!-- Positive button title for data usage cycle editor, confirming that changes should be saved. [CHAR LIMIT=32] --> <!-- Positive button title for data usage cycle editor, confirming that changes should be saved. [CHAR LIMIT=32] -->
<string name="data_usage_cycle_editor_positive">set</string> <string name="data_usage_cycle_editor_positive">Set</string>
<!-- Title of dialog shown before user limits data usage. [CHAR LIMIT=48] --> <!-- Title of dialog shown before user limits data usage. [CHAR LIMIT=48] -->
<string name="data_usage_limit_dialog_title">Limiting data usage</string> <string name="data_usage_limit_dialog_title">Limiting data usage</string>
@@ -3476,6 +3476,9 @@ found in the list of installed applications.</string>
<!-- Body of dialog shown when data usage has exceeded limit and has been disabled. [CHAR LIMIT=NONE] --> <!-- Body of dialog shown when data usage has exceeded limit and has been disabled. [CHAR LIMIT=NONE] -->
<string name="data_usage_disabled_dialog">The specified data usage limit has been reached.\n\nAdditional data use may incur carrier charges.</string> <string name="data_usage_disabled_dialog">The specified data usage limit has been reached.\n\nAdditional data use may incur carrier charges.</string>
<!-- Dialog button indicating that data connection should be re-enabled. [CHAR LIMIT=28] --> <!-- Dialog button indicating that data connection should be re-enabled. [CHAR LIMIT=28] -->
<string name="data_usage_disabled_dialog_enable">re-enable data</string> <string name="data_usage_disabled_dialog_enable">Re-enable data</string>
<!-- Label displaying current network data usage warning threshold. [CHAR LIMIT=18] -->
<string name="data_usage_sweep_warning"><font size="32"><xliff:g id="number" example="128">%1$s</xliff:g></font> <font size="12"><xliff:g id="unit" example="KB">%2$s</xliff:g></font>\n<font size="12">warning</font></string>
</resources> </resources>

View File

@@ -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_4G;
import static android.net.NetworkTemplate.MATCH_MOBILE_ALL; import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
import static android.net.NetworkTemplate.MATCH_WIFI; import static android.net.NetworkTemplate.MATCH_WIFI;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import android.app.AlertDialog; import android.app.AlertDialog;
@@ -68,7 +67,6 @@ import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener; import android.widget.AdapterView.OnItemSelectedListener;
@@ -204,6 +202,7 @@ public class DataUsageSummary extends Fragment {
mDataEnabled.setOnCheckedChangeListener(mDataEnabledListener); mDataEnabled.setOnCheckedChangeListener(mDataEnabledListener);
mDisableAtLimit = new CheckBox(inflater.getContext()); mDisableAtLimit = new CheckBox(inflater.getContext());
mDisableAtLimit.setClickable(false);
mDisableAtLimitView = inflatePreference(inflater, mSwitches, mDisableAtLimit); mDisableAtLimitView = inflatePreference(inflater, mSwitches, mDisableAtLimit);
mDisableAtLimitView.setOnClickListener(mDisableAtLimitListener); mDisableAtLimitView.setOnClickListener(mDisableAtLimitListener);
@@ -216,11 +215,8 @@ public class DataUsageSummary extends Fragment {
mCycleSpinner.setAdapter(mCycleAdapter); mCycleSpinner.setAdapter(mCycleAdapter);
mCycleSpinner.setOnItemSelectedListener(mCycleListener); mCycleSpinner.setOnItemSelectedListener(mCycleListener);
final int chartHeight = getResources().getDimensionPixelSize( mChart = (DataUsageChartView) inflater.inflate(R.layout.data_usage_chart, mListView, false);
R.dimen.data_usage_chart_height);
mChart = new DataUsageChartView(context);
mChart.setListener(mChartListener); mChart.setListener(mChartListener);
mChart.setLayoutParams(new AbsListView.LayoutParams(MATCH_PARENT, chartHeight));
mListView.addHeaderView(mChart, null, false); mListView.addHeaderView(mChart, null, false);
mAdapter = new DataUsageAdapter(); mAdapter = new DataUsageAdapter();
@@ -791,7 +787,7 @@ public class DataUsageSummary extends Fragment {
public View getView(int position, View convertView, ViewGroup parent) { public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) { if (convertView == null) {
convertView = LayoutInflater.from(parent.getContext()).inflate( 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(); 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 * 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) { private static void setPreferenceTitle(View parent, int resId) {
final TextView title = (TextView) parent.findViewById(android.R.id.title); final TextView title = (TextView) parent.findViewById(android.R.id.title);

View File

@@ -29,6 +29,7 @@ public interface ChartAxis {
public long convertToValue(float point); public long convertToValue(float point);
public CharSequence getLabel(long value); public CharSequence getLabel(long value);
public CharSequence getShortLabel(long value);
public float[] getTickPoints(); public float[] getTickPoints();

View File

@@ -17,12 +17,13 @@
package com.android.settings.widget; package com.android.settings.widget;
import android.content.Context; import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.drawable.Drawable;
import android.graphics.Paint; import android.util.AttributeSet;
import android.graphics.Paint.Style;
import android.view.View; import android.view.View;
import com.android.settings.R;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
/** /**
@@ -31,32 +32,42 @@ import com.google.common.base.Preconditions;
*/ */
public class ChartGridView extends View { public class ChartGridView extends View {
private final ChartAxis mHoriz; // TODO: eventually teach about drawing chart labels
private final ChartAxis mVert;
private final Paint mPaintHoriz; private ChartAxis mHoriz;
private final Paint mPaintVert; private ChartAxis mVert;
public ChartGridView(Context context, ChartAxis horiz, ChartAxis vert) { private Drawable mPrimary;
super(context); private Drawable mSecondary;
private Drawable mBorder;
mHoriz = Preconditions.checkNotNull(horiz, "missing horiz"); public ChartGridView(Context context) {
mVert = Preconditions.checkNotNull(vert, "missing vert"); 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); setWillNotDraw(false);
// TODO: convert these colors to resources final TypedArray a = context.obtainStyledAttributes(
mPaintHoriz = new Paint(); attrs, R.styleable.ChartGridView, defStyle, 0);
mPaintHoriz.setColor(Color.parseColor("#667bb5"));
mPaintHoriz.setStrokeWidth(2.0f);
mPaintHoriz.setStyle(Style.STROKE);
mPaintHoriz.setAntiAlias(true);
mPaintVert = new Paint(); mPrimary = a.getDrawable(R.styleable.ChartGridView_primaryDrawable);
mPaintVert.setColor(Color.parseColor("#28262c")); mSecondary = a.getDrawable(R.styleable.ChartGridView_secondaryDrawable);
mPaintVert.setStrokeWidth(1.0f); mBorder = a.getDrawable(R.styleable.ChartGridView_borderDrawable);
mPaintVert.setStyle(Style.STROKE); // TODO: eventually read labelColor
mPaintVert.setAntiAlias(true);
a.recycle();
}
void init(ChartAxis horiz, ChartAxis vert) {
mHoriz = Preconditions.checkNotNull(horiz, "missing horiz");
mVert = Preconditions.checkNotNull(vert, "missing vert");
} }
@Override @Override
@@ -64,16 +75,28 @@ public class ChartGridView extends View {
final int width = getWidth(); final int width = getWidth();
final int height = getHeight(); final int height = getHeight();
final Drawable secondary = mSecondary;
final int secondaryHeight = mSecondary.getIntrinsicHeight();
final float[] vertTicks = mVert.getTickPoints(); final float[] vertTicks = mVert.getTickPoints();
for (float y : vertTicks) { 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(); final float[] horizTicks = mHoriz.getTickPoints();
for (float x : horizTicks) { 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);
} }
} }

View File

@@ -17,6 +17,7 @@
package com.android.settings.widget; package com.android.settings.widget;
import android.content.Context; import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Paint; import android.graphics.Paint;
@@ -24,9 +25,11 @@ import android.graphics.Paint.Style;
import android.graphics.Path; import android.graphics.Path;
import android.graphics.RectF; import android.graphics.RectF;
import android.net.NetworkStatsHistory; import android.net.NetworkStatsHistory;
import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import com.android.settings.R;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
/** /**
@@ -37,35 +40,54 @@ public class ChartNetworkSeriesView extends View {
private static final String TAG = "ChartNetworkSeriesView"; private static final String TAG = "ChartNetworkSeriesView";
private static final boolean LOGD = true; private static final boolean LOGD = true;
private final ChartAxis mHoriz; private ChartAxis mHoriz;
private final ChartAxis mVert; private ChartAxis mVert;
private Paint mPaintStroke; private Paint mPaintStroke;
private Paint mPaintFill; private Paint mPaintFill;
private Paint mPaintFillDisabled; private Paint mPaintFillSecondary;
private NetworkStatsHistory mStats; private NetworkStatsHistory mStats;
private Path mPathStroke; private Path mPathStroke;
private Path mPathFill; private Path mPathFill;
private ChartSweepView mSweep1; private long mPrimaryLeft;
private ChartSweepView mSweep2; private long mPrimaryRight;
public ChartNetworkSeriesView(Context context, ChartAxis horiz, ChartAxis vert) { public ChartNetworkSeriesView(Context context) {
super(context); this(context, null, 0);
}
mHoriz = Preconditions.checkNotNull(horiz, "missing horiz"); public ChartNetworkSeriesView(Context context, AttributeSet attrs) {
mVert = Preconditions.checkNotNull(vert, "missing vert"); this(context, attrs, 0);
}
setChartColor(Color.parseColor("#24aae1"), Color.parseColor("#c050ade5"), public ChartNetworkSeriesView(Context context, AttributeSet attrs, int defStyle) {
Color.parseColor("#88566abc")); 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(); mPathStroke = new Path();
mPathFill = 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 = new Paint();
mPaintStroke.setStrokeWidth(6.0f); mPaintStroke.setStrokeWidth(6.0f);
mPaintStroke.setColor(stroke); mPaintStroke.setColor(stroke);
@@ -77,10 +99,10 @@ public class ChartNetworkSeriesView extends View {
mPaintFill.setStyle(Style.FILL); mPaintFill.setStyle(Style.FILL);
mPaintFill.setAntiAlias(true); mPaintFill.setAntiAlias(true);
mPaintFillDisabled = new Paint(); mPaintFillSecondary = new Paint();
mPaintFillDisabled.setColor(disabled); mPaintFillSecondary.setColor(fillSecondary);
mPaintFillDisabled.setStyle(Style.FILL); mPaintFillSecondary.setStyle(Style.FILL);
mPaintFillDisabled.setAntiAlias(true); mPaintFillSecondary.setAntiAlias(true);
} }
public void bindNetworkStats(NetworkStatsHistory stats) { public void bindNetworkStats(NetworkStatsHistory stats) {
@@ -90,12 +112,10 @@ public class ChartNetworkSeriesView extends View {
mPathFill.reset(); mPathFill.reset();
} }
public void bindSweepRange(ChartSweepView sweep1, ChartSweepView sweep2) { public void setPrimaryRange(long left, long right) {
// TODO: generalize to support vertical sweeps mPrimaryLeft = left;
// TODO: enforce that both sweeps are along same dimension mPrimaryRight = right;
invalidate();
mSweep1 = Preconditions.checkNotNull(sweep1, "missing sweep1");
mSweep2 = Preconditions.checkNotNull(sweep2, "missing sweep2");
} }
@Override @Override
@@ -168,27 +188,20 @@ public class ChartNetworkSeriesView extends View {
@Override @Override
protected void onDraw(Canvas canvas) { 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; int save;
save = canvas.save(); save = canvas.save();
canvas.clipRect(0, 0, sweepLeft, getHeight()); canvas.clipRect(0, 0, mPrimaryLeft, getHeight());
canvas.drawPath(mPathFill, mPaintFillDisabled); canvas.drawPath(mPathFill, mPaintFillSecondary);
canvas.restoreToCount(save); canvas.restoreToCount(save);
save = canvas.save(); save = canvas.save();
canvas.clipRect(sweepRight, 0, getWidth(), getHeight()); canvas.clipRect(mPrimaryRight, 0, getWidth(), getHeight());
canvas.drawPath(mPathFill, mPaintFillDisabled); canvas.drawPath(mPathFill, mPaintFillSecondary);
canvas.restoreToCount(save); canvas.restoreToCount(save);
save = canvas.save(); save = canvas.save();
canvas.clipRect(sweepLeft, 0, sweepRight, getHeight()); canvas.clipRect(mPrimaryLeft, 0, mPrimaryRight, getHeight());
canvas.drawPath(mPathFill, mPaintFill); canvas.drawPath(mPathFill, mPaintFill);
canvas.drawPath(mPathStroke, mPaintStroke); canvas.drawPath(mPathStroke, mPaintStroke);
canvas.restoreToCount(save); canvas.restoreToCount(save);

View File

@@ -17,63 +17,79 @@
package com.android.settings.widget; package com.android.settings.widget;
import android.content.Context; import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Rect;
import android.graphics.DashPathEffect; import android.graphics.drawable.Drawable;
import android.graphics.Paint; import android.util.AttributeSet;
import android.graphics.Paint.Style; import android.util.MathUtils;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.widget.FrameLayout;
import com.android.settings.R;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
/** /**
* Sweep across a {@link ChartView} at a specific {@link ChartAxis} value, which * Sweep across a {@link ChartView} at a specific {@link ChartAxis} value, which
* a user can drag. * a user can drag.
*/ */
public class ChartSweepView extends View { public class ChartSweepView extends FrameLayout {
private final Paint mPaintSweep; // TODO: paint label when requested
private final Paint mPaintSweepDisabled;
private final Paint mPaintShadow;
private final ChartAxis mAxis; private Drawable mSweep;
private int mFollowAxis;
private boolean mShowLabel;
private ChartAxis mAxis;
private long mValue; private long mValue;
public static final int HORIZONTAL = 0;
public static final int VERTICAL = 1;
public interface OnSweepListener { public interface OnSweepListener {
public void onSweep(ChartSweepView sweep, boolean sweepDone); public void onSweep(ChartSweepView sweep, boolean sweepDone);
} }
private OnSweepListener mListener; private OnSweepListener mListener;
private boolean mHorizontal;
private MotionEvent mTracking; private MotionEvent mTracking;
public ChartSweepView(Context context, ChartAxis axis, long value, int color) { public ChartSweepView(Context context) {
super(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"); mAxis = Preconditions.checkNotNull(axis, "missing axis");
mValue = value; }
mPaintSweep = new Paint(); public int getFollowAxis() {
mPaintSweep.setColor(color); return mFollowAxis;
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 void getExtraMargins(Rect rect) {
mSweep.getPadding(rect);
} }
public void addOnSweepListener(OnSweepListener listener) { 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() { public ChartAxis getAxis() {
return mAxis; return mAxis;
} }
@@ -115,14 +181,24 @@ public class ChartSweepView extends View {
case MotionEvent.ACTION_MOVE: { case MotionEvent.ACTION_MOVE: {
getParent().requestDisallowInterceptTouchEvent(true); getParent().requestDisallowInterceptTouchEvent(true);
if (mHorizontal) { if (mFollowAxis == VERTICAL) {
setTranslationY(event.getRawY() - mTracking.getRawY()); 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)) final float point = (getTop() + getTranslationY() + (getHeight() / 2))
- parent.getPaddingTop(); - parent.getPaddingTop();
mValue = mAxis.convertToValue(point); mValue = mAxis.convertToValue(point);
dispatchOnSweep(false); dispatchOnSweep(false);
} else { } 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)) final float point = (getLeft() + getTranslationX() + (getWidth() / 2))
- parent.getPaddingLeft(); - parent.getPaddingLeft();
mValue = mAxis.convertToValue(point); 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 @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// need at least 50px in each direction for grippies setMeasuredDimension(mSweep.getIntrinsicWidth(), mSweep.getIntrinsicHeight());
// TODO: provide this value through params
setMeasuredDimension(50, 50);
} }
@Override @Override
protected void onDraw(Canvas canvas) { protected void onDraw(Canvas canvas) {
// draw line across larger dimension
final int width = getWidth(); final int width = getWidth();
final int height = getHeight(); final int height = getHeight();
mHorizontal = width > height; mSweep.setBounds(0, 0, width, height);
mSweep.draw(canvas);
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);
}
} }
} }

View File

@@ -16,13 +16,11 @@
package com.android.settings.widget; 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 static com.google.common.base.Preconditions.checkNotNull;
import android.content.Context; import android.content.Context;
import android.graphics.Rect; import android.graphics.Rect;
import android.util.Log; import android.util.AttributeSet;
import android.view.Gravity; import android.view.Gravity;
import android.view.View; import android.view.View;
import android.widget.FrameLayout; import android.widget.FrameLayout;
@@ -38,21 +36,31 @@ public class ChartView extends FrameLayout {
// TODO: extend something that supports two-dimensional scrolling // TODO: extend something that supports two-dimensional scrolling
final ChartAxis mHoriz; ChartAxis mHoriz;
final ChartAxis mVert; ChartAxis mVert;
private Rect mContent = new Rect(); private Rect mContent = new Rect();
public ChartView(Context context, ChartAxis horiz, ChartAxis vert) { public ChartView(Context context) {
super(context); this(context, null, 0);
}
mHoriz = checkNotNull(horiz, "missing horiz"); public ChartView(Context context, AttributeSet attrs) {
mVert = checkNotNull(vert, "missing vert"); this(context, attrs, 0);
}
public ChartView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setClipToPadding(false); setClipToPadding(false);
setClipChildren(false); setClipChildren(false);
} }
void init(ChartAxis horiz, ChartAxis vert) {
mHoriz = checkNotNull(horiz, "missing horiz");
mVert = checkNotNull(vert, "missing vert");
}
@Override @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) { protected void onLayout(boolean changed, int l, int t, int r, int b) {
mContent.set(getPaddingLeft(), getPaddingTop(), r - l - getPaddingRight(), mContent.set(getPaddingLeft(), getPaddingTop(), r - l - getPaddingRight(),
@@ -66,6 +74,7 @@ public class ChartView extends FrameLayout {
final Rect parentRect = new Rect(); final Rect parentRect = new Rect();
final Rect childRect = new Rect(); final Rect childRect = new Rect();
final Rect extraMargins = new Rect();
for (int i = 0; i < getChildCount(); i++) { for (int i = 0; i < getChildCount(); i++) {
final View child = getChildAt(i); final View child = getChildAt(i);
@@ -82,23 +91,22 @@ public class ChartView extends FrameLayout {
} else if (child instanceof ChartSweepView) { } else if (child instanceof ChartSweepView) {
// sweep is always placed along specific dimension // sweep is always placed along specific dimension
final ChartSweepView sweep = (ChartSweepView) child; final ChartSweepView sweep = (ChartSweepView) child;
final ChartAxis axis = sweep.getAxis();
final float point = sweep.getPoint(); final float point = sweep.getPoint();
sweep.getExtraMargins(extraMargins);
if (axis == mHoriz) { if (sweep.getFollowAxis() == ChartSweepView.HORIZONTAL) {
parentRect.left = parentRect.right = (int) point + getPaddingLeft(); 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(), Gravity.apply(params.gravity, child.getMeasuredWidth(), parentRect.height(),
parentRect, childRect); parentRect, childRect);
} else if (axis == mVert) { } else {
parentRect.top = parentRect.bottom = (int) point + getPaddingTop(); 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(), Gravity.apply(params.gravity, parentRect.width(), child.getMeasuredHeight(),
parentRect, childRect); 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;
}
} }

View File

@@ -17,12 +17,14 @@
package com.android.settings.widget; package com.android.settings.widget;
import android.content.Context; import android.content.Context;
import android.graphics.Color;
import android.net.NetworkPolicy; import android.net.NetworkPolicy;
import android.net.NetworkStatsHistory; import android.net.NetworkStatsHistory;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View; import android.view.View;
import com.android.settings.R;
import com.android.settings.widget.ChartSweepView.OnSweepListener; 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 MB_IN_BYTES = KB_IN_BYTES * 1024;
private static final long GB_IN_BYTES = MB_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; private ChartNetworkSeriesView mSeries;
// TODO: limit sweeps at graph boundaries private ChartSweepView mSweepLeft;
private ChartSweepView mSweepTime1; private ChartSweepView mSweepRight;
private ChartSweepView mSweepTime2; private ChartSweepView mSweepWarning;
private ChartSweepView mSweepDataWarn; private ChartSweepView mSweepLimit;
private ChartSweepView mSweepDataLimit;
public interface DataUsageChartListener { public interface DataUsageChartListener {
public void onInspectRangeChanged(); public void onInspectRangeChanged();
@@ -51,46 +56,58 @@ public class DataUsageChartView extends ChartView {
private DataUsageChartListener mListener; private DataUsageChartListener mListener;
private static ChartAxis buildTimeAxis() {
return new TimeAxis();
}
private static ChartAxis buildDataAxis() {
return new InvertedChartAxis(new DataAxis());
}
public DataUsageChartView(Context context) { public DataUsageChartView(Context context) {
super(context, buildTimeAxis(), buildDataAxis()); this(context, null, 0);
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);
} }
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) { public void setChartColor(int stroke, int fill, int disabled) {
mSeries.setChartColor(stroke, fill, disabled); mSeries.setChartColor(stroke, fill, disabled);
} }
@@ -105,36 +122,46 @@ public class DataUsageChartView extends ChartView {
public void bindNetworkPolicy(NetworkPolicy policy) { public void bindNetworkPolicy(NetworkPolicy policy) {
if (policy == null) { if (policy == null) {
mSweepDataLimit.setVisibility(View.INVISIBLE); mSweepLimit.setVisibility(View.INVISIBLE);
mSweepDataWarn.setVisibility(View.INVISIBLE); mSweepWarning.setVisibility(View.INVISIBLE);
return; return;
} }
if (policy.limitBytes != NetworkPolicy.LIMIT_DISABLED) { if (policy.limitBytes != NetworkPolicy.LIMIT_DISABLED) {
mSweepDataLimit.setVisibility(View.VISIBLE); mSweepLimit.setVisibility(View.VISIBLE);
mSweepDataLimit.setValue(policy.limitBytes); mSweepLimit.setValue(policy.limitBytes);
mSweepDataLimit.setEnabled(true); mSweepLimit.setEnabled(true);
} else { } else {
// TODO: set limit default based on axis maximum // TODO: set limit default based on axis maximum
mSweepDataLimit.setVisibility(View.VISIBLE); mSweepLimit.setVisibility(View.VISIBLE);
mSweepDataLimit.setValue(5 * GB_IN_BYTES); mSweepLimit.setValue(5 * GB_IN_BYTES);
mSweepDataLimit.setEnabled(false); mSweepLimit.setEnabled(false);
} }
if (policy.warningBytes != NetworkPolicy.WARNING_DISABLED) { if (policy.warningBytes != NetworkPolicy.WARNING_DISABLED) {
mSweepDataWarn.setVisibility(View.VISIBLE); mSweepWarning.setVisibility(View.VISIBLE);
mSweepDataWarn.setValue(policy.warningBytes); mSweepWarning.setValue(policy.warningBytes);
} else { } else {
mSweepDataWarn.setVisibility(View.INVISIBLE); mSweepWarning.setVisibility(View.INVISIBLE);
} }
requestLayout(); 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() { private OnSweepListener mSweepListener = new OnSweepListener() {
public void onSweep(ChartSweepView sweep, boolean sweepDone) { public void onSweep(ChartSweepView sweep, boolean sweepDone) {
// always update graph clip region mSeries.setPrimaryRange(mSweepLeft.getValue(), mSweepRight.getValue());
mSeries.invalidate();
// update detail list only when done sweeping // update detail list only when done sweeping
if (sweepDone && mListener != null) { 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 * Return current inspection range (start and end time) based on internal
* {@link ChartSweepView} positions. * {@link ChartSweepView} positions.
*/ */
public long[] getInspectRange() { public long[] getInspectRange() {
final long sweep1 = mSweepTime1.getValue(); final long start = mSweepLeft.getValue();
final long sweep2 = mSweepTime2.getValue(); final long end = mSweepRight.getValue();
final long start = Math.min(sweep1, sweep2);
final long end = Math.max(sweep1, sweep2);
return new long[] { start, end }; return new long[] { start, end };
} }
public long getWarningBytes() { public long getWarningBytes() {
return mSweepDataWarn.getValue(); return mSweepWarning.getValue();
} }
public long getLimitBytes() { 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 sweepMax = Math.min(end, dataBoundary);
final long sweepMin = Math.max(start, (sweepMax - DateUtils.WEEK_IN_MILLIS)); final long sweepMin = Math.max(start, (sweepMax - DateUtils.WEEK_IN_MILLIS));
mSweepTime1.setValue(sweepMin); mSweepLeft.setValue(sweepMin);
mSweepTime2.setValue(sweepMax); mSweepRight.setValue(sweepMax);
mSeries.setPrimaryRange(sweepMin, sweepMax);
requestLayout(); requestLayout();
mSeries.generatePath(); mSeries.generatePath();
@@ -239,6 +282,12 @@ public class DataUsageChartView extends ChartView {
return Long.toString(value); return Long.toString(value);
} }
/** {@inheritDoc} */
public CharSequence getShortLabel(long value) {
// TODO: convert to string
return Long.toString(value);
}
/** {@inheritDoc} */ /** {@inheritDoc} */
public float[] getTickPoints() { public float[] getTickPoints() {
// tick mark for every week // tick mark for every week
@@ -299,6 +348,16 @@ public class DataUsageChartView extends ChartView {
/** {@inheritDoc} */ /** {@inheritDoc} */
public CharSequence getLabel(long value) { 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 // TODO: convert to string
return Long.toString(value); return Long.toString(value);
} }

View File

@@ -53,6 +53,11 @@ public class InvertedChartAxis implements ChartAxis {
return mWrapped.getLabel(value); return mWrapped.getLabel(value);
} }
/** {@inheritDoc} */
public CharSequence getShortLabel(long value) {
return mWrapped.getShortLabel(value);
}
/** {@inheritDoc} */ /** {@inheritDoc} */
public float[] getTickPoints() { public float[] getTickPoints() {
final float[] points = mWrapped.getTickPoints(); final float[] points = mWrapped.getTickPoints();