Loading packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt +3 −1 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import com.android.systemui.media.controls.ui.controller.MediaCarouselController import com.android.systemui.media.controls.ui.view.MediaHost import com.android.systemui.res.R import com.android.systemui.util.animation.MeasurementInput import kotlinx.coroutines.ExperimentalCoroutinesApi object MediaCarousel { object Elements { Loading @@ -46,6 +47,7 @@ object MediaCarousel { } } @ExperimentalCoroutinesApi @Composable fun SceneScope.MediaCarousel( isVisible: Boolean, Loading @@ -54,7 +56,7 @@ fun SceneScope.MediaCarousel( carouselController: MediaCarouselController, offsetProvider: (() -> IntOffset)? = null, ) { if (!isVisible) { if (!isVisible || carouselController.isLockedAndHidden()) { return } Loading packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt +13 −5 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.Edge Loading Loading @@ -75,7 +76,6 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.FalsingManager import com.android.systemui.qs.PageIndicator import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shared.system.SysUiStatsLog Loading Loading @@ -103,6 +103,7 @@ import javax.inject.Inject import javax.inject.Provider import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged Loading @@ -121,6 +122,7 @@ private val DEBUG = Log.isLoggable(TAG, Log.DEBUG) * Class that is responsible for keeping the view carousel up to date. This also handles changes in * state and applies them to the media carousel like the expansion. */ @ExperimentalCoroutinesApi @SysUISingleton class MediaCarouselController @Inject Loading Loading @@ -149,7 +151,7 @@ constructor( private val secureSettings: SecureSettings, private val mediaCarouselViewModel: MediaCarouselViewModel, private val mediaViewControllerFactory: Provider<MediaViewController>, private val sceneInteractor: SceneInteractor, private val deviceEntryInteractor: DeviceEntryInteractor, ) : Dumpable { /** The current width of the carousel */ var currentCarouselWidth: Int = 0 Loading Loading @@ -904,9 +906,15 @@ constructor( /** Return true if the carousel should be hidden because lockscreen is currently visible */ fun isLockedAndHidden(): Boolean { val keyguardState = keyguardTransitionInteractor.getFinishedState() return !allowMediaPlayerOnLockScreen && KeyguardState.lockscreenVisibleInState(keyguardState) val isOnLockscreen = if (SceneContainerFlag.isEnabled) { !deviceEntryInteractor.isDeviceEntered.value } else { KeyguardState.lockscreenVisibleInState( keyguardTransitionInteractor.getFinishedState() ) } return !allowMediaPlayerOnLockScreen && isOnLockscreen } private fun reorderAllPlayers( Loading packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt +105 −5 Original line number Diff line number Diff line Loading @@ -27,19 +27,24 @@ import android.util.MathUtils.abs import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey import com.android.internal.logging.InstanceId import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.SysuiTestCase import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.dump.DumpManager import com.android.systemui.flags.DisableSceneContainer import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.flags.Flags import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope Loading Loading @@ -71,14 +76,18 @@ import com.android.systemui.util.settings.FakeSettings import com.android.systemui.util.settings.GlobalSettings import com.android.systemui.util.settings.SecureSettings import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import java.util.Locale import javax.inject.Provider import junit.framework.Assert.assertEquals import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.TestDispatcher import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Before Loading Loading @@ -106,6 +115,7 @@ private val SMARTSPACE_KEY = "smartspace" private const val PAUSED_LOCAL = "paused local" private const val PLAYING_LOCAL = "playing local" @ExperimentalCoroutinesApi @SmallTest @TestableLooper.RunWithLooper(setAsMainLooper = true) @RunWith(AndroidJUnit4::class) Loading Loading @@ -183,7 +193,7 @@ class MediaCarouselControllerTest : SysuiTestCase() { secureSettings = secureSettings, mediaCarouselViewModel = kosmos.mediaCarouselViewModel, mediaViewControllerFactory = mediaViewControllerFactory, sceneInteractor = kosmos.sceneInteractor, deviceEntryInteractor = kosmos.deviceEntryInteractor, ) verify(configurationController).addCallback(capture(configListener)) verify(visualStabilityProvider) Loading Loading @@ -868,7 +878,6 @@ class MediaCarouselControllerTest : SysuiTestCase() { } @DisableSceneContainer @ExperimentalCoroutinesApi @Test fun testKeyguardGone_showMediaCarousel() = kosmos.testScope.runTest { Loading @@ -892,7 +901,6 @@ class MediaCarouselControllerTest : SysuiTestCase() { } @EnableSceneContainer @ExperimentalCoroutinesApi @Test fun testKeyguardGone_showMediaCarousel_scene_container() = kosmos.testScope.runTest { Loading @@ -910,7 +918,6 @@ class MediaCarouselControllerTest : SysuiTestCase() { job.cancel() } @ExperimentalCoroutinesApi @Test fun keyguardShowing_notAllowedOnLockscreen_updateVisibility() { kosmos.testScope.runTest { Loading Loading @@ -940,7 +947,6 @@ class MediaCarouselControllerTest : SysuiTestCase() { } } @ExperimentalCoroutinesApi @Test fun keyguardShowing_allowedOnLockscreen_updateVisibility() { kosmos.testScope.runTest { Loading Loading @@ -970,6 +976,74 @@ class MediaCarouselControllerTest : SysuiTestCase() { } } @EnableSceneContainer @Test fun deviceEntered_mediaAllowed_notLockedAndHidden() { kosmos.testScope.runTest { val settingsJob = mediaCarouselController.listenForLockscreenSettingChanges( kosmos.applicationCoroutineScope ) secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, true) setDeviceEntered(true) assertEquals(false, mediaCarouselController.isLockedAndHidden()) settingsJob.cancel() } } @EnableSceneContainer @Test fun deviceEntered_mediaNotAllowed_notLockedAndHidden() { kosmos.testScope.runTest { val settingsJob = mediaCarouselController.listenForLockscreenSettingChanges( kosmos.applicationCoroutineScope ) secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, false) setDeviceEntered(true) assertEquals(false, mediaCarouselController.isLockedAndHidden()) settingsJob.cancel() } } @EnableSceneContainer @Test fun deviceNotEntered_mediaAllowed_notLockedAndHidden() { kosmos.testScope.runTest { val settingsJob = mediaCarouselController.listenForLockscreenSettingChanges( kosmos.applicationCoroutineScope ) secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, true) setDeviceEntered(false) assertEquals(false, mediaCarouselController.isLockedAndHidden()) settingsJob.cancel() } } @EnableSceneContainer @Test fun deviceNotEntered_mediaNotAllowed_lockedAndHidden() { kosmos.testScope.runTest { val settingsJob = mediaCarouselController.listenForLockscreenSettingChanges( kosmos.applicationCoroutineScope ) secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, false) setDeviceEntered(false) assertEquals(true, mediaCarouselController.isLockedAndHidden()) settingsJob.cancel() } } @Test fun testInvisibleToUserAndExpanded_playersNotListening() { // Add players to carousel. Loading Loading @@ -1129,4 +1203,30 @@ class MediaCarouselControllerTest : SysuiTestCase() { mediaCarouselController.mediaCarouselScrollHandler.visibleMediaIndex ) } private fun TestScope.setDeviceEntered(isEntered: Boolean) { if (isEntered) { // Unlock the device, marking the device as entered kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( SuccessFingerprintAuthenticationStatus(0, true) ) runCurrent() } setScene( if (isEntered) { Scenes.Gone } else { Scenes.Lockscreen } ) assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isEqualTo(isEntered) } private fun TestScope.setScene(key: SceneKey) { kosmos.sceneInteractor.changeScene(key, "test") kosmos.sceneInteractor.setTransitionState( MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key)) ) runCurrent() } } Loading
packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt +3 −1 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import com.android.systemui.media.controls.ui.controller.MediaCarouselController import com.android.systemui.media.controls.ui.view.MediaHost import com.android.systemui.res.R import com.android.systemui.util.animation.MeasurementInput import kotlinx.coroutines.ExperimentalCoroutinesApi object MediaCarousel { object Elements { Loading @@ -46,6 +47,7 @@ object MediaCarousel { } } @ExperimentalCoroutinesApi @Composable fun SceneScope.MediaCarousel( isVisible: Boolean, Loading @@ -54,7 +56,7 @@ fun SceneScope.MediaCarousel( carouselController: MediaCarouselController, offsetProvider: (() -> IntOffset)? = null, ) { if (!isVisible) { if (!isVisible || carouselController.isLockedAndHidden()) { return } Loading
packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt +13 −5 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.Edge Loading Loading @@ -75,7 +76,6 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.FalsingManager import com.android.systemui.qs.PageIndicator import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shared.system.SysUiStatsLog Loading Loading @@ -103,6 +103,7 @@ import javax.inject.Inject import javax.inject.Provider import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged Loading @@ -121,6 +122,7 @@ private val DEBUG = Log.isLoggable(TAG, Log.DEBUG) * Class that is responsible for keeping the view carousel up to date. This also handles changes in * state and applies them to the media carousel like the expansion. */ @ExperimentalCoroutinesApi @SysUISingleton class MediaCarouselController @Inject Loading Loading @@ -149,7 +151,7 @@ constructor( private val secureSettings: SecureSettings, private val mediaCarouselViewModel: MediaCarouselViewModel, private val mediaViewControllerFactory: Provider<MediaViewController>, private val sceneInteractor: SceneInteractor, private val deviceEntryInteractor: DeviceEntryInteractor, ) : Dumpable { /** The current width of the carousel */ var currentCarouselWidth: Int = 0 Loading Loading @@ -904,9 +906,15 @@ constructor( /** Return true if the carousel should be hidden because lockscreen is currently visible */ fun isLockedAndHidden(): Boolean { val keyguardState = keyguardTransitionInteractor.getFinishedState() return !allowMediaPlayerOnLockScreen && KeyguardState.lockscreenVisibleInState(keyguardState) val isOnLockscreen = if (SceneContainerFlag.isEnabled) { !deviceEntryInteractor.isDeviceEntered.value } else { KeyguardState.lockscreenVisibleInState( keyguardTransitionInteractor.getFinishedState() ) } return !allowMediaPlayerOnLockScreen && isOnLockscreen } private fun reorderAllPlayers( Loading
packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt +105 −5 Original line number Diff line number Diff line Loading @@ -27,19 +27,24 @@ import android.util.MathUtils.abs import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey import com.android.internal.logging.InstanceId import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.SysuiTestCase import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.dump.DumpManager import com.android.systemui.flags.DisableSceneContainer import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.flags.Flags import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope Loading Loading @@ -71,14 +76,18 @@ import com.android.systemui.util.settings.FakeSettings import com.android.systemui.util.settings.GlobalSettings import com.android.systemui.util.settings.SecureSettings import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import java.util.Locale import javax.inject.Provider import junit.framework.Assert.assertEquals import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.TestDispatcher import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Before Loading Loading @@ -106,6 +115,7 @@ private val SMARTSPACE_KEY = "smartspace" private const val PAUSED_LOCAL = "paused local" private const val PLAYING_LOCAL = "playing local" @ExperimentalCoroutinesApi @SmallTest @TestableLooper.RunWithLooper(setAsMainLooper = true) @RunWith(AndroidJUnit4::class) Loading Loading @@ -183,7 +193,7 @@ class MediaCarouselControllerTest : SysuiTestCase() { secureSettings = secureSettings, mediaCarouselViewModel = kosmos.mediaCarouselViewModel, mediaViewControllerFactory = mediaViewControllerFactory, sceneInteractor = kosmos.sceneInteractor, deviceEntryInteractor = kosmos.deviceEntryInteractor, ) verify(configurationController).addCallback(capture(configListener)) verify(visualStabilityProvider) Loading Loading @@ -868,7 +878,6 @@ class MediaCarouselControllerTest : SysuiTestCase() { } @DisableSceneContainer @ExperimentalCoroutinesApi @Test fun testKeyguardGone_showMediaCarousel() = kosmos.testScope.runTest { Loading @@ -892,7 +901,6 @@ class MediaCarouselControllerTest : SysuiTestCase() { } @EnableSceneContainer @ExperimentalCoroutinesApi @Test fun testKeyguardGone_showMediaCarousel_scene_container() = kosmos.testScope.runTest { Loading @@ -910,7 +918,6 @@ class MediaCarouselControllerTest : SysuiTestCase() { job.cancel() } @ExperimentalCoroutinesApi @Test fun keyguardShowing_notAllowedOnLockscreen_updateVisibility() { kosmos.testScope.runTest { Loading Loading @@ -940,7 +947,6 @@ class MediaCarouselControllerTest : SysuiTestCase() { } } @ExperimentalCoroutinesApi @Test fun keyguardShowing_allowedOnLockscreen_updateVisibility() { kosmos.testScope.runTest { Loading Loading @@ -970,6 +976,74 @@ class MediaCarouselControllerTest : SysuiTestCase() { } } @EnableSceneContainer @Test fun deviceEntered_mediaAllowed_notLockedAndHidden() { kosmos.testScope.runTest { val settingsJob = mediaCarouselController.listenForLockscreenSettingChanges( kosmos.applicationCoroutineScope ) secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, true) setDeviceEntered(true) assertEquals(false, mediaCarouselController.isLockedAndHidden()) settingsJob.cancel() } } @EnableSceneContainer @Test fun deviceEntered_mediaNotAllowed_notLockedAndHidden() { kosmos.testScope.runTest { val settingsJob = mediaCarouselController.listenForLockscreenSettingChanges( kosmos.applicationCoroutineScope ) secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, false) setDeviceEntered(true) assertEquals(false, mediaCarouselController.isLockedAndHidden()) settingsJob.cancel() } } @EnableSceneContainer @Test fun deviceNotEntered_mediaAllowed_notLockedAndHidden() { kosmos.testScope.runTest { val settingsJob = mediaCarouselController.listenForLockscreenSettingChanges( kosmos.applicationCoroutineScope ) secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, true) setDeviceEntered(false) assertEquals(false, mediaCarouselController.isLockedAndHidden()) settingsJob.cancel() } } @EnableSceneContainer @Test fun deviceNotEntered_mediaNotAllowed_lockedAndHidden() { kosmos.testScope.runTest { val settingsJob = mediaCarouselController.listenForLockscreenSettingChanges( kosmos.applicationCoroutineScope ) secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, false) setDeviceEntered(false) assertEquals(true, mediaCarouselController.isLockedAndHidden()) settingsJob.cancel() } } @Test fun testInvisibleToUserAndExpanded_playersNotListening() { // Add players to carousel. Loading Loading @@ -1129,4 +1203,30 @@ class MediaCarouselControllerTest : SysuiTestCase() { mediaCarouselController.mediaCarouselScrollHandler.visibleMediaIndex ) } private fun TestScope.setDeviceEntered(isEntered: Boolean) { if (isEntered) { // Unlock the device, marking the device as entered kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( SuccessFingerprintAuthenticationStatus(0, true) ) runCurrent() } setScene( if (isEntered) { Scenes.Gone } else { Scenes.Lockscreen } ) assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isEqualTo(isEntered) } private fun TestScope.setScene(key: SceneKey) { kosmos.sceneInteractor.changeScene(key, "test") kosmos.sceneInteractor.setTransitionState( MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key)) ) runCurrent() } }