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
BIN
res/drawable-hdpi/data_grid_border.9.png
Normal file
After Width: | Height: | Size: 229 B |
BIN
res/drawable-hdpi/data_grid_primary.9.png
Normal file
After Width: | Height: | Size: 203 B |
BIN
res/drawable-hdpi/data_grid_secondary.9.png
Normal file
After Width: | Height: | Size: 193 B |
BIN
res/drawable-hdpi/data_sweep_left_activated.9.png
Normal file
After Width: | Height: | Size: 851 B |
BIN
res/drawable-hdpi/data_sweep_left_default.9.png
Normal file
After Width: | Height: | Size: 790 B |
BIN
res/drawable-hdpi/data_sweep_limit_activated.9.png
Normal file
After Width: | Height: | Size: 711 B |
BIN
res/drawable-hdpi/data_sweep_limit_default.9.png
Normal file
After Width: | Height: | Size: 833 B |
BIN
res/drawable-hdpi/data_sweep_right_activated.9.png
Normal file
After Width: | Height: | Size: 766 B |
BIN
res/drawable-hdpi/data_sweep_right_default.9.png
Normal file
After Width: | Height: | Size: 799 B |
BIN
res/drawable-hdpi/data_sweep_warning_activated.9.png
Normal file
After Width: | Height: | Size: 813 B |
BIN
res/drawable-hdpi/data_sweep_warning_default.9.png
Normal file
After Width: | Height: | Size: 847 B |
22
res/drawable/data_sweep_left.xml
Normal 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>
|
22
res/drawable/data_sweep_limit.xml
Normal 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>
|
22
res/drawable/data_sweep_right.xml
Normal 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>
|
22
res/drawable/data_sweep_warning.xml
Normal 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>
|
79
res/layout/data_usage_chart.xml
Normal 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>
|
39
res/layout/data_usage_item.xml
Normal 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>
|
@@ -49,4 +49,26 @@
|
||||
<!-- Minimum tick width for each slice in the bar chart. -->
|
||||
<attr name="minTickWidth" format="dimension" />
|
||||
</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>
|
||||
|
@@ -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] -->
|
||||
<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] -->
|
||||
<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] -->
|
||||
<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] -->
|
||||
<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] -->
|
||||
<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>
|
||||
|
@@ -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);
|
||||
|
@@ -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();
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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();
|
||||
|