Merge "Debounce the inset application" into tm-dev

This commit is contained in:
Fabian Kozynski
2022-03-21 13:45:51 +00:00
committed by Android (Google) Code Review
2 changed files with 67 additions and 10 deletions

View File

@@ -4,6 +4,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.WindowInsets import android.view.WindowInsets
import androidx.annotation.VisibleForTesting
import androidx.constraintlayout.widget.ConstraintSet import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.END import androidx.constraintlayout.widget.ConstraintSet.END
@@ -11,6 +12,7 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags import com.android.systemui.flags.Flags
import com.android.systemui.navigationbar.NavigationModeController import com.android.systemui.navigationbar.NavigationModeController
@@ -21,14 +23,19 @@ import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
import com.android.systemui.shared.system.QuickStepContract import com.android.systemui.shared.system.QuickStepContract
import com.android.systemui.util.Utils import com.android.systemui.util.Utils
import com.android.systemui.util.ViewController import com.android.systemui.util.ViewController
import com.android.systemui.util.concurrency.DelayableExecutor
import java.util.function.Consumer import java.util.function.Consumer
import javax.inject.Inject import javax.inject.Inject
@VisibleForTesting
internal const val INSET_DEBOUNCE_MILLIS = 500L
class NotificationsQSContainerController @Inject constructor( class NotificationsQSContainerController @Inject constructor(
view: NotificationsQuickSettingsContainer, view: NotificationsQuickSettingsContainer,
private val navigationModeController: NavigationModeController, private val navigationModeController: NavigationModeController,
private val overviewProxyService: OverviewProxyService, private val overviewProxyService: OverviewProxyService,
private val featureFlags: FeatureFlags private val featureFlags: FeatureFlags,
@Main private val delayableExecutor: DelayableExecutor
) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController { ) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController {
var qsExpanded = false var qsExpanded = false
@@ -60,11 +67,29 @@ class NotificationsQSContainerController @Inject constructor(
taskbarVisible = visible taskbarVisible = visible
} }
} }
private val windowInsetsListener: Consumer<WindowInsets> = Consumer { insets ->
// when taskbar is visible, stableInsetBottom will include its height // With certain configuration changes (like light/dark changes), the nav bar will disappear
bottomStableInsets = insets.stableInsetBottom // for a bit, causing `bottomStableInsets` to be unstable for some time. Debounce the value
bottomCutoutInsets = insets.displayCutout?.safeInsetBottom ?: 0 // for 500ms.
updateBottomSpacing() // 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
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() { override fun onInit() {
@@ -77,7 +102,7 @@ class NotificationsQSContainerController @Inject constructor(
public override fun onViewAttached() { public override fun onViewAttached() {
updateResources() updateResources()
overviewProxyService.addCallback(taskbarVisibilityListener) overviewProxyService.addCallback(taskbarVisibilityListener)
mView.setInsetsChangedListener(windowInsetsListener) mView.setInsetsChangedListener(delayedInsetSetter)
mView.setQSFragmentAttachedListener { qs: QS -> qs.setContainerController(this) } mView.setQSFragmentAttachedListener { qs: QS -> qs.setContainerController(this) }
mView.setConfigurationChangedListener { updateResources() } mView.setConfigurationChangedListener { updateResources() }
} }

View File

@@ -18,6 +18,8 @@ import com.android.systemui.navigationbar.NavigationModeController
import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener
import com.android.systemui.recents.OverviewProxyService import com.android.systemui.recents.OverviewProxyService
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener 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 com.google.common.truth.Truth.assertThat
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@@ -31,6 +33,7 @@ import org.mockito.Mockito.anyInt
import org.mockito.Mockito.doNothing import org.mockito.Mockito.doNothing
import org.mockito.Mockito.eq import org.mockito.Mockito.eq
import org.mockito.Mockito.mock import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
import java.util.function.Consumer import java.util.function.Consumer
@@ -71,6 +74,8 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
private lateinit var navigationModeCallback: ModeChangedListener private lateinit var navigationModeCallback: ModeChangedListener
private lateinit var taskbarVisibilityCallback: OverviewProxyListener private lateinit var taskbarVisibilityCallback: OverviewProxyListener
private lateinit var windowInsetsCallback: Consumer<WindowInsets> private lateinit var windowInsetsCallback: Consumer<WindowInsets>
private lateinit var delayableExecutor: FakeExecutor
private lateinit var fakeSystemClock: FakeSystemClock
@Before @Before
fun setup() { fun setup() {
@@ -78,11 +83,14 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
mContext.ensureTestableResources() mContext.ensureTestableResources()
whenever(notificationsQSContainer.context).thenReturn(mContext) whenever(notificationsQSContainer.context).thenReturn(mContext)
whenever(notificationsQSContainer.resources).thenReturn(mContext.resources) whenever(notificationsQSContainer.resources).thenReturn(mContext.resources)
fakeSystemClock = FakeSystemClock()
delayableExecutor = FakeExecutor(fakeSystemClock)
controller = NotificationsQSContainerController( controller = NotificationsQSContainerController(
notificationsQSContainer, notificationsQSContainer,
navigationModeController, navigationModeController,
overviewProxyService, overviewProxyService,
featureFlags featureFlags,
delayableExecutor
) )
overrideResource(R.dimen.split_shade_notifications_scrim_margin_bottom, SCRIM_MARGIN) overrideResource(R.dimen.split_shade_notifications_scrim_margin_bottom, SCRIM_MARGIN)
@@ -490,13 +498,32 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
container.addView(newViewWithId(1)) container.addView(newViewWithId(1))
container.addView(newViewWithId(View.NO_ID)) container.addView(newViewWithId(View.NO_ID))
val controller = NotificationsQSContainerController(container, navigationModeController, val controller = NotificationsQSContainerController(container, navigationModeController,
overviewProxyService, featureFlags) overviewProxyService, featureFlags, delayableExecutor)
controller.updateResources() controller.updateResources()
assertThat(container.getChildAt(0).id).isEqualTo(1) assertThat(container.getChildAt(0).id).isEqualTo(1)
assertThat(container.getChildAt(1).id).isNotEqualTo(View.NO_ID) 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() { private fun disableSplitShade() {
setSplitShadeEnabled(false) setSplitShadeEnabled(false)
} }
@@ -513,12 +540,17 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
private fun given( private fun given(
taskbarVisible: Boolean, taskbarVisible: Boolean,
navigationMode: Int, navigationMode: Int,
insets: WindowInsets insets: WindowInsets,
applyImmediately: Boolean = true
) { ) {
Mockito.clearInvocations(notificationsQSContainer) Mockito.clearInvocations(notificationsQSContainer)
taskbarVisibilityCallback.onTaskbarStatusUpdated(taskbarVisible, false) taskbarVisibilityCallback.onTaskbarStatusUpdated(taskbarVisible, false)
navigationModeCallback.onNavigationModeChanged(navigationMode) navigationModeCallback.onNavigationModeChanged(navigationMode)
windowInsetsCallback.accept(insets) windowInsetsCallback.accept(insets)
if (applyImmediately) {
delayableExecutor.advanceClockToLast()
delayableExecutor.runAllReady()
}
} }
fun then( fun then(