From 84ffe9f5080bf4352615af1a6cfd0bc82f6cdca1 Mon Sep 17 00:00:00 2001 From: Fabian Kozynski Date: Thu, 9 Jun 2022 14:48:24 -0400 Subject: [PATCH] Fix clipping coordinates on RTL In RTL in landscape, the container for QS and footer was clipping incorrectly, as it should use its coordinates (not its parent's) for clipping its children. Test: manual Test: atest QSPanelTest, test that views are visible to user Fixes: 235422445 Change-Id: Idf29a3f17128591eaa3405058ae16d5b09a51f43 --- .../src/com/android/systemui/qs/QSPanel.java | 10 +- .../com/android/systemui/qs/QSPanelTest.kt | 119 +++++++++++++++--- 2 files changed, 105 insertions(+), 24 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 9de132f64d0bb..41724ef62683c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -151,15 +151,17 @@ public class QSPanel extends LinearLayout implements Tunable { mHorizontalContentContainer.setClipChildren(true); mHorizontalContentContainer.setClipToPadding(false); // Don't clip on the top, that way, secondary pages tiles can animate up + // Clipping coordinates should be relative to this view, not absolute (parent coordinates) mHorizontalContentContainer.addOnLayoutChangeListener( (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { - if (left != oldLeft || right != oldRight || bottom != oldBottom) { - mClippingRect.left = left; - mClippingRect.right = right; - mClippingRect.bottom = bottom; + if ((right - left) != (oldRight - oldLeft) + || ((bottom - top) != (oldBottom - oldTop))) { + mClippingRect.right = right - left; + mClippingRect.bottom = bottom - top; mHorizontalContentContainer.setClipBounds(mClippingRect); } }); + mClippingRect.left = 0; mClippingRect.top = -1000; mHorizontalContentContainer.setClipBounds(mClippingRect); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt index e237a5ce03fe5..60cfd7249919e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt @@ -13,18 +13,24 @@ */ package com.android.systemui.qs +import android.graphics.Rect import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper +import android.testing.ViewUtils import android.view.View -import android.view.ViewGroup +import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.accessibility.AccessibilityNodeInfo import android.widget.FrameLayout import android.widget.LinearLayout import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.plugins.qs.QSTile +import com.android.systemui.qs.tileimpl.QSIconViewImpl +import com.android.systemui.qs.tileimpl.QSTileViewImpl import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -36,37 +42,40 @@ import org.mockito.MockitoAnnotations @RunWithLooper @SmallTest class QSPanelTest : SysuiTestCase() { - private lateinit var mTestableLooper: TestableLooper - private lateinit var mQsPanel: QSPanel + private lateinit var testableLooper: TestableLooper + private lateinit var qsPanel: QSPanel - private lateinit var mParentView: ViewGroup - - private lateinit var mFooter: View + private lateinit var footer: View @Before @Throws(Exception::class) fun setup() { MockitoAnnotations.initMocks(this) - mTestableLooper = TestableLooper.get(this) + testableLooper = TestableLooper.get(this) - mTestableLooper.runWithLooper { - mQsPanel = QSPanel(mContext, null) - mQsPanel.initialize() + testableLooper.runWithLooper { + qsPanel = QSPanel(context, null) + qsPanel.mUsingMediaPlayer = true + + qsPanel.initialize() // QSPanel inflates a footer inside of it, mocking it here - mFooter = LinearLayout(mContext).apply { id = R.id.qs_footer } - mQsPanel.addView(mFooter) - mQsPanel.onFinishInflate() + footer = LinearLayout(context).apply { id = R.id.qs_footer } + qsPanel.addView(footer, MATCH_PARENT, 100) + qsPanel.onFinishInflate() // Provides a parent with non-zero size for QSPanel - mParentView = FrameLayout(mContext).apply { - addView(mQsPanel) - } + ViewUtils.attachView(qsPanel) } } + @After + fun tearDown() { + ViewUtils.detachView(qsPanel) + } + @Test fun testHasCollapseAccessibilityAction() { - val info = AccessibilityNodeInfo(mQsPanel) - mQsPanel.onInitializeAccessibilityNodeInfo(info) + val info = AccessibilityNodeInfo(qsPanel) + qsPanel.onInitializeAccessibilityNodeInfo(info) assertThat(info.actions and AccessibilityNodeInfo.ACTION_COLLAPSE).isNotEqualTo(0) assertThat(info.actions and AccessibilityNodeInfo.ACTION_EXPAND).isEqualTo(0) @@ -75,9 +84,79 @@ class QSPanelTest : SysuiTestCase() { @Test fun testCollapseActionCallsRunnable() { val mockRunnable = mock(Runnable::class.java) - mQsPanel.setCollapseExpandAction(mockRunnable) + qsPanel.setCollapseExpandAction(mockRunnable) - mQsPanel.performAccessibilityAction(AccessibilityNodeInfo.ACTION_COLLAPSE, null) + qsPanel.performAccessibilityAction(AccessibilityNodeInfo.ACTION_COLLAPSE, null) verify(mockRunnable).run() } + + @Test + fun testTilesFooterVisibleRTLLandscapeMedia() { + qsPanel.layoutDirection = View.LAYOUT_DIRECTION_RTL + // We need at least a tile so the layout has a height + qsPanel.tileLayout?.addTile( + QSPanelControllerBase.TileRecord( + mock(QSTile::class.java), + QSTileViewImpl(context, QSIconViewImpl(context)) + ) + ) + + val mediaView = FrameLayout(context) + mediaView.addView(View(context), MATCH_PARENT, 800) + + qsPanel.setUsingHorizontalLayout(/* horizontal */ true, mediaView, /* force */ true) + qsPanel.measure( + /* width */ View.MeasureSpec.makeMeasureSpec(3000, View.MeasureSpec.EXACTLY), + /* height */ View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY) + ) + qsPanel.layout(0, 0, qsPanel.measuredWidth, qsPanel.measuredHeight) + + val tiles = qsPanel.tileLayout as View + // Tiles are effectively to the right of media + assertThat(mediaView isLeftOf tiles) + assertThat(tiles.isVisibleToUser).isTrue() + + assertThat(mediaView isLeftOf footer) + assertThat(footer.isVisibleToUser).isTrue() + } + + @Test + fun testTilesFooterVisibleLandscapeMedia() { + qsPanel.layoutDirection = View.LAYOUT_DIRECTION_LTR + // We need at least a tile so the layout has a height + qsPanel.tileLayout?.addTile( + QSPanelControllerBase.TileRecord( + mock(QSTile::class.java), + QSTileViewImpl(context, QSIconViewImpl(context)) + ) + ) + + val mediaView = FrameLayout(context) + mediaView.addView(View(context), MATCH_PARENT, 800) + + qsPanel.setUsingHorizontalLayout(/* horizontal */ true, mediaView, /* force */ true) + qsPanel.measure( + /* width */ View.MeasureSpec.makeMeasureSpec(3000, View.MeasureSpec.EXACTLY), + /* height */ View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY) + ) + qsPanel.layout(0, 0, qsPanel.measuredWidth, qsPanel.measuredHeight) + + val tiles = qsPanel.tileLayout as View + // Tiles are effectively to the left of media + assertThat(tiles isLeftOf mediaView) + assertThat(tiles.isVisibleToUser).isTrue() + + assertThat(footer isLeftOf mediaView) + assertThat(footer.isVisibleToUser).isTrue() + } + + private infix fun View.isLeftOf(other: View): Boolean { + val rect = Rect() + getBoundsOnScreen(rect) + val thisRight = rect.right + + other.getBoundsOnScreen(rect) + + return thisRight <= rect.left + } }