diff --git a/res/layout/battery_history_detail.xml b/res/layout/battery_history_detail.xml index b782e397c66..ea758655231 100644 --- a/res/layout/battery_history_detail.xml +++ b/res/layout/battery_history_detail.xml @@ -49,7 +49,7 @@ android:textAppearance="?android:attr/textAppearanceMedium" android:textColor="?android:attr/textColorSecondary"/> - - - + diff --git a/res/layout/usage_side_label.xml b/res/layout/usage_side_label.xml new file mode 100644 index 00000000000..6c168806338 --- /dev/null +++ b/res/layout/usage_side_label.xml @@ -0,0 +1,20 @@ + + diff --git a/res/layout/usage_view.xml b/res/layout/usage_view.xml new file mode 100644 index 00000000000..8e9c5802bbf --- /dev/null +++ b/res/layout/usage_view.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 5e7ee7e196c..acb6ec3effd 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -162,4 +162,13 @@ + + + + + + + + + diff --git a/src/com/android/settings/datausage/ChartDataUsagePreference.java b/src/com/android/settings/datausage/ChartDataUsagePreference.java index f72d465e0db..ee074c03308 100644 --- a/src/com/android/settings/datausage/ChartDataUsagePreference.java +++ b/src/com/android/settings/datausage/ChartDataUsagePreference.java @@ -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 { diff --git a/src/com/android/settings/fuelgauge/BatteryHistoryDetail.java b/src/com/android/settings/fuelgauge/BatteryHistoryDetail.java index c6f326f454d..3661467887b 100644 --- a/src/com/android/settings/fuelgauge/BatteryHistoryDetail.java +++ b/src/com/android/settings/fuelgauge/BatteryHistoryDetail.java @@ -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"; diff --git a/src/com/android/settings/fuelgauge/BatteryHistoryPreference.java b/src/com/android/settings/fuelgauge/BatteryHistoryPreference.java index 04835344a7d..920f0e22fbc 100644 --- a/src/com/android/settings/fuelgauge/BatteryHistoryPreference.java +++ b/src/com/android/settings/fuelgauge/BatteryHistoryPreference.java @@ -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 diff --git a/src/com/android/settings/fuelgauge/BatteryInfo.java b/src/com/android/settings/fuelgauge/BatteryInfo.java index 3825218de47..6daf23b5bbc 100644 --- a/src/com/android/settings/fuelgauge/BatteryInfo.java +++ b/src/com/android/settings/fuelgauge/BatteryInfo.java @@ -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 { diff --git a/src/com/android/settings/graph/BottomLabelLayout.java b/src/com/android/settings/graph/BottomLabelLayout.java new file mode 100644 index 00000000000..45fab38bc3b --- /dev/null +++ b/src/com/android/settings/graph/BottomLabelLayout.java @@ -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; + } +} diff --git a/src/com/android/settings/graph/UsageGraph.java b/src/com/android/settings/graph/UsageGraph.java new file mode 100644 index 00000000000..37046c054c9 --- /dev/null +++ b/src/com/android/settings/graph/UsageGraph.java @@ -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); + } +} diff --git a/src/com/android/settings/graph/UsageView.java b/src/com/android/settings/graph/UsageView.java new file mode 100644 index 00000000000..fd128c44206 --- /dev/null +++ b/src/com/android/settings/graph/UsageView.java @@ -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]); + } + } + +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHistoryPreferenceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHistoryPreferenceTest.java index c9be151b3e5..76ec5ba0ac8 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHistoryPreferenceTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHistoryPreferenceTest.java @@ -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; diff --git a/tests/robotests/src/com/android/settings/graph/BottomLabelLayoutTest.java b/tests/robotests/src/com/android/settings/graph/BottomLabelLayoutTest.java new file mode 100644 index 00000000000..c9804d667f1 --- /dev/null +++ b/tests/robotests/src/com/android/settings/graph/BottomLabelLayoutTest.java @@ -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); + } +}