diff options
7 files changed, 159 insertions, 96 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt index 89a9ba7b61a3..963c602b3d1e 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt @@ -18,17 +18,11 @@ package com.android.systemui.media.controls.ui.controller import android.content.Context import android.content.res.Configuration -import android.database.ContentObserver -import android.net.Uri -import android.os.Handler -import android.os.UserHandle -import android.provider.Settings import android.view.View import android.view.ViewGroup import androidx.annotation.VisibleForTesting import com.android.systemui.Dumpable import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.MigrateClocksToBlueprint import com.android.systemui.media.controls.ui.view.MediaHost @@ -43,7 +37,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.SplitShadeStateController import com.android.systemui.util.asIndenting import com.android.systemui.util.println -import com.android.systemui.util.settings.SecureSettings import com.android.systemui.util.withIncreasedIndent import java.io.PrintWriter import javax.inject.Inject @@ -61,8 +54,6 @@ constructor( private val bypassController: KeyguardBypassController, private val statusBarStateController: SysuiStatusBarStateController, private val context: Context, - private val secureSettings: SecureSettings, - @Main private val handler: Handler, configurationController: ConfigurationController, private val splitShadeStateController: SplitShadeStateController, private val logger: KeyguardMediaControllerLogger, @@ -91,26 +82,6 @@ constructor( } ) - val settingsObserver: ContentObserver = - object : ContentObserver(handler) { - override fun onChange(selfChange: Boolean, uri: Uri?) { - if (uri == lockScreenMediaPlayerUri) { - allowMediaPlayerOnLockScreen = - secureSettings.getBoolForUser( - Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, - true, - UserHandle.USER_CURRENT - ) - refreshMediaPosition(reason = "allowMediaPlayerOnLockScreen changed") - } - } - } - secureSettings.registerContentObserverForUser( - Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, - settingsObserver, - UserHandle.USER_ALL - ) - // First let's set the desired state that we want for this host mediaHost.expansion = MediaHostState.EXPANDED mediaHost.showsOnlyActiveMedia = true @@ -156,16 +127,6 @@ constructor( private set private var splitShadeContainer: ViewGroup? = null - /** Track the media player setting status on lock screen. */ - private var allowMediaPlayerOnLockScreen: Boolean = - secureSettings.getBoolForUser( - Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, - true, - UserHandle.USER_CURRENT - ) - private val lockScreenMediaPlayerUri = - secureSettings.getUriFor(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN) - /** * Attaches media container in single pane mode, situated at the top of the notifications list */ @@ -229,14 +190,12 @@ constructor( // mediaHost.visible required for proper animations handling val isMediaHostVisible = mediaHost.visible val isBypassNotEnabled = !bypassController.bypassEnabled - val currentAllowMediaPlayerOnLockScreen = allowMediaPlayerOnLockScreen val useSplitShade = useSplitShade val shouldBeVisibleForSplitShade = shouldBeVisibleForSplitShade() visible = isMediaHostVisible && isBypassNotEnabled && keyguardOrUserSwitcher && - currentAllowMediaPlayerOnLockScreen && shouldBeVisibleForSplitShade logger.logRefreshMediaPosition( reason = reason, @@ -246,7 +205,6 @@ constructor( keyguardOrUserSwitcher = keyguardOrUserSwitcher, mediaHostVisible = isMediaHostVisible, bypassNotEnabled = isBypassNotEnabled, - currentAllowMediaPlayerOnLockScreen = currentAllowMediaPlayerOnLockScreen, shouldBeVisibleForSplitShade = shouldBeVisibleForSplitShade, ) val currActiveContainer = activeContainer @@ -321,7 +279,6 @@ constructor( println("Self", this@KeyguardMediaController) println("visible", visible) println("useSplitShade", useSplitShade) - println("allowMediaPlayerOnLockScreen", allowMediaPlayerOnLockScreen) println("bypassController.bypassEnabled", bypassController.bypassEnabled) println("isDozeWakeUpAnimationWaiting", isDozeWakeUpAnimationWaiting) println("singlePaneContainer", singlePaneContainer) diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerLogger.kt index c0d9dc23a6d5..4d1827efe82f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerLogger.kt @@ -36,7 +36,6 @@ constructor(@KeyguardMediaControllerLog private val logBuffer: LogBuffer) { keyguardOrUserSwitcher: Boolean, mediaHostVisible: Boolean, bypassNotEnabled: Boolean, - currentAllowMediaPlayerOnLockScreen: Boolean, shouldBeVisibleForSplitShade: Boolean, ) { logBuffer.log( @@ -50,8 +49,7 @@ constructor(@KeyguardMediaControllerLog private val logBuffer: LogBuffer) { bool3 = keyguardOrUserSwitcher bool4 = mediaHostVisible int2 = if (bypassNotEnabled) 1 else 0 - str2 = currentAllowMediaPlayerOnLockScreen.toString() - str3 = shouldBeVisibleForSplitShade.toString() + str2 = shouldBeVisibleForSplitShade.toString() }, { "refreshMediaPosition(reason=$str1, " + @@ -60,8 +58,7 @@ constructor(@KeyguardMediaControllerLog private val logBuffer: LogBuffer) { "keyguardOrUserSwitcher=$bool3, " + "mediaHostVisible=$bool4, " + "bypassNotEnabled=${int2 == 1}, " + - "currentAllowMediaPlayerOnLockScreen=$str2, " + - "shouldBeVisibleForSplitShade=$str3)" + "shouldBeVisibleForSplitShade=$str2)" } ) } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt index 655e6a55fb95..c3c1e83546df 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt @@ -22,6 +22,7 @@ import android.content.Intent import android.content.res.ColorStateList import android.content.res.Configuration import android.database.ContentObserver +import android.os.UserHandle import android.provider.Settings import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS import android.util.Log @@ -44,6 +45,7 @@ import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.media.controls.domain.pipeline.MediaDataManager @@ -76,6 +78,8 @@ import com.android.systemui.util.animation.UniqueObjectHostView import com.android.systemui.util.animation.requiresRemeasuring import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.settings.GlobalSettings +import com.android.systemui.util.settings.SecureSettings +import com.android.systemui.util.settings.SettingsProxyExt.observerFlow import com.android.systemui.util.time.SystemClock import java.io.PrintWriter import java.util.Locale @@ -83,10 +87,16 @@ import java.util.TreeMap import java.util.concurrent.Executor import javax.inject.Inject import javax.inject.Provider +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext private const val TAG = "MediaCarouselController" private val settingsIntent = Intent().setAction(ACTION_MEDIA_CONTROLS_SETTINGS) @@ -108,6 +118,7 @@ constructor( private val systemClock: SystemClock, @Main executor: DelayableExecutor, @Background private val bgExecutor: Executor, + @Background private val backgroundDispatcher: CoroutineDispatcher, private val mediaManager: MediaDataManager, configurationController: ConfigurationController, falsingManager: FalsingManager, @@ -118,6 +129,7 @@ constructor( private val keyguardUpdateMonitor: KeyguardUpdateMonitor, private val keyguardTransitionInteractor: KeyguardTransitionInteractor, private val globalSettings: GlobalSettings, + private val secureSettings: SecureSettings, ) : Dumpable { /** The current width of the carousel */ var currentCarouselWidth: Int = 0 @@ -191,6 +203,8 @@ constructor( } } + private var allowMediaPlayerOnLockScreen = false + /** Whether the media card currently has the "expanded" layout */ @VisibleForTesting var currentlyExpanded = true @@ -532,8 +546,9 @@ constructor( keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback) mediaCarousel.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { - // A backup to show media carousel (if available) once the keyguard is gone. listenForAnyStateToGoneKeyguardTransition(this) + listenForAnyStateToLockscreenTransition(this) + listenForLockscreenSettingChanges(this) } } @@ -587,7 +602,49 @@ constructor( return scope.launch { keyguardTransitionInteractor.anyStateToGoneTransition .filter { it.transitionState == TransitionState.FINISHED } - .collect { showMediaCarousel() } + .collect { + showMediaCarousel() + updateHostVisibility() + } + } + } + + @VisibleForTesting + internal fun listenForAnyStateToLockscreenTransition(scope: CoroutineScope): Job { + return scope.launch { + keyguardTransitionInteractor.anyStateToLockscreenTransition + .filter { it.transitionState == TransitionState.FINISHED } + .collect { + if (!allowMediaPlayerOnLockScreen) { + updateHostVisibility() + } + } + } + } + + @VisibleForTesting + internal fun listenForLockscreenSettingChanges(scope: CoroutineScope): Job { + return scope.launch { + secureSettings + .observerFlow(UserHandle.USER_ALL, Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN) + // query to get initial value + .onStart { emit(Unit) } + .map { getMediaLockScreenSetting() } + .distinctUntilChanged() + .collectLatest { + allowMediaPlayerOnLockScreen = it + updateHostVisibility() + } + } + } + + private suspend fun getMediaLockScreenSetting(): Boolean { + return withContext(backgroundDispatcher) { + secureSettings.getBoolForUser( + Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, + true, + UserHandle.USER_CURRENT + ) } } @@ -600,6 +657,13 @@ constructor( updatePlayers(recreateMedia = true) } + /** 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) + } + private fun reorderAllPlayers( previousVisiblePlayerKey: MediaPlayerData.MediaSortKey?, key: String? = null diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt index d92168bf9fa4..eca76b603b1a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt @@ -23,6 +23,7 @@ import android.view.View.OnAttachStateChangeListener import com.android.systemui.media.controls.domain.pipeline.MediaDataManager import com.android.systemui.media.controls.shared.model.MediaData import com.android.systemui.media.controls.shared.model.SmartspaceMediaData +import com.android.systemui.media.controls.ui.controller.MediaCarouselController import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager import com.android.systemui.media.controls.ui.controller.MediaHostStatesManager import com.android.systemui.media.controls.ui.controller.MediaLocation @@ -33,12 +34,12 @@ import com.android.systemui.util.animation.UniqueObjectHostView import java.util.Objects import javax.inject.Inject -class MediaHost -constructor( +class MediaHost( private val state: MediaHostStateHolder, private val mediaHierarchyManager: MediaHierarchyManager, private val mediaDataManager: MediaDataManager, - private val mediaHostStatesManager: MediaHostStatesManager + private val mediaHostStatesManager: MediaHostStatesManager, + private val mediaCarouselController: MediaCarouselController, ) : MediaHostState by state { lateinit var hostView: UniqueObjectHostView var location: Int = -1 @@ -202,7 +203,9 @@ constructor( */ fun updateViewVisibility() { state.visible = - if (showsOnlyActiveMedia) { + if (mediaCarouselController.isLockedAndHidden()) { + false + } else if (showsOnlyActiveMedia) { mediaDataManager.hasActiveMediaOrRecommendation() } else { mediaDataManager.hasAnyMediaOrRecommendation() diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java index 0fa3605ecd6d..59b98b2792be 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java +++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java @@ -21,6 +21,7 @@ import com.android.systemui.log.LogBuffer; import com.android.systemui.log.LogBufferFactory; import com.android.systemui.media.controls.domain.MediaDomainModule; import com.android.systemui.media.controls.domain.pipeline.MediaDataManager; +import com.android.systemui.media.controls.ui.controller.MediaCarouselController; import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager; import com.android.systemui.media.controls.ui.controller.MediaHostStatesManager; import com.android.systemui.media.controls.ui.view.MediaHost; @@ -59,8 +60,9 @@ public interface MediaModule { @Named(QS_PANEL) static MediaHost providesQSMediaHost(MediaHost.MediaHostStateHolder stateHolder, MediaHierarchyManager hierarchyManager, MediaDataManager dataManager, - MediaHostStatesManager statesManager) { - return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager); + MediaHostStatesManager statesManager, MediaCarouselController carouselController) { + return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager, + carouselController); } /** */ @@ -69,8 +71,9 @@ public interface MediaModule { @Named(QUICK_QS_PANEL) static MediaHost providesQuickQSMediaHost(MediaHost.MediaHostStateHolder stateHolder, MediaHierarchyManager hierarchyManager, MediaDataManager dataManager, - MediaHostStatesManager statesManager) { - return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager); + MediaHostStatesManager statesManager, MediaCarouselController carouselController) { + return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager, + carouselController); } /** */ @@ -79,8 +82,9 @@ public interface MediaModule { @Named(KEYGUARD) static MediaHost providesKeyguardMediaHost(MediaHost.MediaHostStateHolder stateHolder, MediaHierarchyManager hierarchyManager, MediaDataManager dataManager, - MediaHostStatesManager statesManager) { - return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager); + MediaHostStatesManager statesManager, MediaCarouselController carouselController) { + return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager, + carouselController); } /** */ @@ -89,8 +93,9 @@ public interface MediaModule { @Named(DREAM) static MediaHost providesDreamMediaHost(MediaHost.MediaHostStateHolder stateHolder, MediaHierarchyManager hierarchyManager, MediaDataManager dataManager, - MediaHostStatesManager statesManager) { - return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager); + MediaHostStatesManager statesManager, MediaCarouselController carouselController) { + return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager, + carouselController); } /** */ @@ -99,8 +104,9 @@ public interface MediaModule { @Named(COMMUNAL_HUB) static MediaHost providesCommunalMediaHost(MediaHost.MediaHostStateHolder stateHolder, MediaHierarchyManager hierarchyManager, MediaDataManager dataManager, - MediaHostStatesManager statesManager) { - return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager); + MediaHostStatesManager statesManager, MediaCarouselController carouselController) { + return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager, + carouselController); } /** Provides a logging buffer related to the media tap-to-transfer chip on the sender device. */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt index 9f5260c252e4..37dea11ccaaf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt @@ -16,7 +16,6 @@ package com.android.systemui.media.controls.ui.controller -import android.provider.Settings import android.test.suitebuilder.annotation.SmallTest import android.testing.AndroidTestingRunner import android.testing.TestableLooper @@ -37,8 +36,6 @@ import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController import com.android.systemui.util.animation.UniqueObjectHostView import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever -import com.android.systemui.util.settings.FakeSettings -import com.android.systemui.utils.os.FakeHandler import com.google.common.truth.Truth.assertThat import junit.framework.Assert.assertTrue import org.junit.Before @@ -65,10 +62,7 @@ class KeyguardMediaControllerTest : SysuiTestCase() { private val mediaContainerView: MediaContainerView = MediaContainerView(context, null) private val hostView = UniqueObjectHostView(context) - private val settings = FakeSettings() private lateinit var keyguardMediaController: KeyguardMediaController - private lateinit var testableLooper: TestableLooper - private lateinit var fakeHandler: FakeHandler private lateinit var statusBarStateListener: StatusBarStateController.StateListener @Before @@ -84,16 +78,12 @@ class KeyguardMediaControllerTest : SysuiTestCase() { whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) whenever(mediaHost.hostView).thenReturn(hostView) hostView.layoutParams = FrameLayout.LayoutParams(100, 100) - testableLooper = TestableLooper.get(this) - fakeHandler = FakeHandler(testableLooper.looper) keyguardMediaController = KeyguardMediaController( mediaHost, bypassController, statusBarStateController, context, - settings, - fakeHandler, configurationController, ResourcesSplitShadeStateController(), mock<KeyguardMediaControllerLogger>(), @@ -126,24 +116,6 @@ class KeyguardMediaControllerTest : SysuiTestCase() { } @Test - fun testHiddenOnKeyguard_whenMediaOnLockScreenDisabled() { - settings.putInt(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, 0) - - keyguardMediaController.refreshMediaPosition(TEST_REASON) - - assertThat(mediaContainerView.visibility).isEqualTo(GONE) - } - - @Test - fun testAvailableOnKeyguard_whenMediaOnLockScreenEnabled() { - settings.putInt(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, 1) - - keyguardMediaController.refreshMediaPosition(TEST_REASON) - - assertThat(mediaContainerView.visibility).isEqualTo(VISIBLE) - } - - @Test fun testActivatesSplitShadeContainerInSplitShadeMode() { val splitShadeContainer = FrameLayout(context) keyguardMediaController.attachSplitShadeContainer(splitShadeContainer) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt index 59e2696c6123..c3daf8485634 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt @@ -34,9 +34,9 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.kosmos.testScope import com.android.systemui.media.controls.MediaTestUtils import com.android.systemui.media.controls.domain.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA import com.android.systemui.media.controls.domain.pipeline.MediaDataManager @@ -59,7 +59,9 @@ import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq +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 java.util.Locale import javax.inject.Provider @@ -67,6 +69,7 @@ import junit.framework.Assert.assertEquals import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestDispatcher import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import org.junit.Before @@ -114,8 +117,8 @@ class MediaCarouselControllerTest : SysuiTestCase() { @Mock lateinit var pageIndicator: PageIndicator @Mock lateinit var mediaFlags: MediaFlags @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor - @Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor @Mock lateinit var globalSettings: GlobalSettings + private lateinit var secureSettings: SecureSettings private val transitionRepository = kosmos.fakeKeyguardTransitionRepository @Captor lateinit var listener: ArgumentCaptor<MediaDataManager.Listener> @Captor @@ -127,13 +130,16 @@ class MediaCarouselControllerTest : SysuiTestCase() { private val clock = FakeSystemClock() private lateinit var bgExecutor: FakeExecutor + private lateinit var testDispatcher: TestDispatcher private lateinit var mediaCarouselController: MediaCarouselController @Before fun setup() { MockitoAnnotations.initMocks(this) + secureSettings = FakeSettings() context.resources.configuration.setLocales(LocaleList(Locale.US, Locale.UK)) bgExecutor = FakeExecutor(clock) + testDispatcher = UnconfinedTestDispatcher() mediaCarouselController = MediaCarouselController( context, @@ -144,6 +150,7 @@ class MediaCarouselControllerTest : SysuiTestCase() { clock, executor, bgExecutor, + testDispatcher, mediaDataManager, configurationController, falsingManager, @@ -153,7 +160,8 @@ class MediaCarouselControllerTest : SysuiTestCase() { mediaFlags, keyguardUpdateMonitor, kosmos.keyguardTransitionInteractor, - globalSettings + globalSettings, + secureSettings, ) verify(configurationController).addCallback(capture(configListener)) verify(mediaDataManager).addListener(capture(listener)) @@ -807,7 +815,9 @@ class MediaCarouselControllerTest : SysuiTestCase() { @ExperimentalCoroutinesApi @Test fun testKeyguardGone_showMediaCarousel() = - runTest(UnconfinedTestDispatcher()) { + kosmos.testScope.runTest { + var updatedVisibility = false + mediaCarouselController.updateHostVisibility = { updatedVisibility = true } mediaCarouselController.mediaCarousel = mediaCarousel val job = mediaCarouselController.listenForAnyStateToGoneKeyguardTransition(this) @@ -818,10 +828,64 @@ class MediaCarouselControllerTest : SysuiTestCase() { ) verify(mediaCarousel).visibility = View.VISIBLE + assertEquals(true, updatedVisibility) + assertEquals(false, mediaCarouselController.isLockedAndHidden()) job.cancel() } + @ExperimentalCoroutinesApi + @Test + fun keyguardShowing_notAllowedOnLockscreen_updateVisibility() { + kosmos.testScope.runTest { + var updatedVisibility = false + mediaCarouselController.updateHostVisibility = { updatedVisibility = true } + mediaCarouselController.mediaCarousel = mediaCarousel + + val settingsJob = mediaCarouselController.listenForLockscreenSettingChanges(this) + secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, false) + + val keyguardJob = mediaCarouselController.listenForAnyStateToLockscreenTransition(this) + transitionRepository.sendTransitionSteps( + from = KeyguardState.GONE, + to = KeyguardState.LOCKSCREEN, + this + ) + + assertEquals(true, updatedVisibility) + assertEquals(true, mediaCarouselController.isLockedAndHidden()) + + settingsJob.cancel() + keyguardJob.cancel() + } + } + + @ExperimentalCoroutinesApi + @Test + fun keyguardShowing_allowedOnLockscreen_updateVisibility() { + kosmos.testScope.runTest { + var updatedVisibility = false + mediaCarouselController.updateHostVisibility = { updatedVisibility = true } + mediaCarouselController.mediaCarousel = mediaCarousel + + val settingsJob = mediaCarouselController.listenForLockscreenSettingChanges(this) + secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, true) + + val keyguardJob = mediaCarouselController.listenForAnyStateToLockscreenTransition(this) + transitionRepository.sendTransitionSteps( + from = KeyguardState.GONE, + to = KeyguardState.LOCKSCREEN, + this + ) + + assertEquals(true, updatedVisibility) + assertEquals(false, mediaCarouselController.isLockedAndHidden()) + + settingsJob.cancel() + keyguardJob.cancel() + } + } + @Test fun testInvisibleToUserAndExpanded_playersNotListening() { // Add players to carousel. |