Merge "Draw vertical data usage sweep labels."
diff --git a/res/layout/data_usage_chart.xml b/res/layout/data_usage_chart.xml
index 199a38e..a942bb3 100644
--- a/res/layout/data_usage_chart.xml
+++ b/res/layout/data_usage_chart.xml
@@ -55,24 +55,14 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
settings:sweepDrawable="@drawable/data_sweep_left"
- settings:followAxis="horizontal"
- settings:showLabel="false" />
+ settings:followAxis="horizontal" />
<com.android.settings.widget.ChartSweepView
android:id="@+id/sweep_right"
android:layout_width="wrap_content"
android:layout_height="match_parent"
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"
- settings:sweepDrawable="@drawable/data_sweep_limit"
- settings:followAxis="vertical"
- settings:showLabel="true" />
+ settings:followAxis="horizontal" />
<com.android.settings.widget.ChartSweepView
android:id="@+id/sweep_warning"
@@ -80,6 +70,18 @@
android:layout_height="wrap_content"
settings:sweepDrawable="@drawable/data_sweep_warning"
settings:followAxis="vertical"
- settings:showLabel="true" />
+ settings:labelSize="60dip"
+ settings:labelTemplate="@string/data_usage_sweep_warning"
+ settings:labelColor="#f7931d" />
+
+ <com.android.settings.widget.ChartSweepView
+ android:id="@+id/sweep_limit"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ settings:sweepDrawable="@drawable/data_sweep_limit"
+ settings:followAxis="vertical"
+ settings:labelSize="60dip"
+ settings:labelTemplate="@string/data_usage_sweep_limit"
+ settings:labelColor="#c01a2c" />
</com.android.settings.widget.DataUsageChartView>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 06d2650..a0a6c77 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -56,14 +56,16 @@
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
</attr>
- <attr name="showLabel" format="boolean" />
+ <attr name="labelSize" format="dimension" />
+ <attr name="labelTemplate" format="reference" />
+ <attr name="labelColor" format="color" />
</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" />
+ <attr name="labelColor" />
</declare-styleable>
<declare-styleable name="ChartNetworkSeriesView">
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 94524da..abd62bf 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3414,7 +3414,9 @@
<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>
+ <string name="data_usage_sweep_warning"><font size="21"><xliff:g id="number" example="128">^1</xliff:g></font> <font size="9"><xliff:g id="unit" example="KB">^2</xliff:g></font>\n<font size="12">warning</font></string>
+ <!-- Label displaying current network data usage limit threshold. [CHAR LIMIT=18] -->
+ <string name="data_usage_sweep_limit"><font size="21"><xliff:g id="number" example="128">^1</xliff:g></font> <font size="9"><xliff:g id="unit" example="KB">^2</xliff:g></font>\n<font size="12">limit</font></string>
<!-- Button at the bottom of the CryptKeeper screen to make an emergency call. -->
<string name="cryptkeeper_emergency_call">Emergency call</string>
diff --git a/src/com/android/settings/widget/ChartAxis.java b/src/com/android/settings/widget/ChartAxis.java
index e761202..463541f 100644
--- a/src/com/android/settings/widget/ChartAxis.java
+++ b/src/com/android/settings/widget/ChartAxis.java
@@ -16,6 +16,9 @@
package com.android.settings.widget;
+import android.content.res.Resources;
+import android.text.SpannableStringBuilder;
+
/**
* Axis along a {@link ChartView} that knows how to convert between raw point
* and screen coordinate systems.
@@ -28,8 +31,7 @@
public float convertToPoint(long value);
public long convertToValue(float point);
- public CharSequence getLabel(long value);
- public CharSequence getShortLabel(long value);
+ public void buildLabel(Resources res, SpannableStringBuilder builder, long value);
public float[] getTickPoints();
diff --git a/src/com/android/settings/widget/ChartSweepView.java b/src/com/android/settings/widget/ChartSweepView.java
index 6c9ded4..b0d00bb 100644
--- a/src/com/android/settings/widget/ChartSweepView.java
+++ b/src/com/android/settings/widget/ChartSweepView.java
@@ -19,8 +19,15 @@
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.text.DynamicLayout;
+import android.text.Layout.Alignment;
+import android.text.SpannableStringBuilder;
+import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.MathUtils;
import android.view.MotionEvent;
@@ -36,13 +43,20 @@
*/
public class ChartSweepView extends FrameLayout {
- // TODO: paint label when requested
-
private Drawable mSweep;
- private Rect mSweepMargins = new Rect();
+ private Rect mSweepPadding = new Rect();
+ private Point mSweepOffset = new Point();
+
+ private Rect mMargins = new Rect();
private int mFollowAxis;
- private boolean mShowLabel;
+
+ private int mLabelSize;
+ private int mLabelTemplateRes;
+ private int mLabelColor;
+
+ private SpannableStringBuilder mLabelTemplate;
+ private DynamicLayout mLabelLayout;
private ChartAxis mAxis;
private long mValue;
@@ -73,7 +87,10 @@
setSweepDrawable(a.getDrawable(R.styleable.ChartSweepView_sweepDrawable));
setFollowAxis(a.getInt(R.styleable.ChartSweepView_followAxis, -1));
- setShowLabel(a.getBoolean(R.styleable.ChartSweepView_showLabel, false));
+
+ setLabelSize(a.getDimensionPixelSize(R.styleable.ChartSweepView_labelSize, 0));
+ setLabelTemplate(a.getResourceId(R.styleable.ChartSweepView_labelTemplate, 0));
+ setLabelColor(a.getColor(R.styleable.ChartSweepView_labelColor, Color.BLUE));
a.recycle();
@@ -90,27 +107,23 @@
return mFollowAxis;
}
- /**
- * Return margins of {@link #setSweepDrawable(Drawable)}, indicating how the
- * sweep should be displayed around a content region.
- */
- public Rect getSweepMargins() {
- return mSweepMargins;
+ public Rect getMargins() {
+ return mMargins;
}
/**
* Return the number of pixels that the "target" area is inset from the
* {@link View} edge, along the current {@link #setFollowAxis(int)}.
*/
- public float getTargetInset() {
+ private float getTargetInset() {
if (mFollowAxis == VERTICAL) {
- final float targetHeight = mSweep.getIntrinsicHeight() - mSweepMargins.top
- - mSweepMargins.bottom;
- return mSweepMargins.top + (targetHeight / 2);
+ final float targetHeight = mSweep.getIntrinsicHeight() - mSweepPadding.top
+ - mSweepPadding.bottom;
+ return mSweepPadding.top + (targetHeight / 2);
} else {
- final float targetWidth = mSweep.getIntrinsicWidth() - mSweepMargins.left
- - mSweepMargins.right;
- return mSweepMargins.left + (targetWidth / 2);
+ final float targetWidth = mSweep.getIntrinsicWidth() - mSweepPadding.left
+ - mSweepPadding.right;
+ return mSweepPadding.left + (targetWidth / 2);
}
}
@@ -124,6 +137,12 @@
}
}
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ requestLayout();
+ }
+
public void setSweepDrawable(Drawable sweep) {
if (mSweep != null) {
mSweep.setCallback(null);
@@ -137,7 +156,7 @@
}
sweep.setVisible(getVisibility() == VISIBLE, false);
mSweep = sweep;
- sweep.getPadding(mSweepMargins);
+ sweep.getPadding(mSweepPadding);
} else {
mSweep = null;
}
@@ -149,9 +168,49 @@
mFollowAxis = followAxis;
}
- public void setShowLabel(boolean showLabel) {
- mShowLabel = showLabel;
+ public void setLabelSize(int size) {
+ mLabelSize = size;
+ invalidateLabelTemplate();
+ }
+
+ public void setLabelTemplate(int resId) {
+ mLabelTemplateRes = resId;
+ invalidateLabelTemplate();
+ }
+
+ public void setLabelColor(int color) {
+ mLabelColor = color;
+ invalidateLabelTemplate();
+ }
+
+ private void invalidateLabelTemplate() {
+ if (mLabelTemplateRes != 0) {
+ final CharSequence template = getResources().getText(mLabelTemplateRes);
+
+ final TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
+ paint.density = getResources().getDisplayMetrics().density;
+ paint.setCompatibilityScaling(getResources().getCompatibilityInfo().applicationScale);
+ paint.setColor(mLabelColor);
+
+ mLabelTemplate = new SpannableStringBuilder(template);
+ mLabelLayout = new DynamicLayout(
+ mLabelTemplate, paint, mLabelSize, Alignment.ALIGN_RIGHT, 1f, 0f, false);
+ invalidateLabel();
+
+ } else {
+ mLabelTemplate = null;
+ mLabelLayout = null;
+ }
+
invalidate();
+ requestLayout();
+ }
+
+ private void invalidateLabel() {
+ if (mLabelTemplate != null && mAxis != null) {
+ mAxis.buildLabel(getResources(), mLabelTemplate, mValue);
+ invalidate();
+ }
}
@Override
@@ -181,6 +240,7 @@
public void setValue(long value) {
mValue = value;
+ invalidateLabel();
}
public long getValue() {
@@ -207,9 +267,9 @@
// only start tracking when in sweet spot
final boolean accept;
if (mFollowAxis == VERTICAL) {
- accept = event.getX() > getWidth() - (mSweepMargins.right * 2);
+ accept = event.getX() > getWidth() - (mSweepPadding.right * 2);
} else {
- accept = event.getY() > getHeight() - (mSweepMargins.bottom * 2);
+ accept = event.getY() > getHeight() - (mSweepPadding.bottom * 2);
}
if (accept) {
@@ -222,42 +282,40 @@
case MotionEvent.ACTION_MOVE: {
getParent().requestDisallowInterceptTouchEvent(true);
- final Rect sweepMargins = mSweepMargins;
-
// content area of parent
final Rect parentContent = new Rect(parent.getPaddingLeft(), parent.getPaddingTop(),
parent.getWidth() - parent.getPaddingRight(),
parent.getHeight() - parent.getPaddingBottom());
if (mFollowAxis == VERTICAL) {
- final float currentTargetY = getTop() + getTargetInset();
+ final float currentTargetY = getTop() - mMargins.top;
final float requestedTargetY = currentTargetY
+ (event.getRawY() - mTracking.getRawY());
final float clampedTargetY = MathUtils.constrain(
requestedTargetY, parentContent.top, parentContent.bottom);
setTranslationY(clampedTargetY - currentTargetY);
- mValue = mAxis.convertToValue(clampedTargetY - parentContent.top);
- dispatchOnSweep(false);
+ setValue(mAxis.convertToValue(clampedTargetY - parentContent.top));
} else {
- final float currentTargetX = getLeft() + getTargetInset();
+ final float currentTargetX = getLeft() - mMargins.left;
final float requestedTargetX = currentTargetX
+ (event.getRawX() - mTracking.getRawX());
final float clampedTargetX = MathUtils.constrain(
requestedTargetX, parentContent.left, parentContent.right);
setTranslationX(clampedTargetX - currentTargetX);
- mValue = mAxis.convertToValue(clampedTargetX - parentContent.left);
- dispatchOnSweep(false);
+ setValue(mAxis.convertToValue(clampedTargetX - parentContent.left));
}
+
+ dispatchOnSweep(false);
return true;
}
case MotionEvent.ACTION_UP: {
mTracking = null;
+ dispatchOnSweep(true);
setTranslationX(0);
setTranslationY(0);
requestLayout();
- dispatchOnSweep(true);
return true;
}
default: {
@@ -276,7 +334,39 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- setMeasuredDimension(mSweep.getIntrinsicWidth(), mSweep.getIntrinsicHeight());
+
+ // TODO: handle vertical labels
+ if (isEnabled() && mLabelLayout != null) {
+ final int sweepHeight = mSweep.getIntrinsicHeight();
+ final int templateHeight = mLabelLayout.getHeight();
+
+ mSweepOffset.x = 0;
+ mSweepOffset.y = (int) ((templateHeight / 2) - getTargetInset());
+ setMeasuredDimension(mSweep.getIntrinsicWidth(), Math.max(sweepHeight, templateHeight));
+
+ } else {
+ mSweepOffset.x = 0;
+ mSweepOffset.y = 0;
+ setMeasuredDimension(mSweep.getIntrinsicWidth(), mSweep.getIntrinsicHeight());
+ }
+
+ if (mFollowAxis == VERTICAL) {
+ final int targetHeight = mSweep.getIntrinsicHeight() - mSweepPadding.top
+ - mSweepPadding.bottom;
+ mMargins.top = -(mSweepPadding.top + (targetHeight / 2));
+ mMargins.bottom = 0;
+ mMargins.left = -mSweepPadding.left;
+ mMargins.right = mSweepPadding.right;
+ } else {
+ final int targetWidth = mSweep.getIntrinsicWidth() - mSweepPadding.left
+ - mSweepPadding.right;
+ mMargins.left = -(mSweepPadding.left + (targetWidth / 2));
+ mMargins.right = 0;
+ mMargins.top = -mSweepPadding.top;
+ mMargins.bottom = mSweepPadding.bottom;
+ }
+
+ mMargins.offset(-mSweepOffset.x, -mSweepOffset.y);
}
@Override
@@ -284,7 +374,22 @@
final int width = getWidth();
final int height = getHeight();
- mSweep.setBounds(0, 0, width, height);
+ final int labelSize;
+ if (isEnabled() && mLabelLayout != null) {
+ mLabelLayout.draw(canvas);
+ labelSize = mLabelSize;
+ } else {
+ labelSize = 0;
+ }
+
+ if (mFollowAxis == VERTICAL) {
+ mSweep.setBounds(labelSize, mSweepOffset.y, width,
+ mSweepOffset.y + mSweep.getIntrinsicHeight());
+ } else {
+ mSweep.setBounds(mSweepOffset.x, labelSize,
+ mSweepOffset.x + mSweep.getIntrinsicWidth(), height);
+ }
+
mSweep.draw(canvas);
}
diff --git a/src/com/android/settings/widget/ChartView.java b/src/com/android/settings/widget/ChartView.java
index 9223ca6..a5b8b09 100644
--- a/src/com/android/settings/widget/ChartView.java
+++ b/src/com/android/settings/widget/ChartView.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.widget.FrameLayout;
@@ -93,22 +92,22 @@
} else if (child instanceof ChartSweepView) {
// sweep is always placed along specific dimension
final ChartSweepView sweep = (ChartSweepView) child;
- final Rect sweepMargins = sweep.getSweepMargins();
+ final Rect sweepMargins = sweep.getMargins();
- if (sweep.getFollowAxis() == ChartSweepView.HORIZONTAL) {
- parentRect.left = parentRect.right =
- (int) (sweep.getPoint() - sweep.getTargetInset()) + getPaddingLeft();
- parentRect.top -= sweepMargins.top;
- parentRect.bottom += sweepMargins.bottom;
- Gravity.apply(SWEEP_GRAVITY, child.getMeasuredWidth(), parentRect.height(),
+ if (sweep.getFollowAxis() == ChartSweepView.VERTICAL) {
+ parentRect.top += sweepMargins.top + (int) sweep.getPoint();
+ parentRect.bottom = parentRect.top;
+ parentRect.left += sweepMargins.left;
+ parentRect.right += sweepMargins.right;
+ Gravity.apply(SWEEP_GRAVITY, parentRect.width(), child.getMeasuredHeight(),
parentRect, childRect);
} else {
- parentRect.top = parentRect.bottom =
- (int) (sweep.getPoint() - sweep.getTargetInset()) + getPaddingTop();
- parentRect.left -= sweepMargins.left;
- parentRect.right += sweepMargins.right;
- Gravity.apply(SWEEP_GRAVITY, parentRect.width(), child.getMeasuredHeight(),
+ parentRect.left += sweepMargins.left + (int) sweep.getPoint();
+ parentRect.right = parentRect.left;
+ parentRect.top += sweepMargins.top;
+ parentRect.bottom += sweepMargins.bottom;
+ Gravity.apply(SWEEP_GRAVITY, child.getMeasuredWidth(), parentRect.height(),
parentRect, childRect);
}
}
diff --git a/src/com/android/settings/widget/DataUsageChartView.java b/src/com/android/settings/widget/DataUsageChartView.java
index a8bdaa6..89caef1 100644
--- a/src/com/android/settings/widget/DataUsageChartView.java
+++ b/src/com/android/settings/widget/DataUsageChartView.java
@@ -17,8 +17,12 @@
package com.android.settings.widget;
import android.content.Context;
+import android.content.res.Resources;
import android.net.NetworkPolicy;
import android.net.NetworkStatsHistory;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
@@ -283,15 +287,9 @@
}
/** {@inheritDoc} */
- public CharSequence getLabel(long value) {
- // TODO: convert to string
- return Long.toString(value);
- }
-
- /** {@inheritDoc} */
- public CharSequence getShortLabel(long value) {
- // TODO: convert to string
- return Long.toString(value);
+ public void buildLabel(Resources res, SpannableStringBuilder builder, long value) {
+ // TODO: convert to better string
+ builder.replace(0, builder.length(), Long.toString(value));
}
/** {@inheritDoc} */
@@ -345,16 +343,33 @@
return (long) fraction;
}
- /** {@inheritDoc} */
- public CharSequence getLabel(long value) {
- // TODO: use exploded string here
- return Long.toString(value);
- }
+ private static final Object sSpanSize = new Object();
+ private static final Object sSpanUnit = new Object();
/** {@inheritDoc} */
- public CharSequence getShortLabel(long value) {
- // TODO: convert to string
- return Long.toString(value);
+ public void buildLabel(Resources res, SpannableStringBuilder builder, long value) {
+
+ float result = value;
+ final CharSequence unit;
+ if (result <= 100 * MB_IN_BYTES) {
+ unit = res.getText(com.android.internal.R.string.megabyteShort);
+ result /= MB_IN_BYTES;
+ } else {
+ unit = res.getText(com.android.internal.R.string.gigabyteShort);
+ result /= GB_IN_BYTES;
+ }
+
+ final CharSequence size;
+ if (result < 10) {
+ size = String.format("%.1f", result);
+ } else {
+ size = String.format("%.0f", result);
+ }
+
+ final int[] sizeBounds = findOrCreateSpan(builder, sSpanSize, "^1");
+ builder.replace(sizeBounds[0], sizeBounds[1], size);
+ final int[] unitBounds = findOrCreateSpan(builder, sSpanUnit, "^2");
+ builder.replace(unitBounds[0], unitBounds[1], unit);
}
/** {@inheritDoc} */
@@ -372,4 +387,16 @@
}
}
+ private static int[] findOrCreateSpan(
+ SpannableStringBuilder builder, Object key, CharSequence bootstrap) {
+ int start = builder.getSpanStart(key);
+ int end = builder.getSpanEnd(key);
+ if (start == -1) {
+ start = TextUtils.indexOf(builder, bootstrap);
+ end = start + bootstrap.length();
+ builder.setSpan(key, start, end, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+ }
+ return new int[] { start, end };
+ }
+
}
diff --git a/src/com/android/settings/widget/InvertedChartAxis.java b/src/com/android/settings/widget/InvertedChartAxis.java
index a30d24c..e589da9 100644
--- a/src/com/android/settings/widget/InvertedChartAxis.java
+++ b/src/com/android/settings/widget/InvertedChartAxis.java
@@ -16,6 +16,9 @@
package com.android.settings.widget;
+import android.content.res.Resources;
+import android.text.SpannableStringBuilder;
+
/**
* Utility to invert another {@link ChartAxis}.
*/
@@ -49,13 +52,8 @@
}
/** {@inheritDoc} */
- public CharSequence getLabel(long value) {
- return mWrapped.getLabel(value);
- }
-
- /** {@inheritDoc} */
- public CharSequence getShortLabel(long value) {
- return mWrapped.getShortLabel(value);
+ public void buildLabel(Resources res, SpannableStringBuilder builder, long value) {
+ mWrapped.buildLabel(res, builder, value);
}
/** {@inheritDoc} */