Merge "Debounce the inset application" into tm-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
00b19cdef0
@@ -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() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
Reference in New Issue
Block a user