Merge "Fix split_shade_status_bar height to avoid layout on each animation frame" into udc-dev am: 7db06f8503

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/22645423

Change-Id: Ide8c48bc1d1b2ff7473bc6377465e28f03b61e93
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Anton Potapov
2023-05-03 10:21:39 +00:00
committed by Automerger Merge Worker
5 changed files with 132 additions and 7 deletions

View File

@@ -14,12 +14,17 @@
~ See the License for the specific language governing permissions and ~ See the License for the specific language governing permissions and
~ limitations under the License. ~ 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 <com.android.systemui.util.NoRemeasureMotionLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/split_shade_status_bar" android:id="@+id/split_shade_status_bar"
android:layout_width="match_parent" 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:minHeight="@dimen/large_screen_shade_header_min_height"
android:clickable="false" android:clickable="false"
android:focusable="true" android:focusable="true"
@@ -29,6 +34,14 @@
android:theme="@style/Theme.SystemUI.QuickSettings.Header" android:theme="@style/Theme.SystemUI.QuickSettings.Header"
app:layoutDescription="@xml/combined_qs_header_scene"> 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 <androidx.constraintlayout.widget.Guideline
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@@ -615,6 +615,7 @@
<dimen name="qs_header_carrier_separator_width">6dp</dimen> <dimen name="qs_header_carrier_separator_width">6dp</dimen>
<dimen name="qs_carrier_margin_width">4dp</dimen> <dimen name="qs_carrier_margin_width">4dp</dimen>
<dimen name="qs_footer_icon_size">20dp</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_row_min_height">48dp</dimen>
<dimen name="qs_header_non_clickable_element_height">24dp</dimen> <dimen name="qs_header_non_clickable_element_height">24dp</dimen>

View File

@@ -28,7 +28,7 @@
android:layout_height="@dimen/large_screen_shade_header_min_height" android:layout_height="@dimen/large_screen_shade_header_min_height"
app:layout_constraintStart_toStartOf="@id/begin_guide" app:layout_constraintStart_toStartOf="@id/begin_guide"
app:layout_constraintTop_toTopOf="parent" 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_constraintEnd_toStartOf="@id/date"
app:layout_constraintHorizontal_bias="0" app:layout_constraintHorizontal_bias="0"
app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintHorizontal_chainStyle="packed"
@@ -62,7 +62,7 @@
app:layout_constraintStart_toEndOf="@id/date" app:layout_constraintStart_toEndOf="@id/date"
app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon" app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon"
app:layout_constraintTop_toTopOf="parent" 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_bias="1"
app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintHorizontal_chainStyle="packed"
/> />
@@ -77,7 +77,7 @@
app:layout_constraintStart_toEndOf="@id/statusIcons" app:layout_constraintStart_toEndOf="@id/statusIcons"
app:layout_constraintEnd_toEndOf="@id/end_guide" app:layout_constraintEnd_toEndOf="@id/end_guide"
app:layout_constraintTop_toTopOf="parent" 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_bias="1"
app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintHorizontal_chainStyle="packed"
/> />
@@ -105,7 +105,7 @@
app:layout_constraintStart_toEndOf="@id/date" app:layout_constraintStart_toEndOf="@id/date"
app:layout_constraintEnd_toEndOf="@id/end_guide" app:layout_constraintEnd_toEndOf="@id/end_guide"
app:layout_constraintTop_toTopOf="parent" 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_bias="1"
/> />
</Constraint> </Constraint>

View File

@@ -18,7 +18,6 @@ package com.android.systemui.shade
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.WindowInsets import android.view.WindowInsets
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import androidx.constraintlayout.widget.ConstraintSet import androidx.constraintlayout.widget.ConstraintSet
@@ -62,6 +61,7 @@ class NotificationsQSContainerController @Inject constructor(
private var isQSCustomizing = false private var isQSCustomizing = false
private var isQSCustomizerAnimating = false private var isQSCustomizerAnimating = false
private var shadeHeaderHeight = 0
private var largeScreenShadeHeaderHeight = 0 private var largeScreenShadeHeaderHeight = 0
private var largeScreenShadeHeaderActive = false private var largeScreenShadeHeaderActive = false
private var notificationsBottomMargin = 0 private var notificationsBottomMargin = 0
@@ -146,6 +146,8 @@ class NotificationsQSContainerController @Inject constructor(
R.dimen.notification_panel_margin_bottom) R.dimen.notification_panel_margin_bottom)
largeScreenShadeHeaderHeight = largeScreenShadeHeaderHeight =
resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height) resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height)
shadeHeaderHeight =
resources.getDimensionPixelSize(R.dimen.qs_header_height)
panelMarginHorizontal = resources.getDimensionPixelSize( panelMarginHorizontal = resources.getDimensionPixelSize(
R.dimen.notification_panel_margin_horizontal) R.dimen.notification_panel_margin_horizontal)
topMargin = if (largeScreenShadeHeaderActive) { topMargin = if (largeScreenShadeHeaderActive) {
@@ -245,7 +247,7 @@ class NotificationsQSContainerController @Inject constructor(
if (largeScreenShadeHeaderActive) { if (largeScreenShadeHeaderActive) {
constraintSet.constrainHeight(R.id.split_shade_status_bar, largeScreenShadeHeaderHeight) constraintSet.constrainHeight(R.id.split_shade_status_bar, largeScreenShadeHeaderHeight)
} else { } else {
constraintSet.constrainHeight(R.id.split_shade_status_bar, WRAP_CONTENT) constraintSet.constrainHeight(R.id.split_shade_status_bar, shadeHeaderHeight)
} }
} }

View File

@@ -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)
}
}