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:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
android:textColor="?android:attr/textColorSecondary"/>
|
android:textColor="?android:attr/textColorSecondary"/>
|
||||||
|
|
||||||
<com.android.settingslib.graph.UsageView
|
<com.android.settings.graph.UsageView
|
||||||
android:id="@+id/battery_usage"
|
android:id="@+id/battery_usage"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="141dp"
|
android:layout_height="141dp"
|
||||||
|
@@ -32,7 +32,7 @@
|
|||||||
android:textSize="36sp"
|
android:textSize="36sp"
|
||||||
android:textColor="?android:attr/colorAccent" />
|
android:textColor="?android:attr/colorAccent" />
|
||||||
|
|
||||||
<com.android.settingslib.graph.UsageView
|
<com.android.settings.graph.UsageView
|
||||||
android:id="@+id/battery_usage"
|
android:id="@+id/battery_usage"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="141dp"
|
android:layout_height="141dp"
|
||||||
|
@@ -22,7 +22,7 @@
|
|||||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<com.android.settingslib.graph.UsageView
|
<com.android.settings.graph.UsageView
|
||||||
android:id="@+id/data_usage"
|
android:id="@+id/data_usage"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="@dimen/data_usage_chart_height"
|
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" />
|
<attr name="android:tint" format="color|reference" />
|
||||||
</declare-styleable>
|
</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>
|
</resources>
|
||||||
|
@@ -15,7 +15,6 @@
|
|||||||
package com.android.settings.datausage;
|
package com.android.settings.datausage;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.TypedArray;
|
|
||||||
import android.net.NetworkPolicy;
|
import android.net.NetworkPolicy;
|
||||||
import android.net.NetworkStatsHistory;
|
import android.net.NetworkStatsHistory;
|
||||||
import android.net.TrafficStats;
|
import android.net.TrafficStats;
|
||||||
@@ -29,7 +28,7 @@ import android.util.AttributeSet;
|
|||||||
import android.util.SparseIntArray;
|
import android.util.SparseIntArray;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.Utils;
|
import com.android.settings.Utils;
|
||||||
import com.android.settingslib.graph.UsageView;
|
import com.android.settings.graph.UsageView;
|
||||||
|
|
||||||
public class ChartDataUsagePreference extends Preference {
|
public class ChartDataUsagePreference extends Preference {
|
||||||
|
|
||||||
|
@@ -30,7 +30,7 @@ import com.android.internal.os.BatteryStatsHelper;
|
|||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.SettingsPreferenceFragment;
|
import com.android.settings.SettingsPreferenceFragment;
|
||||||
import com.android.settings.fuelgauge.BatteryActiveView.BatteryActiveProvider;
|
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 class BatteryHistoryDetail extends SettingsPreferenceFragment {
|
||||||
public static final String EXTRA_STATS = "stats";
|
public static final String EXTRA_STATS = "stats";
|
||||||
|
@@ -24,7 +24,7 @@ import android.util.AttributeSet;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import com.android.internal.os.BatteryStatsHelper;
|
import com.android.internal.os.BatteryStatsHelper;
|
||||||
import com.android.settings.R;
|
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
|
* 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.settings.overlay.FeatureFactory;
|
||||||
import com.android.settingslib.R;
|
import com.android.settingslib.R;
|
||||||
import com.android.settings.Utils;
|
import com.android.settings.Utils;
|
||||||
import com.android.settingslib.graph.UsageView;
|
import com.android.settings.graph.UsageView;
|
||||||
|
|
||||||
public class BatteryInfo {
|
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.R;
|
||||||
import com.android.settings.SettingsRobolectricTestRunner;
|
import com.android.settings.SettingsRobolectricTestRunner;
|
||||||
import com.android.settings.TestConfig;
|
import com.android.settings.TestConfig;
|
||||||
import com.android.settingslib.graph.UsageView;
|
import com.android.settings.graph.UsageView;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
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