Merge "Fix unlocked split shade jumping back into place after over expanding" into tm-qpr-dev am: 2429c98084

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21510345

Change-Id: I22df5dd516991be186638a32d6bfe92f0e225858
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
TreeHugger Robot
2023-02-22 16:57:54 +00:00
committed by Automerger Merge Worker
6 changed files with 14 additions and 435 deletions

View File

@@ -1915,7 +1915,6 @@ public final class NotificationPanelViewController implements Dumpable {
// we want to perform an overshoot animation when flinging open // we want to perform an overshoot animation when flinging open
final boolean addOverscroll = final boolean addOverscroll =
expand expand
&& !mSplitShadeEnabled // Split shade has its own overscroll logic
&& mStatusBarStateController.getState() != KEYGUARD && mStatusBarStateController.getState() != KEYGUARD
&& mOverExpansion == 0.0f && mOverExpansion == 0.0f
&& vel >= 0; && vel >= 0;
@@ -2591,9 +2590,14 @@ public final class NotificationPanelViewController implements Dumpable {
return; return;
} }
mOverExpansion = overExpansion; mOverExpansion = overExpansion;
// Translating the quick settings by half the overexpansion to center it in the background if (mSplitShadeEnabled) {
// frame mQsController.setOverScrollAmount((int) overExpansion);
mScrimController.setNotificationsOverScrollAmount((int) overExpansion);
} else {
// Translating the quick settings by half the overexpansion to center it in the
// background frame
mQsController.updateQsFrameTranslation(); mQsController.updateQsFrameTranslation();
}
mNotificationStackScrollLayoutController.setOverExpansion(overExpansion); mNotificationStackScrollLayoutController.setOverExpansion(overExpansion);
} }
@@ -3595,7 +3599,7 @@ public final class NotificationPanelViewController implements Dumpable {
private void fling(float vel, boolean expand, float collapseSpeedUpFactor, private void fling(float vel, boolean expand, float collapseSpeedUpFactor,
boolean expandBecauseOfFalsing) { boolean expandBecauseOfFalsing) {
float target = expand ? getMaxPanelHeight() : 0; float target = expand ? getMaxPanelTransitionDistance() : 0;
if (!expand) { if (!expand) {
setClosing(true); setClosing(true);
} }
@@ -3680,7 +3684,7 @@ public final class NotificationPanelViewController implements Dumpable {
float maxPanelHeight = getMaxPanelTransitionDistance(); float maxPanelHeight = getMaxPanelTransitionDistance();
if (mHeightAnimator == null) { if (mHeightAnimator == null) {
// Split shade has its own overscroll logic // Split shade has its own overscroll logic
if (mTracking && !mSplitShadeEnabled) { if (mTracking) {
float overExpansionPixels = Math.max(0, h - maxPanelHeight); float overExpansionPixels = Math.max(0, h - maxPanelHeight);
setOverExpansionInternal(overExpansionPixels, true /* isFromGesture */); setOverExpansionInternal(overExpansionPixels, true /* isFromGesture */);
} }

View File

@@ -803,6 +803,10 @@ public class QuickSettingsController {
} }
} }
void setOverScrollAmount(int overExpansion) {
mQs.setOverScrollAmount(overExpansion);
}
private void setOverScrolling(boolean overscrolling) { private void setOverScrolling(boolean overscrolling) {
mStackScrollerOverscrolling = overscrolling; mStackScrollerOverscrolling = overscrolling;
if (mQs != null) { if (mQs != null) {

View File

@@ -43,8 +43,6 @@ constructor(
shadeExpansionStateManager: ShadeExpansionStateManager, shadeExpansionStateManager: ShadeExpansionStateManager,
dumpManager: DumpManager, dumpManager: DumpManager,
private val context: Context, private val context: Context,
private val splitShadeOverScrollerFactory: SplitShadeOverScroller.Factory,
private val noOpOverScroller: NoOpOverScroller,
private val scrimShadeTransitionController: ScrimShadeTransitionController, private val scrimShadeTransitionController: ScrimShadeTransitionController,
private val statusBarStateController: SysuiStatusBarStateController, private val statusBarStateController: SysuiStatusBarStateController,
) { ) {
@@ -57,17 +55,6 @@ constructor(
private var currentPanelState: Int? = null private var currentPanelState: Int? = null
private var lastShadeExpansionChangeEvent: ShadeExpansionChangeEvent? = null private var lastShadeExpansionChangeEvent: ShadeExpansionChangeEvent? = null
private val splitShadeOverScroller by lazy {
splitShadeOverScrollerFactory.create({ qs }, { notificationStackScrollLayoutController })
}
private val shadeOverScroller: ShadeOverScroller
get() =
if (inSplitShade && isScreenUnlocked() && propertiesInitialized()) {
splitShadeOverScroller
} else {
noOpOverScroller
}
init { init {
updateResources() updateResources()
configurationController.addCallback( configurationController.addCallback(
@@ -89,21 +76,14 @@ constructor(
private fun onPanelStateChanged(@PanelState state: Int) { private fun onPanelStateChanged(@PanelState state: Int) {
currentPanelState = state currentPanelState = state
shadeOverScroller.onPanelStateChanged(state)
scrimShadeTransitionController.onPanelStateChanged(state) scrimShadeTransitionController.onPanelStateChanged(state)
} }
private fun onPanelExpansionChanged(event: ShadeExpansionChangeEvent) { private fun onPanelExpansionChanged(event: ShadeExpansionChangeEvent) {
lastShadeExpansionChangeEvent = event lastShadeExpansionChangeEvent = event
shadeOverScroller.onDragDownAmountChanged(event.dragDownPxAmount)
scrimShadeTransitionController.onPanelExpansionChanged(event) scrimShadeTransitionController.onPanelExpansionChanged(event)
} }
private fun propertiesInitialized() =
this::qs.isInitialized &&
this::notificationPanelViewController.isInitialized &&
this::notificationStackScrollLayoutController.isInitialized
private fun dump(pw: PrintWriter) { private fun dump(pw: PrintWriter) {
pw.println( pw.println(
""" """

View File

@@ -1,167 +0,0 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.shade.transition
import android.animation.Animator
import android.animation.ValueAnimator
import android.content.Context
import android.content.res.Configuration
import android.util.MathUtils
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.R
import com.android.systemui.animation.Interpolators
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.qs.QS
import com.android.systemui.shade.PanelState
import com.android.systemui.shade.STATE_CLOSED
import com.android.systemui.shade.STATE_OPENING
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.ScrimController
import com.android.systemui.statusbar.policy.ConfigurationController
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import java.io.PrintWriter
class SplitShadeOverScroller
@AssistedInject
constructor(
configurationController: ConfigurationController,
dumpManager: DumpManager,
private val context: Context,
private val scrimController: ScrimController,
@Assisted private val qSProvider: () -> QS,
@Assisted private val nsslControllerProvider: () -> NotificationStackScrollLayoutController
) : ShadeOverScroller {
private var releaseOverScrollDuration = 0L
private var maxOverScrollAmount = 0
private var previousOverscrollAmount = 0
private var dragDownAmount: Float = 0f
@PanelState private var panelState: Int = STATE_CLOSED
private var releaseOverScrollAnimator: Animator? = null
private val qS: QS
get() = qSProvider()
private val nsslController: NotificationStackScrollLayoutController
get() = nsslControllerProvider()
init {
updateResources()
configurationController.addCallback(
object : ConfigurationController.ConfigurationListener {
override fun onConfigChanged(newConfig: Configuration?) {
updateResources()
}
})
dumpManager.registerCriticalDumpable("SplitShadeOverScroller") { printWriter, _ ->
dump(printWriter)
}
}
private fun updateResources() {
val resources = context.resources
maxOverScrollAmount = resources.getDimensionPixelSize(R.dimen.shade_max_over_scroll_amount)
releaseOverScrollDuration =
resources.getInteger(R.integer.lockscreen_shade_over_scroll_release_duration).toLong()
}
override fun onPanelStateChanged(@PanelState newPanelState: Int) {
if (shouldReleaseOverscroll(previousState = panelState, newState = newPanelState)) {
releaseOverScroll()
}
panelState = newPanelState
}
override fun onDragDownAmountChanged(newDragDownAmount: Float) {
if (dragDownAmount == newDragDownAmount) {
return
}
dragDownAmount = newDragDownAmount
if (shouldOverscroll()) {
overScroll(newDragDownAmount)
}
}
private fun shouldOverscroll() = panelState == STATE_OPENING
private fun shouldReleaseOverscroll(@PanelState previousState: Int, @PanelState newState: Int) =
previousState == STATE_OPENING && newState != STATE_OPENING
private fun overScroll(dragDownAmount: Float) {
val overscrollAmount: Int = calculateOverscrollAmount(dragDownAmount)
applyOverscroll(overscrollAmount)
previousOverscrollAmount = overscrollAmount
}
private fun calculateOverscrollAmount(dragDownAmount: Float): Int {
val fullHeight: Int = nsslController.height
val fullHeightProgress: Float = MathUtils.saturate(dragDownAmount / fullHeight)
return (fullHeightProgress * maxOverScrollAmount).toInt()
}
private fun applyOverscroll(overscrollAmount: Int) {
qS.setOverScrollAmount(overscrollAmount)
scrimController.setNotificationsOverScrollAmount(overscrollAmount)
nsslController.setOverScrollAmount(overscrollAmount)
}
private fun releaseOverScroll() {
val animator = ValueAnimator.ofInt(previousOverscrollAmount, 0)
animator.addUpdateListener {
val overScrollAmount = it.animatedValue as Int
qS.setOverScrollAmount(overScrollAmount)
scrimController.setNotificationsOverScrollAmount(overScrollAmount)
nsslController.setOverScrollAmount(overScrollAmount)
}
animator.interpolator = Interpolators.STANDARD
animator.duration = releaseOverScrollDuration
animator.start()
releaseOverScrollAnimator = animator
previousOverscrollAmount = 0
}
@VisibleForTesting
internal fun finishAnimations() {
releaseOverScrollAnimator?.end()
releaseOverScrollAnimator = null
}
private fun dump(pw: PrintWriter) {
pw.println(
"""
SplitShadeOverScroller:
Resources:
releaseOverScrollDuration: $releaseOverScrollDuration
maxOverScrollAmount: $maxOverScrollAmount
State:
previousOverscrollAmount: $previousOverscrollAmount
dragDownAmount: $dragDownAmount
panelState: $panelState
""".trimIndent())
}
@AssistedFactory
fun interface Factory {
fun create(
qSProvider: () -> QS,
nsslControllerProvider: () -> NotificationStackScrollLayoutController
): SplitShadeOverScroller
}
}

View File

@@ -2,37 +2,24 @@ package com.android.systemui.shade.transition
import android.testing.AndroidTestingRunner import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.qs.QS
import com.android.systemui.shade.NotificationPanelViewController
import com.android.systemui.shade.STATE_OPENING import com.android.systemui.shade.STATE_OPENING
import com.android.systemui.shade.ShadeExpansionChangeEvent import com.android.systemui.shade.ShadeExpansionChangeEvent
import com.android.systemui.shade.ShadeExpansionStateManager import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.policy.FakeConfigurationController import com.android.systemui.statusbar.policy.FakeConfigurationController
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class) @RunWith(AndroidTestingRunner::class)
@SmallTest @SmallTest
class ShadeTransitionControllerTest : SysuiTestCase() { class ShadeTransitionControllerTest : SysuiTestCase() {
@Mock private lateinit var npvc: NotificationPanelViewController
@Mock private lateinit var nsslController: NotificationStackScrollLayoutController
@Mock private lateinit var qs: QS
@Mock private lateinit var noOpOverScroller: NoOpOverScroller
@Mock private lateinit var splitShadeOverScroller: SplitShadeOverScroller
@Mock private lateinit var scrimShadeTransitionController: ScrimShadeTransitionController @Mock private lateinit var scrimShadeTransitionController: ScrimShadeTransitionController
@Mock private lateinit var dumpManager: DumpManager @Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var statusBarStateController: SysuiStatusBarStateController @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
@@ -52,119 +39,19 @@ class ShadeTransitionControllerTest : SysuiTestCase() {
shadeExpansionStateManager, shadeExpansionStateManager,
dumpManager, dumpManager,
context, context,
splitShadeOverScrollerFactory = { _, _ -> splitShadeOverScroller },
noOpOverScroller,
scrimShadeTransitionController, scrimShadeTransitionController,
statusBarStateController, statusBarStateController,
) )
// Resetting as they are notified upon initialization.
reset(noOpOverScroller, splitShadeOverScroller)
}
@Test
fun onPanelExpansionChanged_inSplitShade_forwardsToSplitShadeOverScroller() {
initLateProperties()
enableSplitShade()
startPanelExpansion()
verify(splitShadeOverScroller).onPanelStateChanged(STATE_OPENING)
verify(splitShadeOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT)
verifyZeroInteractions(noOpOverScroller)
}
@Test
fun onPanelStateChanged_inSplitShade_propertiesNotInitialized_forwardsToNoOpOverScroller() {
enableSplitShade()
startPanelExpansion()
verify(noOpOverScroller).onPanelStateChanged(STATE_OPENING)
verify(noOpOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT)
verifyZeroInteractions(splitShadeOverScroller)
}
@Test
fun onPanelStateChanged_inSplitShade_onKeyguard_forwardsToNoOpOverScroller() {
initLateProperties()
enableSplitShade()
setOnKeyguard()
startPanelExpansion()
verify(noOpOverScroller).onPanelStateChanged(STATE_OPENING)
verify(noOpOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT)
verifyZeroInteractions(splitShadeOverScroller)
}
@Test
fun onPanelStateChanged_inSplitShade_onLockedShade_forwardsToNoOpOverScroller() {
initLateProperties()
enableSplitShade()
setOnLockedShade()
startPanelExpansion()
verify(noOpOverScroller).onPanelStateChanged(STATE_OPENING)
verify(noOpOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT)
verifyZeroInteractions(splitShadeOverScroller)
}
@Test
fun onPanelExpansionChanged_inSplitShade_onUnlockedShade_forwardsToSplitShadeOverScroller() {
initLateProperties()
enableSplitShade()
setOnUnlockedShade()
startPanelExpansion()
verify(splitShadeOverScroller).onPanelStateChanged(STATE_OPENING)
verify(splitShadeOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT)
verifyZeroInteractions(noOpOverScroller)
}
@Test
fun onPanelStateChanged_notInSplitShade_forwardsToNoOpOverScroller() {
initLateProperties()
disableSplitShade()
startPanelExpansion()
verify(noOpOverScroller).onPanelStateChanged(STATE_OPENING)
verify(noOpOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT)
verifyZeroInteractions(splitShadeOverScroller)
} }
@Test @Test
fun onPanelStateChanged_forwardsToScrimTransitionController() { fun onPanelStateChanged_forwardsToScrimTransitionController() {
initLateProperties()
startPanelExpansion() startPanelExpansion()
verify(scrimShadeTransitionController).onPanelStateChanged(STATE_OPENING) verify(scrimShadeTransitionController).onPanelStateChanged(STATE_OPENING)
verify(scrimShadeTransitionController).onPanelExpansionChanged(DEFAULT_EXPANSION_EVENT) verify(scrimShadeTransitionController).onPanelExpansionChanged(DEFAULT_EXPANSION_EVENT)
} }
private fun initLateProperties() {
controller.qs = qs
controller.notificationStackScrollLayoutController = nsslController
controller.notificationPanelViewController = npvc
}
private fun disableSplitShade() {
setSplitShadeEnabled(false)
}
private fun enableSplitShade() {
setSplitShadeEnabled(true)
}
private fun setSplitShadeEnabled(enabled: Boolean) {
overrideResource(R.bool.config_use_split_notification_shade, enabled)
configurationController.notifyConfigurationChanged()
}
private fun startPanelExpansion() { private fun startPanelExpansion() {
shadeExpansionStateManager.onPanelExpansionChanged( shadeExpansionStateManager.onPanelExpansionChanged(
DEFAULT_EXPANSION_EVENT.fraction, DEFAULT_EXPANSION_EVENT.fraction,
@@ -174,23 +61,6 @@ class ShadeTransitionControllerTest : SysuiTestCase() {
) )
} }
private fun setOnKeyguard() {
setShadeState(StatusBarState.KEYGUARD)
}
private fun setOnLockedShade() {
setShadeState(StatusBarState.SHADE_LOCKED)
}
private fun setOnUnlockedShade() {
setShadeState(StatusBarState.SHADE)
}
private fun setShadeState(state: Int) {
whenever(statusBarStateController.state).thenReturn(state)
whenever(statusBarStateController.currentOrUpcomingState).thenReturn(state)
}
companion object { companion object {
private const val DEFAULT_DRAG_DOWN_AMOUNT = 123f private const val DEFAULT_DRAG_DOWN_AMOUNT = 123f
private val DEFAULT_EXPANSION_EVENT = private val DEFAULT_EXPANSION_EVENT =

View File

@@ -1,112 +0,0 @@
package com.android.systemui.shade.transition
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.qs.QS
import com.android.systemui.shade.STATE_CLOSED
import com.android.systemui.shade.STATE_OPEN
import com.android.systemui.shade.STATE_OPENING
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.ScrimController
import com.android.systemui.statusbar.policy.FakeConfigurationController
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
class SplitShadeOverScrollerTest : SysuiTestCase() {
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var scrimController: ScrimController
@Mock private lateinit var qs: QS
@Mock private lateinit var nsslController: NotificationStackScrollLayoutController
private val configurationController = FakeConfigurationController()
private lateinit var overScroller: SplitShadeOverScroller
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(nsslController.height).thenReturn(1000)
overScroller =
SplitShadeOverScroller(
configurationController,
dumpManager,
context,
scrimController,
{ qs },
{ nsslController })
}
@Test
fun onDragDownAmountChanged_panelOpening_overScrolls_basedOnHeightAndMaxAmount() {
val maxOverScrollAmount = 50
val dragDownAmount = 100f
overrideResource(R.dimen.shade_max_over_scroll_amount, maxOverScrollAmount)
configurationController.notifyConfigurationChanged()
overScroller.onPanelStateChanged(STATE_OPENING)
overScroller.onDragDownAmountChanged(dragDownAmount)
val expectedOverScrollAmount =
(dragDownAmount / nsslController.height * maxOverScrollAmount).toInt()
verify(qs).setOverScrollAmount(expectedOverScrollAmount)
verify(nsslController).setOverScrollAmount(expectedOverScrollAmount)
verify(scrimController).setNotificationsOverScrollAmount(expectedOverScrollAmount)
}
@Test
fun onDragDownAmountChanged_panelClosed_doesNotOverScroll() {
overScroller.onPanelStateChanged(STATE_CLOSED)
overScroller.onDragDownAmountChanged(100f)
verifyZeroInteractions(qs, scrimController, nsslController)
}
@Test
fun onDragDownAmountChanged_panelOpen_doesNotOverScroll() {
overScroller.onPanelStateChanged(STATE_OPEN)
overScroller.onDragDownAmountChanged(100f)
verifyZeroInteractions(qs, scrimController, nsslController)
}
@Test
fun onPanelStateChanged_opening_thenOpen_releasesOverScroll() {
overScroller.onPanelStateChanged(STATE_OPENING)
overScroller.onDragDownAmountChanged(100f)
overScroller.onPanelStateChanged(STATE_OPEN)
overScroller.finishAnimations()
verify(qs, atLeastOnce()).setOverScrollAmount(0)
verify(scrimController, atLeastOnce()).setNotificationsOverScrollAmount(0)
verify(nsslController, atLeastOnce()).setOverScrollAmount(0)
}
@Test
fun onPanelStateChanged_opening_thenClosed_releasesOverScroll() {
overScroller.onPanelStateChanged(STATE_OPENING)
overScroller.onDragDownAmountChanged(100f)
overScroller.onPanelStateChanged(STATE_CLOSED)
overScroller.finishAnimations()
verify(qs, atLeastOnce()).setOverScrollAmount(0)
verify(scrimController, atLeastOnce()).setNotificationsOverScrollAmount(0)
verify(nsslController, atLeastOnce()).setOverScrollAmount(0)
}
}