Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt +32 −7 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ 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 import androidx.constraintlayout.widget.ConstraintSet.BOTTOM import androidx.constraintlayout.widget.ConstraintSet.END Loading @@ -11,6 +12,7 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP import com.android.systemui.R import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.navigationbar.NavigationModeController Loading @@ -21,14 +23,19 @@ import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener import com.android.systemui.shared.system.QuickStepContract import com.android.systemui.util.Utils import com.android.systemui.util.ViewController import com.android.systemui.util.concurrency.DelayableExecutor import java.util.function.Consumer import javax.inject.Inject @VisibleForTesting internal const val INSET_DEBOUNCE_MILLIS = 500L class NotificationsQSContainerController @Inject constructor( view: NotificationsQuickSettingsContainer, private val navigationModeController: NavigationModeController, private val overviewProxyService: OverviewProxyService, private val featureFlags: FeatureFlags private val featureFlags: FeatureFlags, @Main private val delayableExecutor: DelayableExecutor ) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController { var qsExpanded = false Loading Loading @@ -60,12 +67,30 @@ class NotificationsQSContainerController @Inject constructor( taskbarVisible = visible } } private val windowInsetsListener: Consumer<WindowInsets> = Consumer { insets -> // With certain configuration changes (like light/dark changes), the nav bar will disappear // for a bit, causing `bottomStableInsets` to be unstable for some time. Debounce the value // for 500ms. // All interactions with this object happen in the main thread. private val delayedInsetSetter = object : Runnable, Consumer<WindowInsets> { private var canceller: Runnable? = null private var stableInsets = 0 private var cutoutInsets = 0 override fun accept(insets: WindowInsets) { // when taskbar is visible, stableInsetBottom will include its height bottomStableInsets = insets.stableInsetBottom bottomCutoutInsets = insets.displayCutout?.safeInsetBottom ?: 0 stableInsets = insets.stableInsetBottom cutoutInsets = insets.displayCutout?.safeInsetBottom ?: 0 canceller?.run() canceller = delayableExecutor.executeDelayed(this, INSET_DEBOUNCE_MILLIS) } override fun run() { bottomStableInsets = stableInsets bottomCutoutInsets = cutoutInsets updateBottomSpacing() } } override fun onInit() { val currentMode: Int = navigationModeController.addListener { mode: Int -> Loading @@ -77,7 +102,7 @@ class NotificationsQSContainerController @Inject constructor( public override fun onViewAttached() { updateResources() overviewProxyService.addCallback(taskbarVisibilityListener) mView.setInsetsChangedListener(windowInsetsListener) mView.setInsetsChangedListener(delayedInsetSetter) mView.setQSFragmentAttachedListener { qs: QS -> qs.setContainerController(this) } mView.setConfigurationChangedListener { updateResources() } } Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt +35 −3 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ import com.android.systemui.navigationbar.NavigationModeController import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener import com.android.systemui.recents.OverviewProxyService import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test Loading @@ -31,6 +33,7 @@ import org.mockito.Mockito.anyInt import org.mockito.Mockito.doNothing import org.mockito.Mockito.eq import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import java.util.function.Consumer Loading Loading @@ -71,6 +74,8 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { private lateinit var navigationModeCallback: ModeChangedListener private lateinit var taskbarVisibilityCallback: OverviewProxyListener private lateinit var windowInsetsCallback: Consumer<WindowInsets> private lateinit var delayableExecutor: FakeExecutor private lateinit var fakeSystemClock: FakeSystemClock @Before fun setup() { Loading @@ -78,11 +83,14 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { mContext.ensureTestableResources() whenever(notificationsQSContainer.context).thenReturn(mContext) whenever(notificationsQSContainer.resources).thenReturn(mContext.resources) fakeSystemClock = FakeSystemClock() delayableExecutor = FakeExecutor(fakeSystemClock) controller = NotificationsQSContainerController( notificationsQSContainer, navigationModeController, overviewProxyService, featureFlags featureFlags, delayableExecutor ) overrideResource(R.dimen.split_shade_notifications_scrim_margin_bottom, SCRIM_MARGIN) Loading Loading @@ -490,13 +498,32 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { container.addView(newViewWithId(1)) container.addView(newViewWithId(View.NO_ID)) val controller = NotificationsQSContainerController(container, navigationModeController, overviewProxyService, featureFlags) overviewProxyService, featureFlags, delayableExecutor) controller.updateResources() assertThat(container.getChildAt(0).id).isEqualTo(1) assertThat(container.getChildAt(1).id).isNotEqualTo(View.NO_ID) } @Test fun testWindowInsetDebounce() { disableSplitShade() useNewFooter(true) given(taskbarVisible = false, navigationMode = GESTURES_NAVIGATION, insets = emptyInsets(), applyImmediately = false) fakeSystemClock.advanceTime(INSET_DEBOUNCE_MILLIS / 2) windowInsetsCallback.accept(windowInsets().withStableBottom()) delayableExecutor.advanceClockToLast() delayableExecutor.runAllReady() verify(notificationsQSContainer, never()).setQSContainerPaddingBottom(0) verify(notificationsQSContainer).setQSContainerPaddingBottom(STABLE_INSET_BOTTOM) } private fun disableSplitShade() { setSplitShadeEnabled(false) } Loading @@ -513,12 +540,17 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { private fun given( taskbarVisible: Boolean, navigationMode: Int, insets: WindowInsets insets: WindowInsets, applyImmediately: Boolean = true ) { Mockito.clearInvocations(notificationsQSContainer) taskbarVisibilityCallback.onTaskbarStatusUpdated(taskbarVisible, false) navigationModeCallback.onNavigationModeChanged(navigationMode) windowInsetsCallback.accept(insets) if (applyImmediately) { delayableExecutor.advanceClockToLast() delayableExecutor.runAllReady() } } fun then( Loading Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt +32 −7 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ 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 import androidx.constraintlayout.widget.ConstraintSet.BOTTOM import androidx.constraintlayout.widget.ConstraintSet.END Loading @@ -11,6 +12,7 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP import com.android.systemui.R import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.navigationbar.NavigationModeController Loading @@ -21,14 +23,19 @@ import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener import com.android.systemui.shared.system.QuickStepContract import com.android.systemui.util.Utils import com.android.systemui.util.ViewController import com.android.systemui.util.concurrency.DelayableExecutor import java.util.function.Consumer import javax.inject.Inject @VisibleForTesting internal const val INSET_DEBOUNCE_MILLIS = 500L class NotificationsQSContainerController @Inject constructor( view: NotificationsQuickSettingsContainer, private val navigationModeController: NavigationModeController, private val overviewProxyService: OverviewProxyService, private val featureFlags: FeatureFlags private val featureFlags: FeatureFlags, @Main private val delayableExecutor: DelayableExecutor ) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController { var qsExpanded = false Loading Loading @@ -60,12 +67,30 @@ class NotificationsQSContainerController @Inject constructor( taskbarVisible = visible } } private val windowInsetsListener: Consumer<WindowInsets> = Consumer { insets -> // With certain configuration changes (like light/dark changes), the nav bar will disappear // for a bit, causing `bottomStableInsets` to be unstable for some time. Debounce the value // for 500ms. // All interactions with this object happen in the main thread. private val delayedInsetSetter = object : Runnable, Consumer<WindowInsets> { private var canceller: Runnable? = null private var stableInsets = 0 private var cutoutInsets = 0 override fun accept(insets: WindowInsets) { // when taskbar is visible, stableInsetBottom will include its height bottomStableInsets = insets.stableInsetBottom bottomCutoutInsets = insets.displayCutout?.safeInsetBottom ?: 0 stableInsets = insets.stableInsetBottom cutoutInsets = insets.displayCutout?.safeInsetBottom ?: 0 canceller?.run() canceller = delayableExecutor.executeDelayed(this, INSET_DEBOUNCE_MILLIS) } override fun run() { bottomStableInsets = stableInsets bottomCutoutInsets = cutoutInsets updateBottomSpacing() } } override fun onInit() { val currentMode: Int = navigationModeController.addListener { mode: Int -> Loading @@ -77,7 +102,7 @@ class NotificationsQSContainerController @Inject constructor( public override fun onViewAttached() { updateResources() overviewProxyService.addCallback(taskbarVisibilityListener) mView.setInsetsChangedListener(windowInsetsListener) mView.setInsetsChangedListener(delayedInsetSetter) mView.setQSFragmentAttachedListener { qs: QS -> qs.setContainerController(this) } mView.setConfigurationChangedListener { updateResources() } } Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt +35 −3 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ import com.android.systemui.navigationbar.NavigationModeController import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener import com.android.systemui.recents.OverviewProxyService import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test Loading @@ -31,6 +33,7 @@ import org.mockito.Mockito.anyInt import org.mockito.Mockito.doNothing import org.mockito.Mockito.eq import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import java.util.function.Consumer Loading Loading @@ -71,6 +74,8 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { private lateinit var navigationModeCallback: ModeChangedListener private lateinit var taskbarVisibilityCallback: OverviewProxyListener private lateinit var windowInsetsCallback: Consumer<WindowInsets> private lateinit var delayableExecutor: FakeExecutor private lateinit var fakeSystemClock: FakeSystemClock @Before fun setup() { Loading @@ -78,11 +83,14 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { mContext.ensureTestableResources() whenever(notificationsQSContainer.context).thenReturn(mContext) whenever(notificationsQSContainer.resources).thenReturn(mContext.resources) fakeSystemClock = FakeSystemClock() delayableExecutor = FakeExecutor(fakeSystemClock) controller = NotificationsQSContainerController( notificationsQSContainer, navigationModeController, overviewProxyService, featureFlags featureFlags, delayableExecutor ) overrideResource(R.dimen.split_shade_notifications_scrim_margin_bottom, SCRIM_MARGIN) Loading Loading @@ -490,13 +498,32 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { container.addView(newViewWithId(1)) container.addView(newViewWithId(View.NO_ID)) val controller = NotificationsQSContainerController(container, navigationModeController, overviewProxyService, featureFlags) overviewProxyService, featureFlags, delayableExecutor) controller.updateResources() assertThat(container.getChildAt(0).id).isEqualTo(1) assertThat(container.getChildAt(1).id).isNotEqualTo(View.NO_ID) } @Test fun testWindowInsetDebounce() { disableSplitShade() useNewFooter(true) given(taskbarVisible = false, navigationMode = GESTURES_NAVIGATION, insets = emptyInsets(), applyImmediately = false) fakeSystemClock.advanceTime(INSET_DEBOUNCE_MILLIS / 2) windowInsetsCallback.accept(windowInsets().withStableBottom()) delayableExecutor.advanceClockToLast() delayableExecutor.runAllReady() verify(notificationsQSContainer, never()).setQSContainerPaddingBottom(0) verify(notificationsQSContainer).setQSContainerPaddingBottom(STABLE_INSET_BOTTOM) } private fun disableSplitShade() { setSplitShadeEnabled(false) } Loading @@ -513,12 +540,17 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { private fun given( taskbarVisible: Boolean, navigationMode: Int, insets: WindowInsets insets: WindowInsets, applyImmediately: Boolean = true ) { Mockito.clearInvocations(notificationsQSContainer) taskbarVisibilityCallback.onTaskbarStatusUpdated(taskbarVisible, false) navigationModeCallback.onNavigationModeChanged(navigationMode) windowInsetsCallback.accept(insets) if (applyImmediately) { delayableExecutor.advanceClockToLast() delayableExecutor.runAllReady() } } fun then( Loading