Merge "Move UsageGraph from SettingsLib to Settings."
This commit is contained in:
committed by
Android (Google) Code Review
commit
5999ea8f15
@@ -49,7 +49,7 @@
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textColor="?android:attr/textColorSecondary"/>
|
||||
|
||||
<com.android.settingslib.graph.UsageView
|
||||
<com.android.settings.graph.UsageView
|
||||
android:id="@+id/battery_usage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="141dp"
|
||||
|
@@ -32,7 +32,7 @@
|
||||
android:textSize="36sp"
|
||||
android:textColor="?android:attr/colorAccent" />
|
||||
|
||||
<com.android.settingslib.graph.UsageView
|
||||
<com.android.settings.graph.UsageView
|
||||
android:id="@+id/battery_usage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="141dp"
|
||||
|
@@ -22,7 +22,7 @@
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.android.settingslib.graph.UsageView
|
||||
<com.android.settings.graph.UsageView
|
||||
android:id="@+id/data_usage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/data_usage_chart_height"
|
||||
|
20
res/layout/usage_bottom_label.xml
Normal file
20
res/layout/usage_bottom_label.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<!--
|
||||
Copyright (C) 2016 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.
|
||||
-->
|
||||
<TextView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
20
res/layout/usage_side_label.xml
Normal file
20
res/layout/usage_side_label.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<!--
|
||||
Copyright (C) 2016 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.
|
||||
-->
|
||||
<TextView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
99
res/layout/usage_view.xml
Normal file
99
res/layout/usage_view.xml
Normal file
@@ -0,0 +1,99 @@
|
||||
<!--
|
||||
Copyright (C) 2016 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:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/graph_label_group"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="horizontal"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/label_group"
|
||||
android:layout_width="@dimen/usage_graph_labels_width"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include android:id="@+id/label_top"
|
||||
layout="@layout/usage_side_label" />
|
||||
|
||||
<Space
|
||||
android:id="@+id/space1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<include android:id="@+id/label_middle"
|
||||
layout="@layout/usage_side_label" />
|
||||
|
||||
<Space
|
||||
android:id="@+id/space2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<include android:id="@+id/label_bottom"
|
||||
layout="@layout/usage_side_label" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.android.settings.graph.UsageGraph
|
||||
android:id="@+id/usage_graph"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginTop="@dimen/usage_graph_margin_top_bottom"
|
||||
android:layout_marginBottom="@dimen/usage_graph_margin_top_bottom" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/bottom_label_group"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
<Space
|
||||
android:id="@+id/bottom_label_space"
|
||||
android:layout_width="@dimen/usage_graph_labels_width"
|
||||
android:layout_height="wrap_content"/>
|
||||
<com.android.settings.graph.BottomLabelLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="horizontal"
|
||||
android:layoutDirection="ltr">
|
||||
<include android:id="@+id/label_start"
|
||||
layout="@layout/usage_side_label" />
|
||||
|
||||
<Space
|
||||
android:id="@+id/spacer"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<include android:id="@+id/label_end"
|
||||
layout="@layout/usage_side_label" />
|
||||
</com.android.settings.graph.BottomLabelLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
@@ -162,4 +162,13 @@
|
||||
<attr name="android:tint" format="color|reference" />
|
||||
</declare-styleable>
|
||||
|
||||
<!-- For UsageView -->
|
||||
<declare-styleable name="UsageView">
|
||||
<attr name="android:colorAccent" />
|
||||
<attr name="sideLabels" format="reference" />
|
||||
<attr name="bottomLabels" format="reference" />
|
||||
<attr name="textColor" format="color" />
|
||||
<attr name="android:gravity" />
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
||||
|
@@ -15,7 +15,6 @@
|
||||
package com.android.settings.datausage;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.net.NetworkPolicy;
|
||||
import android.net.NetworkStatsHistory;
|
||||
import android.net.TrafficStats;
|
||||
@@ -29,7 +28,7 @@ import android.util.AttributeSet;
|
||||
import android.util.SparseIntArray;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settingslib.graph.UsageView;
|
||||
import com.android.settings.graph.UsageView;
|
||||
|
||||
public class ChartDataUsagePreference extends Preference {
|
||||
|
||||
|
@@ -30,7 +30,7 @@ import com.android.internal.os.BatteryStatsHelper;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsPreferenceFragment;
|
||||
import com.android.settings.fuelgauge.BatteryActiveView.BatteryActiveProvider;
|
||||
import com.android.settingslib.graph.UsageView;
|
||||
import com.android.settings.graph.UsageView;
|
||||
|
||||
public class BatteryHistoryDetail extends SettingsPreferenceFragment {
|
||||
public static final String EXTRA_STATS = "stats";
|
||||
|
@@ -24,7 +24,7 @@ import android.util.AttributeSet;
|
||||
import android.widget.TextView;
|
||||
import com.android.internal.os.BatteryStatsHelper;
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.graph.UsageView;
|
||||
import com.android.settings.graph.UsageView;
|
||||
|
||||
/**
|
||||
* Custom preference for displaying power consumption as a bar and an icon on the left for the
|
||||
|
@@ -34,7 +34,7 @@ import com.android.internal.os.BatteryStatsHelper;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settingslib.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settingslib.graph.UsageView;
|
||||
import com.android.settings.graph.UsageView;
|
||||
|
||||
public class BatteryInfo {
|
||||
|
||||
|
97
src/com/android/settings/graph/BottomLabelLayout.java
Normal file
97
src/com/android/settings/graph/BottomLabelLayout.java
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package com.android.settings.graph;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.android.settingslib.R;
|
||||
|
||||
/**
|
||||
* An extension of LinearLayout that automatically switches to vertical
|
||||
* orientation when it can't fit its child views horizontally.
|
||||
*
|
||||
* Main logic in this class comes from {@link android.support.v7.widget.ButtonBarLayout}.
|
||||
* Compared with {@link android.support.v7.widget.ButtonBarLayout}, this layout won't reverse
|
||||
* children's order and won't update the minimum height
|
||||
*/
|
||||
public class BottomLabelLayout extends LinearLayout {
|
||||
private static final String TAG = "BottomLabelLayout";
|
||||
|
||||
public BottomLabelLayout(Context context,
|
||||
@Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
|
||||
final boolean isStacked = isStacked();
|
||||
boolean needsRemeasure = false;
|
||||
|
||||
// If we're not stacked, make sure the measure spec is AT_MOST rather
|
||||
// than EXACTLY. This ensures that we'll still get TOO_SMALL so that we
|
||||
// know to stack the buttons.
|
||||
final int initialWidthMeasureSpec;
|
||||
if (!isStacked && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) {
|
||||
initialWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.AT_MOST);
|
||||
|
||||
// We'll need to remeasure again to fill excess space.
|
||||
needsRemeasure = true;
|
||||
} else {
|
||||
initialWidthMeasureSpec = widthMeasureSpec;
|
||||
}
|
||||
|
||||
super.onMeasure(initialWidthMeasureSpec, heightMeasureSpec);
|
||||
if (!isStacked) {
|
||||
final int measuredWidth = getMeasuredWidthAndState();
|
||||
final int measuredWidthState = measuredWidth & View.MEASURED_STATE_MASK;
|
||||
|
||||
if (measuredWidthState == View.MEASURED_STATE_TOO_SMALL) {
|
||||
setStacked(true);
|
||||
// Measure again in the new orientation.
|
||||
needsRemeasure = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needsRemeasure) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setStacked(boolean stacked) {
|
||||
setOrientation(stacked ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL);
|
||||
setGravity(stacked ? Gravity.START : Gravity.BOTTOM);
|
||||
|
||||
final View spacer = findViewById(R.id.spacer);
|
||||
if (spacer != null) {
|
||||
spacer.setVisibility(stacked ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isStacked() {
|
||||
return getOrientation() == LinearLayout.VERTICAL;
|
||||
}
|
||||
}
|
277
src/com/android/settings/graph/UsageGraph.java
Normal file
277
src/com/android/settings/graph/UsageGraph.java
Normal file
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the
|
||||
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.graph;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.CornerPathEffect;
|
||||
import android.graphics.DashPathEffect;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.Cap;
|
||||
import android.graphics.Paint.Join;
|
||||
import android.graphics.Paint.Style;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Shader.TileMode;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.SparseIntArray;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import com.android.settingslib.R;
|
||||
|
||||
public class UsageGraph extends View {
|
||||
|
||||
private static final int PATH_DELIM = -1;
|
||||
|
||||
private final Paint mLinePaint;
|
||||
private final Paint mFillPaint;
|
||||
private final Paint mDottedPaint;
|
||||
|
||||
private final Drawable mDivider;
|
||||
private final Drawable mTintedDivider;
|
||||
private final int mDividerSize;
|
||||
|
||||
private final Path mPath = new Path();
|
||||
|
||||
// Paths in coordinates they are passed in.
|
||||
private final SparseIntArray mPaths = new SparseIntArray();
|
||||
// Paths in local coordinates for drawing.
|
||||
private final SparseIntArray mLocalPaths = new SparseIntArray();
|
||||
private final int mCornerRadius;
|
||||
|
||||
private int mAccentColor;
|
||||
private boolean mShowProjection;
|
||||
private boolean mProjectUp;
|
||||
|
||||
private float mMaxX = 100;
|
||||
private float mMaxY = 100;
|
||||
|
||||
private float mMiddleDividerLoc = .5f;
|
||||
private int mMiddleDividerTint = -1;
|
||||
private int mTopDividerTint = -1;
|
||||
|
||||
public UsageGraph(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
final Resources resources = context.getResources();
|
||||
|
||||
mLinePaint = new Paint();
|
||||
mLinePaint.setStyle(Style.STROKE);
|
||||
mLinePaint.setStrokeCap(Cap.ROUND);
|
||||
mLinePaint.setStrokeJoin(Join.ROUND);
|
||||
mLinePaint.setAntiAlias(true);
|
||||
mCornerRadius = resources.getDimensionPixelSize(R.dimen.usage_graph_line_corner_radius);
|
||||
mLinePaint.setPathEffect(new CornerPathEffect(mCornerRadius));
|
||||
mLinePaint.setStrokeWidth(resources.getDimensionPixelSize(R.dimen.usage_graph_line_width));
|
||||
|
||||
mFillPaint = new Paint(mLinePaint);
|
||||
mFillPaint.setStyle(Style.FILL);
|
||||
|
||||
mDottedPaint = new Paint(mLinePaint);
|
||||
mDottedPaint.setStyle(Style.STROKE);
|
||||
float dots = resources.getDimensionPixelSize(R.dimen.usage_graph_dot_size);
|
||||
float interval = resources.getDimensionPixelSize(R.dimen.usage_graph_dot_interval);
|
||||
mDottedPaint.setStrokeWidth(dots * 3);
|
||||
mDottedPaint.setPathEffect(new DashPathEffect(new float[] {dots, interval}, 0));
|
||||
mDottedPaint.setColor(context.getColor(R.color.usage_graph_dots));
|
||||
|
||||
TypedValue v = new TypedValue();
|
||||
context.getTheme().resolveAttribute(com.android.internal.R.attr.listDivider, v, true);
|
||||
mDivider = context.getDrawable(v.resourceId);
|
||||
mTintedDivider = context.getDrawable(v.resourceId);
|
||||
mDividerSize = resources.getDimensionPixelSize(R.dimen.usage_graph_divider_size);
|
||||
}
|
||||
|
||||
void clearPaths() {
|
||||
mPaths.clear();
|
||||
}
|
||||
|
||||
void setMax(int maxX, int maxY) {
|
||||
mMaxX = maxX;
|
||||
mMaxY = maxY;
|
||||
}
|
||||
|
||||
void setDividerLoc(int height) {
|
||||
mMiddleDividerLoc = 1 - height / mMaxY;
|
||||
}
|
||||
|
||||
void setDividerColors(int middleColor, int topColor) {
|
||||
mMiddleDividerTint = middleColor;
|
||||
mTopDividerTint = topColor;
|
||||
}
|
||||
|
||||
public void addPath(SparseIntArray points) {
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
mPaths.put(points.keyAt(i), points.valueAt(i));
|
||||
}
|
||||
mPaths.put(points.keyAt(points.size() - 1) + 1, PATH_DELIM);
|
||||
calculateLocalPaths();
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
void setAccentColor(int color) {
|
||||
mAccentColor = color;
|
||||
mLinePaint.setColor(mAccentColor);
|
||||
updateGradient();
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
void setShowProjection(boolean showProjection, boolean projectUp) {
|
||||
mShowProjection = showProjection;
|
||||
mProjectUp = projectUp;
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
updateGradient();
|
||||
calculateLocalPaths();
|
||||
}
|
||||
|
||||
private void calculateLocalPaths() {
|
||||
if (getWidth() == 0) return;
|
||||
mLocalPaths.clear();
|
||||
int pendingXLoc = 0;
|
||||
int pendingYLoc = PATH_DELIM;
|
||||
for (int i = 0; i < mPaths.size(); i++) {
|
||||
int x = mPaths.keyAt(i);
|
||||
int y = mPaths.valueAt(i);
|
||||
if (y == PATH_DELIM) {
|
||||
if (i == mPaths.size() - 1 && pendingYLoc != PATH_DELIM) {
|
||||
// Connect to the end of the graph.
|
||||
mLocalPaths.put(pendingXLoc, pendingYLoc);
|
||||
}
|
||||
// Clear out any pending points.
|
||||
pendingYLoc = PATH_DELIM;
|
||||
mLocalPaths.put(pendingXLoc + 1, PATH_DELIM);
|
||||
} else {
|
||||
final int lx = getX(x);
|
||||
final int ly = getY(y);
|
||||
pendingXLoc = lx;
|
||||
if (mLocalPaths.size() > 0) {
|
||||
int lastX = mLocalPaths.keyAt(mLocalPaths.size() - 1);
|
||||
int lastY = mLocalPaths.valueAt(mLocalPaths.size() - 1);
|
||||
if (lastY != PATH_DELIM && !hasDiff(lastX, lx) && !hasDiff(lastY, ly)) {
|
||||
pendingYLoc = ly;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
mLocalPaths.put(lx, ly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasDiff(int x1, int x2) {
|
||||
return Math.abs(x2 - x1) >= mCornerRadius;
|
||||
}
|
||||
|
||||
private int getX(float x) {
|
||||
return (int) (x / mMaxX * getWidth());
|
||||
}
|
||||
|
||||
private int getY(float y) {
|
||||
return (int) (getHeight() * (1 - (y / mMaxY)));
|
||||
}
|
||||
|
||||
private void updateGradient() {
|
||||
mFillPaint.setShader(new LinearGradient(0, 0, 0, getHeight(),
|
||||
getColor(mAccentColor, .2f), 0, TileMode.CLAMP));
|
||||
}
|
||||
|
||||
private int getColor(int color, float alphaScale) {
|
||||
return (color & (((int) (0xff * alphaScale) << 24) | 0xffffff));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
// Draw lines across the top, middle, and bottom.
|
||||
if (mMiddleDividerLoc != 0) {
|
||||
drawDivider(0, canvas, mTopDividerTint);
|
||||
}
|
||||
drawDivider((int) ((canvas.getHeight() - mDividerSize) * mMiddleDividerLoc), canvas,
|
||||
mMiddleDividerTint);
|
||||
drawDivider(canvas.getHeight() - mDividerSize, canvas, -1);
|
||||
|
||||
if (mLocalPaths.size() == 0) {
|
||||
return;
|
||||
}
|
||||
if (mShowProjection) {
|
||||
drawProjection(canvas);
|
||||
}
|
||||
drawFilledPath(canvas);
|
||||
drawLinePath(canvas);
|
||||
}
|
||||
|
||||
private void drawProjection(Canvas canvas) {
|
||||
mPath.reset();
|
||||
int x = mLocalPaths.keyAt(mLocalPaths.size() - 2);
|
||||
int y = mLocalPaths.valueAt(mLocalPaths.size() - 2);
|
||||
mPath.moveTo(x, y);
|
||||
mPath.lineTo(canvas.getWidth(), mProjectUp ? 0 : canvas.getHeight());
|
||||
canvas.drawPath(mPath, mDottedPaint);
|
||||
}
|
||||
|
||||
private void drawLinePath(Canvas canvas) {
|
||||
mPath.reset();
|
||||
mPath.moveTo(mLocalPaths.keyAt(0), mLocalPaths.valueAt(0));
|
||||
for (int i = 1; i < mLocalPaths.size(); i++) {
|
||||
int x = mLocalPaths.keyAt(i);
|
||||
int y = mLocalPaths.valueAt(i);
|
||||
if (y == PATH_DELIM) {
|
||||
if (++i < mLocalPaths.size()) {
|
||||
mPath.moveTo(mLocalPaths.keyAt(i), mLocalPaths.valueAt(i));
|
||||
}
|
||||
} else {
|
||||
mPath.lineTo(x, y);
|
||||
}
|
||||
}
|
||||
canvas.drawPath(mPath, mLinePaint);
|
||||
}
|
||||
|
||||
private void drawFilledPath(Canvas canvas) {
|
||||
mPath.reset();
|
||||
float lastStartX = mLocalPaths.keyAt(0);
|
||||
mPath.moveTo(mLocalPaths.keyAt(0), mLocalPaths.valueAt(0));
|
||||
for (int i = 1; i < mLocalPaths.size(); i++) {
|
||||
int x = mLocalPaths.keyAt(i);
|
||||
int y = mLocalPaths.valueAt(i);
|
||||
if (y == PATH_DELIM) {
|
||||
mPath.lineTo(mLocalPaths.keyAt(i - 1), getHeight());
|
||||
mPath.lineTo(lastStartX, getHeight());
|
||||
mPath.close();
|
||||
if (++i < mLocalPaths.size()) {
|
||||
lastStartX = mLocalPaths.keyAt(i);
|
||||
mPath.moveTo(mLocalPaths.keyAt(i), mLocalPaths.valueAt(i));
|
||||
}
|
||||
} else {
|
||||
mPath.lineTo(x, y);
|
||||
}
|
||||
}
|
||||
canvas.drawPath(mPath, mFillPaint);
|
||||
}
|
||||
|
||||
private void drawDivider(int y, Canvas canvas, int tintColor) {
|
||||
Drawable d = mDivider;
|
||||
if (tintColor != -1) {
|
||||
mTintedDivider.setTint(tintColor);
|
||||
d = mTintedDivider;
|
||||
}
|
||||
d.setBounds(0, y, canvas.getWidth(), y + mDividerSize);
|
||||
d.draw(canvas);
|
||||
}
|
||||
}
|
141
src/com/android/settings/graph/UsageView.java
Normal file
141
src/com/android/settings/graph/UsageView.java
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the
|
||||
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.graph;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import com.android.settingslib.R;
|
||||
|
||||
public class UsageView extends FrameLayout {
|
||||
|
||||
private final UsageGraph mUsageGraph;
|
||||
private final TextView[] mLabels;
|
||||
private final TextView[] mBottomLabels;
|
||||
|
||||
public UsageView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
LayoutInflater.from(context).inflate(R.layout.usage_view, this);
|
||||
mUsageGraph = findViewById(R.id.usage_graph);
|
||||
mLabels = new TextView[] {
|
||||
findViewById(R.id.label_bottom),
|
||||
findViewById(R.id.label_middle),
|
||||
findViewById(R.id.label_top),
|
||||
};
|
||||
mBottomLabels = new TextView[] {
|
||||
findViewById(R.id.label_start),
|
||||
findViewById(R.id.label_end),
|
||||
};
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.UsageView, 0, 0);
|
||||
if (a.hasValue(R.styleable.UsageView_sideLabels)) {
|
||||
setSideLabels(a.getTextArray(R.styleable.UsageView_sideLabels));
|
||||
}
|
||||
if (a.hasValue(R.styleable.UsageView_bottomLabels)) {
|
||||
setBottomLabels(a.getTextArray(R.styleable.UsageView_bottomLabels));
|
||||
}
|
||||
if (a.hasValue(R.styleable.UsageView_textColor)) {
|
||||
int color = a.getColor(R.styleable.UsageView_textColor, 0);
|
||||
for (TextView v : mLabels) {
|
||||
v.setTextColor(color);
|
||||
}
|
||||
for (TextView v : mBottomLabels) {
|
||||
v.setTextColor(color);
|
||||
}
|
||||
}
|
||||
if (a.hasValue(R.styleable.UsageView_android_gravity)) {
|
||||
int gravity = a.getInt(R.styleable.UsageView_android_gravity, 0);
|
||||
if (gravity == Gravity.END) {
|
||||
LinearLayout layout = findViewById(R.id.graph_label_group);
|
||||
LinearLayout labels = findViewById(R.id.label_group);
|
||||
// Swap the children order.
|
||||
layout.removeView(labels);
|
||||
layout.addView(labels);
|
||||
// Set gravity.
|
||||
labels.setGravity(Gravity.END);
|
||||
// Swap the bottom space order.
|
||||
LinearLayout bottomLabels = findViewById(R.id.bottom_label_group);
|
||||
View bottomSpace = bottomLabels.findViewById(R.id.bottom_label_space);
|
||||
bottomLabels.removeView(bottomSpace);
|
||||
bottomLabels.addView(bottomSpace);
|
||||
} else if (gravity != Gravity.START) {
|
||||
throw new IllegalArgumentException("Unsupported gravity " + gravity);
|
||||
}
|
||||
}
|
||||
mUsageGraph.setAccentColor(a.getColor(R.styleable.UsageView_android_colorAccent, 0));
|
||||
}
|
||||
|
||||
public void clearPaths() {
|
||||
mUsageGraph.clearPaths();
|
||||
}
|
||||
|
||||
public void addPath(SparseIntArray points) {
|
||||
mUsageGraph.addPath(points);
|
||||
}
|
||||
|
||||
public void configureGraph(int maxX, int maxY, boolean showProjection, boolean projectUp) {
|
||||
mUsageGraph.setMax(maxX, maxY);
|
||||
mUsageGraph.setShowProjection(showProjection, projectUp);
|
||||
}
|
||||
|
||||
public void setAccentColor(int color) {
|
||||
mUsageGraph.setAccentColor(color);
|
||||
}
|
||||
|
||||
public void setDividerLoc(int dividerLoc) {
|
||||
mUsageGraph.setDividerLoc(dividerLoc);
|
||||
}
|
||||
|
||||
public void setDividerColors(int middleColor, int topColor) {
|
||||
mUsageGraph.setDividerColors(middleColor, topColor);
|
||||
}
|
||||
|
||||
public void setSideLabelWeights(float before, float after) {
|
||||
setWeight(R.id.space1, before);
|
||||
setWeight(R.id.space2, after);
|
||||
}
|
||||
|
||||
private void setWeight(int id, float weight) {
|
||||
View v = findViewById(id);
|
||||
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) v.getLayoutParams();
|
||||
params.weight = weight;
|
||||
v.setLayoutParams(params);
|
||||
}
|
||||
|
||||
public void setSideLabels(CharSequence[] labels) {
|
||||
if (labels.length != mLabels.length) {
|
||||
throw new IllegalArgumentException("Invalid number of labels");
|
||||
}
|
||||
for (int i = 0; i < mLabels.length; i++) {
|
||||
mLabels[i].setText(labels[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBottomLabels(CharSequence[] labels) {
|
||||
if (labels.length != mBottomLabels.length) {
|
||||
throw new IllegalArgumentException("Invalid number of labels");
|
||||
}
|
||||
for (int i = 0; i < mBottomLabels.length; i++) {
|
||||
mBottomLabels[i].setText(labels[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -30,7 +30,7 @@ import android.widget.TextView;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.TestConfig;
|
||||
import com.android.settingslib.graph.UsageView;
|
||||
import com.android.settings.graph.UsageView;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package com.android.settings.graph;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Space;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.TestConfig;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
||||
public class BottomLabelLayoutTest {
|
||||
private BottomLabelLayout mBottomLabelLayout;
|
||||
private Context mContext;
|
||||
private Space mSpace;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mBottomLabelLayout = new BottomLabelLayout(mContext, null);
|
||||
mBottomLabelLayout.setOrientation(LinearLayout.HORIZONTAL);
|
||||
|
||||
mSpace = new Space(mContext);
|
||||
mSpace.setId(R.id.spacer);
|
||||
mBottomLabelLayout.addView(mSpace);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetStacked_stackedTrue_layoutVertical() {
|
||||
mBottomLabelLayout.setStacked(true);
|
||||
|
||||
assertThat(mBottomLabelLayout.getOrientation()).isEqualTo(LinearLayout.VERTICAL);
|
||||
assertThat(mSpace.getVisibility()).isEqualTo(View.GONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetStacked_stackedFalse_layoutHorizontal() {
|
||||
mBottomLabelLayout.setStacked(false);
|
||||
|
||||
assertThat(mBottomLabelLayout.getOrientation()).isEqualTo(LinearLayout.HORIZONTAL);
|
||||
assertThat(mSpace.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user