summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Adrian Roos <roosa@google.com> 2016-12-05 15:42:16 -0800
committer Adrian Roos <roosa@google.com> 2016-12-07 15:16:19 -0800
commit4ac8f40d96196a2a36d7bda7d92eee9bbd4ee7e7 (patch)
tree03833b1a6b867e839b6b51d091e24a08e82b314b
parente8ac4110c8ba22dfa01e9941c296d8b32781c500 (diff)
MessagingStyle: Fix buggy measure in MessagingLinearLayout
Fixes a bug in MessagingLinearLayout where we would not recompute the height of it after applying the computed insets to the text views, leading to incorrect padding and in rare cases a cut off message at the bottom. Change-Id: If87a527555158e94e501832e9f49e380cc7da2bb Test: runtest -x core/tests/coretests/src/com/android/internal/widget/MessagingLinearLayoutTest.java Fixes: 31463075
-rw-r--r--core/java/com/android/internal/widget/MessagingLinearLayout.java9
-rw-r--r--core/tests/coretests/res/layout/messaging_linear_layout_test.xml25
-rw-r--r--core/tests/coretests/src/com/android/internal/widget/MessagingLinearLayoutTest.java223
3 files changed, 256 insertions, 1 deletions
diff --git a/core/java/com/android/internal/widget/MessagingLinearLayout.java b/core/java/com/android/internal/widget/MessagingLinearLayout.java
index d2a43b755f18..cb123a13ad1d 100644
--- a/core/java/com/android/internal/widget/MessagingLinearLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLinearLayout.java
@@ -126,7 +126,8 @@ public class MessagingLinearLayout extends ViewGroup {
// Pretend we need the image padding for all views, we don't know which
// one will end up needing to do this (might end up not using all the space,
// but calculating this exactly would be more expensive).
- ((ImageFloatingTextView) child).setNumIndentLines(mIndentLines);
+ ((ImageFloatingTextView) child).setNumIndentLines(
+ mIndentLines == 2 ? 3 : mIndentLines);
}
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
@@ -147,6 +148,9 @@ public class MessagingLinearLayout extends ViewGroup {
// Now that we know which views to take, fix up the indents and see what width we get.
int measuredWidth = mPaddingLeft + mPaddingRight;
int imageLines = mIndentLines;
+ // Need to redo the height because it may change due to changing indents.
+ totalHeight = mPaddingTop + mPaddingBottom;
+ first = true;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
@@ -173,6 +177,9 @@ public class MessagingLinearLayout extends ViewGroup {
measuredWidth = Math.max(measuredWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin
+ mPaddingLeft + mPaddingRight);
+ totalHeight = Math.max(totalHeight, totalHeight + child.getMeasuredHeight() +
+ lp.topMargin + lp.bottomMargin + (first ? 0 : mSpacing));
+ first = false;
}
diff --git a/core/tests/coretests/res/layout/messaging_linear_layout_test.xml b/core/tests/coretests/res/layout/messaging_linear_layout_test.xml
new file mode 100644
index 000000000000..8ba3e07da22e
--- /dev/null
+++ b/core/tests/coretests/res/layout/messaging_linear_layout_test.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<com.android.internal.widget.MessagingLinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:maxHeight="300px"
+ android:spacing="5px">
+
+</com.android.internal.widget.MessagingLinearLayout> \ No newline at end of file
diff --git a/core/tests/coretests/src/com/android/internal/widget/MessagingLinearLayoutTest.java b/core/tests/coretests/src/com/android/internal/widget/MessagingLinearLayoutTest.java
new file mode 100644
index 000000000000..75b2c1d5fd2c
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/widget/MessagingLinearLayoutTest.java
@@ -0,0 +1,223 @@
+/*
+ * 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.internal.widget;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.Debug;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.espresso.core.deps.guava.base.Function;
+import android.support.test.filters.SmallTest;
+import android.view.LayoutInflater;
+import android.view.View.MeasureSpec;
+
+import com.android.frameworks.coretests.R;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+
+@SmallTest
+public class MessagingLinearLayoutTest {
+
+ public static final int WIDTH_SPEC = MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY);
+ public static final int HEIGHT_SPEC = MeasureSpec.makeMeasureSpec(400, MeasureSpec.AT_MOST);
+ private Context mContext;
+ private MessagingLinearLayout mView;
+
+ @Before
+ public void setup() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ // maxHeight: 300px
+ // spacing: 50px
+ mView = (MessagingLinearLayout) LayoutInflater.from(mContext).inflate(
+ R.layout.messaging_linear_layout_test, null);
+ }
+
+ @Test
+ public void testSingleChild() {
+ FakeImageFloatingTextView child = fakeChild((i) -> 3);
+
+ mView.setNumIndentLines(2);
+ mView.addView(child);
+
+ mView.measure(WIDTH_SPEC, HEIGHT_SPEC);
+ mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
+
+ assertEquals(3, child.getNumIndentLines());
+ assertFalse(child.isHidden());
+ assertEquals(150, mView.getMeasuredHeight());
+ }
+
+ @Test
+ public void testLargeSmall() {
+ FakeImageFloatingTextView child1 = fakeChild((i) -> 3);
+ FakeImageFloatingTextView child2 = fakeChild((i) -> 1);
+
+ mView.setNumIndentLines(2);
+ mView.addView(child1);
+ mView.addView(child2);
+
+ mView.measure(WIDTH_SPEC, HEIGHT_SPEC);
+ mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
+
+ assertEquals(3, child1.getNumIndentLines());
+ assertEquals(0, child2.getNumIndentLines());
+ assertFalse(child1.isHidden());
+ assertFalse(child2.isHidden());
+ assertEquals(205, mView.getMeasuredHeight());
+ }
+
+ @Test
+ public void testSmallSmall() {
+ FakeImageFloatingTextView child1 = fakeChild((i) -> 1);
+ FakeImageFloatingTextView child2 = fakeChild((i) -> 1);
+
+ mView.setNumIndentLines(2);
+ mView.addView(child1);
+ mView.addView(child2);
+
+ mView.measure(WIDTH_SPEC, HEIGHT_SPEC);
+ mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
+
+ assertEquals(2, child1.getNumIndentLines());
+ assertEquals(1, child2.getNumIndentLines());
+ assertFalse(child1.isHidden());
+ assertFalse(child2.isHidden());
+ assertEquals(105, mView.getMeasuredHeight());
+ }
+
+ @Test
+ public void testLargeLarge() {
+ FakeImageFloatingTextView child1 = fakeChild((i) -> 7);
+ FakeImageFloatingTextView child2 = fakeChild((i) -> 7);
+
+ mView.setNumIndentLines(2);
+ mView.addView(child1);
+ mView.addView(child2);
+
+ mView.measure(WIDTH_SPEC, HEIGHT_SPEC);
+ mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
+
+ assertEquals(3, child2.getNumIndentLines());
+ assertTrue(child1.isHidden());
+ assertFalse(child2.isHidden());
+ assertEquals(300, mView.getMeasuredHeight());
+ }
+
+ @Test
+ public void testLargeSmall_largeWrapsWith3indentbutnot3_andHitsMax() {
+ FakeImageFloatingTextView child1 = fakeChild((i) -> i > 2 ? 5 : 4);
+ FakeImageFloatingTextView child2 = fakeChild((i) -> 1);
+
+ mView.setNumIndentLines(2);
+ mView.addView(child1);
+ mView.addView(child2);
+
+ mView.measure(WIDTH_SPEC, HEIGHT_SPEC);
+ mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
+
+ assertTrue(child1.isHidden());
+ assertFalse(child2.isHidden());
+ assertEquals(50, mView.getMeasuredHeight());
+ assertEquals(2, child2.getNumIndentLines());
+ }
+
+ @Test
+ public void testLargeSmall_largeWrapsWith3indentbutnot3() {
+ FakeImageFloatingTextView child1 = fakeChild((i) -> i > 2 ? 4 : 3);
+ FakeImageFloatingTextView child2 = fakeChild((i) -> 1);
+
+ mView.setNumIndentLines(2);
+ mView.addView(child1);
+ mView.addView(child2);
+
+ mView.measure(WIDTH_SPEC, HEIGHT_SPEC);
+ mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
+
+ assertFalse(child1.isHidden());
+ assertFalse(child2.isHidden());
+ assertEquals(255, mView.getMeasuredHeight());
+ assertEquals(3, child1.getNumIndentLines());
+ assertEquals(0, child2.getNumIndentLines());
+ }
+
+ private class FakeImageFloatingTextView extends ImageFloatingTextView {
+
+ public static final int LINE_HEIGHT = 50;
+ private final Function<Integer, Integer> mLinesForIndent;
+ private int mNumIndentLines;
+
+ public FakeImageFloatingTextView(Context context,
+ Function<Integer, Integer> linesForIndent) {
+ super(context, null, 0, 0);
+ mLinesForIndent = linesForIndent;
+ }
+
+ @Override
+ public boolean setNumIndentLines(int lines) {
+ boolean changed = (mNumIndentLines != lines);
+ mNumIndentLines = lines;
+ return changed;
+ }
+
+ public int getNumIndentLines() {
+ return mNumIndentLines;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ setMeasuredDimension(
+ getDefaultSize(500, widthMeasureSpec),
+ resolveSize(getDesiredHeight(), heightMeasureSpec));
+ }
+
+ @Override
+ public int getLineCount() {
+ return mLinesForIndent.apply(mNumIndentLines);
+ }
+
+ public int getDesiredHeight() {
+ return LINE_HEIGHT * getLineCount();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ // swallow
+ }
+
+ public boolean isHidden() {
+ MessagingLinearLayout.LayoutParams lp =
+ (MessagingLinearLayout.LayoutParams) getLayoutParams();
+ try {
+ Field hide = MessagingLinearLayout.LayoutParams.class.getDeclaredField("hide");
+ hide.setAccessible(true);
+ return hide.getBoolean(lp);
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private FakeImageFloatingTextView fakeChild(Function<Integer,Integer> linesForIndent) {
+ return new FakeImageFloatingTextView(mContext, linesForIndent);
+ }
+}