Merge "Fix media staying too long during shade "immediate" expansion/collapsing" into tm-qpr-dev

This commit is contained in:
Christian Göllner
2022-08-19 12:24:46 +00:00
committed by Android (Google) Code Review
5 changed files with 151 additions and 40 deletions

View File

@@ -36,6 +36,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dreams.DreamOverlayStateController import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.NotifPanelEvents
import com.android.systemui.statusbar.CrossFadeHelper import com.android.systemui.statusbar.CrossFadeHelper
import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.StatusBarState
@@ -85,12 +86,21 @@ class MediaHierarchyManager @Inject constructor(
private val bypassController: KeyguardBypassController, private val bypassController: KeyguardBypassController,
private val mediaCarouselController: MediaCarouselController, private val mediaCarouselController: MediaCarouselController,
private val notifLockscreenUserManager: NotificationLockscreenUserManager, private val notifLockscreenUserManager: NotificationLockscreenUserManager,
private val keyguardViewController: KeyguardViewController,
private val dreamOverlayStateController: DreamOverlayStateController,
configurationController: ConfigurationController, configurationController: ConfigurationController,
wakefulnessLifecycle: WakefulnessLifecycle, wakefulnessLifecycle: WakefulnessLifecycle,
private val keyguardViewController: KeyguardViewController, panelEventsEvents: NotifPanelEvents,
private val dreamOverlayStateController: DreamOverlayStateController
) { ) {
/**
* Whether we "skip" QQS during panel expansion.
*
* This means that when expanding the panel we go directly to QS. Also when we are on QS and
* start closing the panel, it fully collapses instead of going to QQS.
*/
private var skipQqsOnExpansion: Boolean = false
/** /**
* The root overlay of the hierarchy. This is where the media notification is attached to * The root overlay of the hierarchy. This is where the media notification is attached to
* whenever the view is transitioning from one host to another. It also make sure that the * whenever the view is transitioning from one host to another. It also make sure that the
@@ -504,6 +514,13 @@ class MediaHierarchyManager @Inject constructor(
mediaCarouselController.updateUserVisibility = { mediaCarouselController.updateUserVisibility = {
mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser() mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser()
} }
panelEventsEvents.registerListener(object : NotifPanelEvents.Listener {
override fun onExpandImmediateChanged(isExpandImmediateEnabled: Boolean) {
skipQqsOnExpansion = isExpandImmediateEnabled
updateDesiredLocation()
}
})
} }
private fun updateConfiguration() { private fun updateConfiguration() {
@@ -701,6 +718,9 @@ class MediaHierarchyManager @Inject constructor(
if (isCurrentlyInGuidedTransformation()) { if (isCurrentlyInGuidedTransformation()) {
return false return false
} }
if (skipQqsOnExpansion) {
return false
}
// This is an invalid transition, and can happen when using the camera gesture from the // This is an invalid transition, and can happen when using the camera gesture from the
// lock screen. Disallow. // lock screen. Disallow.
if (previousLocation == LOCATION_LOCKSCREEN && if (previousLocation == LOCATION_LOCKSCREEN &&
@@ -852,6 +872,9 @@ class MediaHierarchyManager @Inject constructor(
* otherwise * otherwise
*/ */
private fun getTransformationProgress(): Float { private fun getTransformationProgress(): Float {
if (skipQqsOnExpansion) {
return -1.0f
}
val progress = getQSTransformationProgress() val progress = getQSTransformationProgress()
if (statusbarState != StatusBarState.KEYGUARD && progress >= 0) { if (statusbarState != StatusBarState.KEYGUARD && progress >= 0) {
return progress return progress
@@ -1042,6 +1065,10 @@ class MediaHierarchyManager @Inject constructor(
// reattach it without an animation // reattach it without an animation
return LOCATION_LOCKSCREEN return LOCATION_LOCKSCREEN
} }
if (skipQqsOnExpansion) {
// When doing an immediate expand or collapse, we want to keep it in QS.
return LOCATION_QS
}
return location return location
} }

View File

@@ -29,11 +29,25 @@ interface NotifPanelEvents {
interface Listener { interface Listener {
/** Invoked when the notification panel starts or stops collapsing. */ /** Invoked when the notification panel starts or stops collapsing. */
fun onPanelCollapsingChanged(isCollapsing: Boolean) @JvmDefault
fun onPanelCollapsingChanged(isCollapsing: Boolean) {}
/** /**
* Invoked when the notification panel starts or stops launching an [android.app.Activity]. * Invoked when the notification panel starts or stops launching an [android.app.Activity].
*/ */
fun onLaunchingActivityChanged(isLaunchingActivity: Boolean) @JvmDefault
fun onLaunchingActivityChanged(isLaunchingActivity: Boolean) {}
/**
* Invoked when the "expand immediate" attribute changes.
*
* An example of expanding immediately is when swiping down from the top with two fingers.
* Instead of going to QQS, we immediately expand to full QS.
*
* Another example is when full QS is showing, and we swipe up from the bottom. Instead of
* going to QQS, the panel fully collapses.
*/
@JvmDefault
fun onExpandImmediateChanged(isExpandImmediateEnabled: Boolean) {}
} }
} }

View File

@@ -1745,12 +1745,17 @@ public final class NotificationPanelViewController extends PanelViewController {
} }
if (mQsExpanded) { if (mQsExpanded) {
mQsExpandImmediate = true; setQsExpandImmediate(true);
setShowShelfOnly(true); setShowShelfOnly(true);
} }
super.collapse(delayed, speedUpFactor); super.collapse(delayed, speedUpFactor);
} }
private void setQsExpandImmediate(boolean expandImmediate) {
mQsExpandImmediate = expandImmediate;
mPanelEventsEmitter.notifyExpandImmediateChange(expandImmediate);
}
private void setShowShelfOnly(boolean shelfOnly) { private void setShowShelfOnly(boolean shelfOnly) {
mNotificationStackScrollLayoutController.setShouldShowShelfOnly( mNotificationStackScrollLayoutController.setShouldShowShelfOnly(
shelfOnly && !mSplitShadeEnabled); shelfOnly && !mSplitShadeEnabled);
@@ -1803,7 +1808,7 @@ public final class NotificationPanelViewController extends PanelViewController {
public void expandWithQs() { public void expandWithQs() {
if (isQsExpansionEnabled()) { if (isQsExpansionEnabled()) {
mQsExpandImmediate = true; setQsExpandImmediate(true);
setShowShelfOnly(true); setShowShelfOnly(true);
} }
if (mSplitShadeEnabled && isOnKeyguard()) { if (mSplitShadeEnabled && isOnKeyguard()) {
@@ -2132,7 +2137,7 @@ public final class NotificationPanelViewController extends PanelViewController {
if (mTwoFingerQsExpandPossible && isOpenQsEvent(event) && event.getY(event.getActionIndex()) if (mTwoFingerQsExpandPossible && isOpenQsEvent(event) && event.getY(event.getActionIndex())
< mStatusBarMinHeight) { < mStatusBarMinHeight) {
mMetricsLogger.count(COUNTER_PANEL_OPEN_QS, 1); mMetricsLogger.count(COUNTER_PANEL_OPEN_QS, 1);
mQsExpandImmediate = true; setQsExpandImmediate(true);
setShowShelfOnly(true); setShowShelfOnly(true);
requestPanelHeightUpdate(); requestPanelHeightUpdate();
@@ -3334,7 +3339,7 @@ public final class NotificationPanelViewController extends PanelViewController {
} else { } else {
setListening(true); setListening(true);
} }
mQsExpandImmediate = false; setQsExpandImmediate(false);
setShowShelfOnly(false); setShowShelfOnly(false);
mTwoFingerQsExpandPossible = false; mTwoFingerQsExpandPossible = false;
updateTrackingHeadsUp(null); updateTrackingHeadsUp(null);
@@ -3392,7 +3397,7 @@ public final class NotificationPanelViewController extends PanelViewController {
super.onTrackingStarted(); super.onTrackingStarted();
mScrimController.onTrackingStarted(); mScrimController.onTrackingStarted();
if (mQsFullyExpanded) { if (mQsFullyExpanded) {
mQsExpandImmediate = true; setQsExpandImmediate(true);
setShowShelfOnly(true); setShowShelfOnly(true);
} }
mNotificationStackScrollLayoutController.onPanelTrackingStarted(); mNotificationStackScrollLayoutController.onPanelTrackingStarted();
@@ -4959,7 +4964,7 @@ public final class NotificationPanelViewController extends PanelViewController {
// to locked will trigger this event and we're not actually in the process of opening // to locked will trigger this event and we're not actually in the process of opening
// the shade, lockscreen is just always expanded // the shade, lockscreen is just always expanded
if (mSplitShadeEnabled && !isOnKeyguard()) { if (mSplitShadeEnabled && !isOnKeyguard()) {
mQsExpandImmediate = true; setQsExpandImmediate(true);
} }
mCentralSurfaces.makeExpandedVisible(false); mCentralSurfaces.makeExpandedVisible(false);
} }
@@ -5026,5 +5031,11 @@ public final class NotificationPanelViewController extends PanelViewController {
cb.onPanelCollapsingChanged(isCollapsing); cb.onPanelCollapsingChanged(isCollapsing);
} }
} }
private void notifyExpandImmediateChange(boolean expandImmediateEnabled) {
for (NotifPanelEvents.Listener cb : mListeners) {
cb.onExpandImmediateChanged(expandImmediateEnabled);
}
}
} }
} }

View File

@@ -16,7 +16,6 @@
package com.android.systemui.media package com.android.systemui.media
import org.mockito.Mockito.`when` as whenever
import android.graphics.Rect import android.graphics.Rect
import android.testing.AndroidTestingRunner import android.testing.AndroidTestingRunner
import android.testing.TestableLooper import android.testing.TestableLooper
@@ -30,6 +29,7 @@ import com.android.systemui.controls.controller.ControlsControllerImplTest.Compa
import com.android.systemui.dreams.DreamOverlayStateController import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.testing.FakeNotifPanelEvents
import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -50,10 +50,11 @@ import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyLong import org.mockito.ArgumentMatchers.anyLong
import org.mockito.Captor import org.mockito.Captor
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.times import org.mockito.Mockito.times
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoJUnit
@SmallTest @SmallTest
@@ -61,32 +62,19 @@ import org.mockito.junit.MockitoJUnit
@TestableLooper.RunWithLooper @TestableLooper.RunWithLooper
class MediaHierarchyManagerTest : SysuiTestCase() { class MediaHierarchyManagerTest : SysuiTestCase() {
@Mock @Mock private lateinit var lockHost: MediaHost
private lateinit var lockHost: MediaHost @Mock private lateinit var qsHost: MediaHost
@Mock @Mock private lateinit var qqsHost: MediaHost
private lateinit var qsHost: MediaHost @Mock private lateinit var bypassController: KeyguardBypassController
@Mock @Mock private lateinit var keyguardStateController: KeyguardStateController
private lateinit var qqsHost: MediaHost @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
@Mock @Mock private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager
private lateinit var bypassController: KeyguardBypassController @Mock private lateinit var mediaCarouselController: MediaCarouselController
@Mock @Mock private lateinit var mediaCarouselScrollHandler: MediaCarouselScrollHandler
private lateinit var keyguardStateController: KeyguardStateController @Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
@Mock @Mock private lateinit var keyguardViewController: KeyguardViewController
private lateinit var statusBarStateController: SysuiStatusBarStateController @Mock private lateinit var uniqueObjectHostView: UniqueObjectHostView
@Mock @Mock private lateinit var dreamOverlayStateController: DreamOverlayStateController
private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager
@Mock
private lateinit var mediaCarouselController: MediaCarouselController
@Mock
private lateinit var mediaCarouselScrollHandler: MediaCarouselScrollHandler
@Mock
private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
@Mock
private lateinit var keyguardViewController: KeyguardViewController
@Mock
private lateinit var uniqueObjectHostView: UniqueObjectHostView
@Mock
private lateinit var dreamOverlayStateController: DreamOverlayStateController
@Captor @Captor
private lateinit var wakefullnessObserver: ArgumentCaptor<(WakefulnessLifecycle.Observer)> private lateinit var wakefullnessObserver: ArgumentCaptor<(WakefulnessLifecycle.Observer)>
@Captor @Captor
@@ -97,6 +85,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
private lateinit var mediaHiearchyManager: MediaHierarchyManager private lateinit var mediaHiearchyManager: MediaHierarchyManager
private lateinit var mediaFrame: ViewGroup private lateinit var mediaFrame: ViewGroup
private val configurationController = FakeConfigurationController() private val configurationController = FakeConfigurationController()
private val notifPanelEvents = FakeNotifPanelEvents()
@Before @Before
fun setup() { fun setup() {
@@ -111,10 +100,12 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
bypassController, bypassController,
mediaCarouselController, mediaCarouselController,
notificationLockscreenUserManager, notificationLockscreenUserManager,
keyguardViewController,
dreamOverlayStateController,
configurationController, configurationController,
wakefulnessLifecycle, wakefulnessLifecycle,
keyguardViewController, notifPanelEvents,
dreamOverlayStateController) )
verify(wakefulnessLifecycle).addObserver(wakefullnessObserver.capture()) verify(wakefulnessLifecycle).addObserver(wakefullnessObserver.capture())
verify(statusBarStateController).addCallback(statusBarCallback.capture()) verify(statusBarStateController).addCallback(statusBarCallback.capture())
setupHost(lockHost, MediaHierarchyManager.LOCATION_LOCKSCREEN, LOCKSCREEN_TOP) setupHost(lockHost, MediaHierarchyManager.LOCATION_LOCKSCREEN, LOCKSCREEN_TOP)
@@ -211,6 +202,25 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE) assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
} }
@Test
fun calculateTransformationType_notOnLockscreen_returnsTransition() {
expandQS()
val transformType = mediaHiearchyManager.calculateTransformationType()
assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_TRANSITION)
}
@Test
fun calculateTransformationType_onLockscreen_returnsTransition() {
goToLockscreen()
expandQS()
val transformType = mediaHiearchyManager.calculateTransformationType()
assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
}
@Test @Test
fun calculateTransformationType_onLockShade_inSplitShade_goingToFullShade_returnsTransition() { fun calculateTransformationType_onLockShade_inSplitShade_goingToFullShade_returnsTransition() {
enableSplitShade() enableSplitShade()
@@ -294,6 +304,18 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
assertThat(mediaHiearchyManager.isCurrentlyInGuidedTransformation()).isTrue() assertThat(mediaHiearchyManager.isCurrentlyInGuidedTransformation()).isTrue()
} }
@Test
fun isCurrentlyInGuidedTransformation_hostsVisible_expandImmediateEnabled_returnsFalse() {
notifPanelEvents.changeExpandImmediate(expandImmediate = true)
goToLockscreen()
enterGuidedTransformation()
whenever(lockHost.visible).thenReturn(true)
whenever(qsHost.visible).thenReturn(true)
whenever(qqsHost.visible).thenReturn(true)
assertThat(mediaHiearchyManager.isCurrentlyInGuidedTransformation()).isFalse()
}
@Test @Test
fun isCurrentlyInGuidedTransformation_hostNotVisible_returnsTrue() { fun isCurrentlyInGuidedTransformation_hostNotVisible_returnsTrue() {
goToLockscreen() goToLockscreen()

View File

@@ -0,0 +1,37 @@
/*
* 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.testing
import com.android.systemui.shade.NotifPanelEvents
/** Fake implementation of [NotifPanelEvents] for testing. */
class FakeNotifPanelEvents : NotifPanelEvents {
private val listeners = mutableListOf<NotifPanelEvents.Listener>()
override fun registerListener(listener: NotifPanelEvents.Listener) {
listeners.add(listener)
}
override fun unregisterListener(listener: NotifPanelEvents.Listener) {
listeners.remove(listener)
}
fun changeExpandImmediate(expandImmediate: Boolean) {
listeners.forEach { it.onExpandImmediateChanged(expandImmediate) }
}
}