Fix split_shade_status_bar height to avoid layout on each animation
frame This happens because MotionLayout with height==WRAP_CONTENT updates it's height when transitioning from one state to another. Given the fact that below content assumes the final height of the split_shade_status_bar being around 120dp brightness slider accordingly Test: manual on foldable. Verified screenshots before and after Test: atest NotificationsQSContainerControllerTest Fixes: 267125811 Change-Id: I1332f4c0115366afdafdd0eb17e01153216d253c
This commit is contained in:
@@ -14,12 +14,17 @@
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<!--
|
||||
keep split_shade_status_bar height constant to avoid requestLayout calls on each
|
||||
frame when animating QS <-> QQS transition
|
||||
-->
|
||||
<com.android.systemui.util.NoRemeasureMotionLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/split_shade_status_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="@dimen/qs_header_height"
|
||||
android:minHeight="@dimen/large_screen_shade_header_min_height"
|
||||
android:clickable="false"
|
||||
android:focusable="true"
|
||||
@@ -29,6 +34,14 @@
|
||||
android:theme="@style/Theme.SystemUI.QuickSettings.Header"
|
||||
app:layoutDescription="@xml/combined_qs_header_scene">
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/qqs_header_bottom_guideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_begin="@dimen/large_screen_shade_header_min_height"
|
||||
/>
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -615,6 +615,7 @@
|
||||
<dimen name="qs_header_carrier_separator_width">6dp</dimen>
|
||||
<dimen name="qs_carrier_margin_width">4dp</dimen>
|
||||
<dimen name="qs_footer_icon_size">20dp</dimen>
|
||||
<dimen name="qs_header_height">120dp</dimen>
|
||||
<dimen name="qs_header_row_min_height">48dp</dimen>
|
||||
|
||||
<dimen name="qs_header_non_clickable_element_height">24dp</dimen>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
android:layout_height="@dimen/large_screen_shade_header_min_height"
|
||||
app:layout_constraintStart_toStartOf="@id/begin_guide"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="@id/qqs_header_bottom_guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/date"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
@@ -62,7 +62,7 @@
|
||||
app:layout_constraintStart_toEndOf="@id/date"
|
||||
app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="@id/qqs_header_bottom_guideline"
|
||||
app:layout_constraintHorizontal_bias="1"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
/>
|
||||
@@ -77,7 +77,7 @@
|
||||
app:layout_constraintStart_toEndOf="@id/statusIcons"
|
||||
app:layout_constraintEnd_toEndOf="@id/end_guide"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="@id/qqs_header_bottom_guideline"
|
||||
app:layout_constraintHorizontal_bias="1"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
/>
|
||||
@@ -105,7 +105,7 @@
|
||||
app:layout_constraintStart_toEndOf="@id/date"
|
||||
app:layout_constraintEnd_toEndOf="@id/end_guide"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="@id/qqs_header_bottom_guideline"
|
||||
app:layout_constraintHorizontal_bias="1"
|
||||
/>
|
||||
</Constraint>
|
||||
|
||||
@@ -18,7 +18,6 @@ package com.android.systemui.shade
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
import android.view.WindowInsets
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.constraintlayout.widget.ConstraintSet
|
||||
@@ -62,6 +61,7 @@ class NotificationsQSContainerController @Inject constructor(
|
||||
private var isQSCustomizing = false
|
||||
private var isQSCustomizerAnimating = false
|
||||
|
||||
private var shadeHeaderHeight = 0
|
||||
private var largeScreenShadeHeaderHeight = 0
|
||||
private var largeScreenShadeHeaderActive = false
|
||||
private var notificationsBottomMargin = 0
|
||||
@@ -146,6 +146,8 @@ class NotificationsQSContainerController @Inject constructor(
|
||||
R.dimen.notification_panel_margin_bottom)
|
||||
largeScreenShadeHeaderHeight =
|
||||
resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height)
|
||||
shadeHeaderHeight =
|
||||
resources.getDimensionPixelSize(R.dimen.qs_header_height)
|
||||
panelMarginHorizontal = resources.getDimensionPixelSize(
|
||||
R.dimen.notification_panel_margin_horizontal)
|
||||
topMargin = if (largeScreenShadeHeaderActive) {
|
||||
@@ -245,7 +247,7 @@ class NotificationsQSContainerController @Inject constructor(
|
||||
if (largeScreenShadeHeaderActive) {
|
||||
constraintSet.constrainHeight(R.id.split_shade_status_bar, largeScreenShadeHeaderHeight)
|
||||
} else {
|
||||
constraintSet.constrainHeight(R.id.split_shade_status_bar, WRAP_CONTENT)
|
||||
constraintSet.constrainHeight(R.id.split_shade_status_bar, shadeHeaderHeight)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.systemui.shade
|
||||
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
import android.testing.TestableResources
|
||||
import androidx.constraintlayout.widget.ConstraintSet
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.fragments.FragmentService
|
||||
import com.android.systemui.navigationbar.NavigationModeController
|
||||
import com.android.systemui.recents.OverviewProxyService
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||
import com.android.systemui.util.concurrency.FakeExecutor
|
||||
import com.android.systemui.util.mockito.capture
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@TestableLooper.RunWithLooper
|
||||
@SmallTest
|
||||
class NotificationsQSContainerControllerTest : SysuiTestCase() {
|
||||
|
||||
@Mock lateinit var view: NotificationsQuickSettingsContainer
|
||||
@Mock lateinit var navigationModeController: NavigationModeController
|
||||
@Mock lateinit var overviewProxyService: OverviewProxyService
|
||||
@Mock lateinit var shadeHeaderController: ShadeHeaderController
|
||||
@Mock lateinit var shadeExpansionStateManager: ShadeExpansionStateManager
|
||||
@Mock lateinit var fragmentService: FragmentService
|
||||
|
||||
lateinit var underTest: NotificationsQSContainerController
|
||||
|
||||
private lateinit var fakeResources: TestableResources
|
||||
|
||||
private val delayableExecutor: DelayableExecutor = FakeExecutor(FakeSystemClock())
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
fakeResources = TestableResources(context.resources)
|
||||
|
||||
whenever(view.resources).thenReturn(fakeResources.resources)
|
||||
|
||||
underTest =
|
||||
NotificationsQSContainerController(
|
||||
view,
|
||||
navigationModeController,
|
||||
overviewProxyService,
|
||||
shadeHeaderController,
|
||||
shadeExpansionStateManager,
|
||||
fragmentService,
|
||||
delayableExecutor,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSmallScreen_updateResources_splitShadeHeightIsSet() {
|
||||
with(fakeResources) {
|
||||
addOverride(R.bool.config_use_large_screen_shade_header, false)
|
||||
addOverride(R.dimen.qs_header_height, 1)
|
||||
addOverride(R.dimen.large_screen_shade_header_height, 2)
|
||||
}
|
||||
|
||||
underTest.updateResources()
|
||||
|
||||
val captor = ArgumentCaptor.forClass(ConstraintSet::class.java)
|
||||
verify(view).applyConstraints(capture(captor))
|
||||
assertThat(captor.value.getHeight(R.id.split_shade_status_bar)).isEqualTo(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLargeScreen_updateResources_splitShadeHeightIsSet() {
|
||||
with(fakeResources) {
|
||||
addOverride(R.bool.config_use_large_screen_shade_header, true)
|
||||
addOverride(R.dimen.qs_header_height, 1)
|
||||
addOverride(R.dimen.large_screen_shade_header_height, 2)
|
||||
}
|
||||
|
||||
underTest.updateResources()
|
||||
|
||||
val captor = ArgumentCaptor.forClass(ConstraintSet::class.java)
|
||||
verify(view).applyConstraints(capture(captor))
|
||||
assertThat(captor.value.getHeight(R.id.split_shade_status_bar)).isEqualTo(2)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user