From b43685b77da9ef17221e8c2623f25754223d9d6e Mon Sep 17 00:00:00 2001 From: Lucas Dupin Date: Mon, 25 Oct 2021 16:15:50 -0700 Subject: [PATCH] Do not relayout when expanding shade Going from VISIBLE to GONE requires a relayout. We should avoid it whenever possible, given that we'll have to scan through all views and it's fairly expansive. Making the view INVISIBLE instead of GONE mitigates the issue. Test: manually pull down the shade Bug: 199047412 Change-Id: I8a454be25059dfa1254e44a945faa1a546d6af99 --- .../phone/SplitShadeHeaderController.kt | 30 +++++-- .../phone/SplitShadeHeaderControllerTest.kt | 87 +++++++++++++++++++ 2 files changed, 111 insertions(+), 6 deletions(-) create mode 100644 packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt index a7ecd0619d266..8732891301398 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt @@ -41,15 +41,28 @@ class SplitShadeHeaderController @Inject constructor( private val iconManager: StatusBarIconController.IconManager private val qsCarrierGroupController: QSCarrierGroupController private var visible = false + set(value) { + if (field == value) { + return + } + field = value + updateListeners() + } var shadeExpanded = false set(value) { + if (field == value) { + return + } field = value updateVisibility() } var splitShadeMode = false set(value) { + if (field == value) { + return + } field = value updateVisibility() } @@ -78,15 +91,20 @@ class SplitShadeHeaderController @Inject constructor( } private fun updateVisibility() { - val shouldBeVisible = shadeExpanded && splitShadeMode - if (visible != shouldBeVisible) { - visible = shouldBeVisible - statusBar.visibility = if (shouldBeVisible) View.VISIBLE else View.GONE - updateListeners(shouldBeVisible) + val visibility = if (!splitShadeMode) { + View.GONE + } else if (shadeExpanded) { + View.VISIBLE + } else { + View.INVISIBLE + } + if (statusBar.visibility != visibility) { + statusBar.visibility = visibility + visible = visibility == View.VISIBLE } } - private fun updateListeners(visible: Boolean) { + private fun updateListeners() { qsCarrierGroupController.setListening(visible) if (visible) { statusBarIconController.addIconGroup(iconManager) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt new file mode 100644 index 0000000000000..a9e8164e8b873 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt @@ -0,0 +1,87 @@ +package com.android.systemui.statusbar.phone + +import android.content.Context +import android.content.res.Resources +import android.test.suitebuilder.annotation.SmallTest +import android.view.View +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.ShadeInterpolation +import com.android.systemui.battery.BatteryMeterView +import com.android.systemui.battery.BatteryMeterViewController +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.qs.carrier.QSCarrierGroupController +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mock +import org.mockito.Mockito.`when` as whenever +import org.mockito.Mockito.verify +import org.mockito.junit.MockitoJUnit + +@SmallTest +class SplitShadeHeaderControllerTest : SysuiTestCase() { + + @Mock private lateinit var view: View + @Mock private lateinit var statusIcons: StatusIconContainer + @Mock private lateinit var statusBarIconController: StatusBarIconController + @Mock private lateinit var qsCarrierGroupController: QSCarrierGroupController + @Mock private lateinit var qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder + @Mock private lateinit var featureFlags: FeatureFlags + @Mock private lateinit var batteryMeterView: BatteryMeterView + @Mock private lateinit var batteryMeterViewController: BatteryMeterViewController + @Mock private lateinit var resources: Resources + @Mock private lateinit var context: Context + @JvmField @Rule val mockitoRule = MockitoJUnit.rule() + var viewVisibility = View.GONE + + private lateinit var splitShadeHeaderController: SplitShadeHeaderController + + @Before + fun setup() { + whenever(view.findViewById(R.id.batteryRemainingIcon)) + .thenReturn(batteryMeterView) + whenever(view.findViewById(R.id.statusIcons)).thenReturn(statusIcons) + whenever(statusIcons.context).thenReturn(context) + whenever(context.resources).thenReturn(resources) + whenever(qsCarrierGroupControllerBuilder.setQSCarrierGroup(any())) + .thenReturn(qsCarrierGroupControllerBuilder) + whenever(qsCarrierGroupControllerBuilder.build()).thenReturn(qsCarrierGroupController) + whenever(view.setVisibility(anyInt())).then { + viewVisibility = it.arguments[0] as Int + null + } + whenever(view.visibility).thenAnswer { _ -> viewVisibility } + splitShadeHeaderController = SplitShadeHeaderController(view, statusBarIconController, + qsCarrierGroupControllerBuilder, featureFlags, batteryMeterViewController) + } + + @Test + fun setVisible_onlyInSplitShade() { + splitShadeHeaderController.splitShadeMode = true + splitShadeHeaderController.shadeExpanded = true + assertThat(viewVisibility).isEqualTo(View.VISIBLE) + + splitShadeHeaderController.splitShadeMode = false + assertThat(viewVisibility).isEqualTo(View.GONE) + } + + @Test + fun updateListeners_registersWhenVisible() { + splitShadeHeaderController.splitShadeMode = true + splitShadeHeaderController.shadeExpanded = true + verify(qsCarrierGroupController).setListening(true) + verify(statusBarIconController).addIconGroup(any()) + } + + @Test + fun shadeExpandedFraction_updatesAlpha() { + splitShadeHeaderController.splitShadeMode = true + splitShadeHeaderController.shadeExpanded = true + splitShadeHeaderController.shadeExpandedFraction = 0.5f + verify(view).setAlpha(ShadeInterpolation.getContentAlpha(0.5f)) + } +} \ No newline at end of file