Merge "Debounce the inset application" into tm-dev am: 00b19cdef0
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17238828 Change-Id: I736e692e49589e36b5ff19f03029f37401d5f985
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -60,11 +67,29 @@ class NotificationsQSContainerController @Inject constructor(
|
||||
taskbarVisible = visible
|
||||
}
|
||||
}
|
||||
private val windowInsetsListener: Consumer<WindowInsets> = Consumer { insets ->
|
||||
// when taskbar is visible, stableInsetBottom will include its height
|
||||
bottomStableInsets = insets.stableInsetBottom
|
||||
bottomCutoutInsets = insets.displayCutout?.safeInsetBottom ?: 0
|
||||
updateBottomSpacing()
|
||||
|
||||
// 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
|
||||
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() {
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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() {
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user